Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

33 changes: 17 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
[![Release](https://img.shields.io/github/v/release/LibreStatic/ezdisplay?include_prereleases&label=release)](https://github.com/LibreStatic/ezdisplay/releases)
[![License: MIT OR Apache-2.0](https://img.shields.io/badge/license-MIT%20OR%20Apache--2.0-blue)](#license)

Windows · KDE Plasma 6 and GNOME (Wayland) · Android tablets · Rust + Kotlin
Windows · KDE Plasma 6, GNOME, Sway and Hyprland (Wayland) · Android tablets · Rust + Kotlin

[Download](https://github.com/LibreStatic/ezdisplay/releases) ·
[Install](#install) ·
Expand Down Expand Up @@ -55,19 +55,20 @@ and no telemetry, and everything travels through the cable.
ezdisplay is in **alpha** (see the [releases](https://github.com/LibreStatic/ezdisplay/releases); every change merged to `main` is published as a pre-release).
Working today, over USB with ADB:

| | Windows | KDE Plasma 6 (Wayland) | GNOME (Wayland) |
| --- | :---: | :---: | :---: |
| Extended display at native resolution and refresh rate | ✅ | ✅ | 🧪 |
| Locally drawn cursor | ✅ | ✅ | 🧪 |
| Zero-copy hardware encode | ✅ | ✅ | 🧪 |
| Touch, pen pressure, tilt and hover | ✅ | ✅ | 🧪 |
| Tray app with settings and live statistics | ✅ | ✅ | 🧪 |
| Installer / packages | ✅ `.exe` | ✅ AppImage, `.deb`, Arch | 🧪 AppImage, `.deb`, Arch |
| | Windows | KDE Plasma 6 (Wayland) | GNOME (Wayland) | Sway, Hyprland |
| --- | :---: | :---: | :---: | :---: |
| Extended display at native resolution and refresh rate | ✅ | ✅ | 🧪 | 🧪 |
| Locally drawn cursor | ✅ | ✅ | 🧪 | 🧪 |
| Zero-copy hardware encode | ✅ | ✅ | 🧪 | ❌ shared-memory capture |
| Touch, pen pressure, tilt and hover | ✅ | ✅ | 🧪 | 🧪 |
| Tray app with settings and live statistics | ✅ | ✅ | 🧪 | 🧪 |
| Installer / packages | ✅ `.exe` | ✅ AppImage, `.deb`, Arch | 🧪 AppImage, `.deb`, Arch | 🧪 AppImage, `.deb`, Arch |

🧪 GNOME support is new: it has been tested against a headless Mutter only, not yet on a real GNOME
session with a tablet. The tray needs the AppIndicator extension there.
🧪 New and not yet tested on a real session with a tablet: GNOME has been tested against a
headless Mutter (the tray needs the AppIndicator extension there), Sway against a headless sway,
and Hyprland only through unit tests.

**Not yet:** audio, wlroots backends, X11.
**Not yet:** audio, X11.

## How it works

Expand Down Expand Up @@ -132,11 +133,11 @@ notification area. Connect the tablet over USB: the app opens on the tablet and
> and Smart App Control blocks it. See [docs/windows-testing.md](docs/windows-testing.md) and
> [packaging/windows/README.md](packaging/windows/README.md).

### 2. PC: Linux (KDE Plasma 6 or GNOME, Wayland)
### 2. PC: Linux (KDE Plasma 6, GNOME, Sway or Hyprland, Wayland)

Plasma 6 or a recent GNOME on Wayland is required; X11 and other desktops are not supported yet.
Plasma 6, a recent GNOME, Sway or Hyprland on Wayland is required; X11 and other desktops are not supported yet.
The host picks the desktop from `XDG_CURRENT_DESKTOP` and the session bus;
`EZD_LINUX_BACKEND=kde|gnome` overrides it. Pick one:
`EZD_LINUX_BACKEND=kde|gnome|sway|hyprland` overrides it. Pick one:

- **Arch Linux**: `sudo pacman -U ezdisplay-*.pkg.tar.zst`.
- **Debian and Ubuntu**: `sudo apt install ./ezdisplay_*.deb`, using the file for your release:
Expand Down Expand Up @@ -229,7 +230,7 @@ cargo check --target aarch64-linux-android -p ezd-proto -p ezd-link
| `ezd-link` | `Link` byte streams (TCP, in-memory), ADB helper, prioritized mux. |
| `ezd-host-core` | `VideoSource` / `VirtualDisplay` traits, host session, synthetic test frames. |
| `ezd-host-service` | The host loop as a library, with live status for the CLI and the tray app. |
| `ezd-host-linux` | KDE and GNOME virtual output, PipeWire capture, VAAPI/NVENC encode, uinput input. |
| `ezd-host-linux` | KDE, GNOME, Sway and Hyprland virtual output, PipeWire and Wayland capture, VAAPI/NVENC encode, uinput input. |
| `ezd-host-windows` | GPU policy, Virtual Display Driver, Desktop Duplication, D3D11 encode, input injection. |
| `ezd-ffmpeg` | Safe FFmpeg wrapper: low-latency `Encoder`, `Decoder`, `SwVideoSource`, encoder probing. |
| `ezd-host` | The host CLI: `run`, `diag` and `vdd`. |
Expand Down
4 changes: 4 additions & 0 deletions crates/ezd-host-linux/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ zbus = { version = "5", default-features = false, features = ["async-io", "block
tokio = { workspace = true }
serde_json.workspace = true
tracing.workspace = true
# Direct Wayland capture for the wlroots compositors (ext-image-copy-capture, wlr-screencopy).
wayland-client = "0.31"
wayland-protocols = { version = "0.32", features = ["client", "staging"] }
wayland-protocols-wlr = { version = "0.3", features = ["client"] }
# Only the bindings: functions are loaded through FFmpeg's Vulkan device.
ash = { version = "0.38", default-features = false, features = ["std"] }

Expand Down
25 changes: 5 additions & 20 deletions crates/ezd-host-linux/src/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,17 @@

use std::fmt;

/// A desktop the Linux host can (or, later, will) drive.
/// A desktop the Linux host can drive.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LinuxBackend {
/// KDE Plasma 6 (KWin): portal VIRTUAL source, `kscreen-doctor`, KWin input mapping.
Kde,
/// GNOME (Mutter): `org.gnome.Mutter.ScreenCast` virtual monitor, `DisplayConfig`, gsettings.
Gnome,
/// Sway. Detected, but not implemented yet.
/// Sway: i3 IPC headless output, direct Wayland capture, per-device input mapping.
Sway,
/// Hyprland. Detected, but not implemented yet.
/// Hyprland: headless output over its command socket, direct Wayland capture, per-device
/// input mapping.
Hyprland,
}

Expand All @@ -41,19 +42,6 @@ impl LinuxBackend {
_ => None,
}
}

/// Whether a virtual output provider exists for this backend.
pub fn supported(self) -> bool {
matches!(self, LinuxBackend::Kde | LinuxBackend::Gnome)
}

/// Error text for backends that are detected but not written yet.
pub fn unsupported_error(self) -> String {
format!(
"the {} backend is not implemented yet; supported desktops are KDE Plasma and GNOME",
self.name()
)
}
}

impl fmt::Display for LinuxBackend {
Expand Down Expand Up @@ -282,10 +270,7 @@ mod tests {
}

#[test]
fn only_kde_and_gnome_are_supported() {
assert!(LinuxBackend::Kde.supported() && LinuxBackend::Gnome.supported());
assert!(!LinuxBackend::Sway.supported() && !LinuxBackend::Hyprland.supported());
assert!(LinuxBackend::Sway.unsupported_error().contains("sway"));
fn names_round_trip() {
for b in [
LinuxBackend::Kde,
LinuxBackend::Gnome,
Expand Down
14 changes: 13 additions & 1 deletion crates/ezd-host-linux/src/diag.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ pub fn section() -> Value {
let name = detected.as_ref().map_or("unknown", |b| b.name());
let mut v = json!({
"backend": format!("linux-{name}"),
"backend_supported": detected.as_ref().is_ok_and(|b| b.supported()),
"backend_supported": detected.is_ok(),
"backend_error": detected.as_ref().err(),
"xdg_current_desktop": std::env::var("XDG_CURRENT_DESKTOP").ok(),
"wayland_display": std::env::var("WAYLAND_DISPLAY").ok(),
Expand All @@ -79,6 +79,18 @@ pub fn section() -> Value {
});
let extra = match detected {
Ok(LinuxBackend::Gnome) => json!({ "gnome": crate::gnome::diag() }),
Ok(b @ (LinuxBackend::Sway | LinuxBackend::Hyprland)) => {
let compositor = if b == LinuxBackend::Sway {
crate::sway::diag()
} else {
crate::hyprland::diag()
};
json!({
"compositor": b.name(),
"wlroots": compositor,
"wayland_capture": crate::wl_capture::diag(),
})
}
_ => json!({
"kwin_version": kwin_version(),
"kwin_drm_devices": crate::adapter::kwin_drm_devices(),
Expand Down
Loading
Loading