From 110ddb9a6ca427c5e61be6e40fa8d490b66dae41 Mon Sep 17 00:00:00 2001 From: fufesou Date: Sun, 13 Sep 2026 17:48:03 +0800 Subject: [PATCH] Report Linux cursor density independently of the host UI --- Cargo.toml | 5 ++--- src/platform/linux.rs | 34 +++++----------------------------- src/server/drm_capturer.rs | 25 ++----------------------- 3 files changed, 9 insertions(+), 55 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 870f97988..21e4d3727 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,7 @@ inline = [] use_samplerate = ["samplerate"] use_rubato = ["rubato"] use_dasp = ["dasp"] -flutter = ["flutter_rust_bridge", "dep:x11rb"] +flutter = ["flutter_rust_bridge"] default = ["use_dasp"] hwcodec = ["scrap/hwcodec"] vram = ["scrap/vram"] @@ -40,7 +40,6 @@ drm-wake = ["drm"] linux-pkg-config = ["magnum-opus/linux-pkg-config", "scrap/linux-pkg-config"] unix-file-copy-paste = [ "dep:x11-clipboard", - "dep:x11rb", "dep:percent-encoding", "dep:once_cell", "clipboard/unix-file-copy-paste", @@ -191,7 +190,7 @@ evdev = { git="https://github.com/rustdesk-org/evdev" } dbus = "0.9" dbus-crossroads = "0.5" x11-clipboard = {git="https://github.com/clslaid/x11-clipboard", branch = "feat/store-batch", optional = true} -x11rb = {version = "0.12", features = ["all-extensions"], optional = true} +x11rb = {version = "0.12", features = ["all-extensions"]} percent-encoding = {version = "2.3", optional = true} once_cell = {version = "1.18", optional = true} nix = { version = "0.29", features = ["term", "process"]} diff --git a/src/platform/linux.rs b/src/platform/linux.rs index 0c84941ba..076b5a476 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -35,11 +35,7 @@ use std::{ use terminfo::{capability as cap, Database}; use wallpaper; -// Scope density lookup to Flutter host builds to preserve legacy capture -// behavior. These builds include density in cursor IDs to refresh on DPI -// changes; non-Flutter builds retain original IDs and unknown density. -// This gate describes the host binary, not the connected controller's UI. -#[cfg(feature = "flutter")] +// Cursor density is capture metadata, independent of either endpoint's UI. mod cursor; pub const PA_SAMPLE_RATE: u32 = 48000; @@ -577,11 +573,8 @@ pub fn get_cursor() -> ResultType> { // polled there is a live session, which is the case the latch reads correctly. #[cfg(feature = "drm")] if !is_x11() { - #[cfg(feature = "flutter")] let cursor = cursor::drm_snapshot(|c| c.id)? .map(|(id, scale)| cursor::cache_id(id, scale)); - #[cfg(not(feature = "flutter"))] - let cursor = crate::server::drm_capturer::drm_cursor_id(); if let Some(id) = cursor { // In a mixed DRM + PipeWire session the DRM streams only cover the DRM-backed displays; // when the pointer sits on a PipeWire-served display every DRM stream reports the hidden @@ -610,7 +603,6 @@ pub fn get_cursor() -> ResultType> { } } }); - #[cfg(feature = "flutter")] let res = res.map(cursor::x11_cursor_id); Ok(res) } @@ -624,13 +616,8 @@ pub fn get_cursor_data(hcursor: u64) -> ResultType { // agree anyway, since a caller that took the DRM branch there has to take it here. #[cfg(feature = "drm")] if !is_x11() { - #[cfg(feature = "flutter")] let cursor = cursor::drm_snapshot(Clone::clone)?; - #[cfg(not(feature = "flutter"))] - let cursor = crate::server::drm_capturer::drm_cursor(); - if let Some(c) = cursor { - #[cfg(feature = "flutter")] - let (c, scale) = c; + if let Some((c, scale)) = cursor { // See get_cursor(): a hidden DRM sentinel is authoritative only in a pure-DRM session. In // a mixed DRM + PipeWire session fall through so the PipeWire display's cursor is served // by the normal path instead of being hidden everywhere. @@ -638,27 +625,19 @@ pub fn get_cursor_data(hcursor: u64) -> ResultType { || !crate::server::display_service::has_non_drm_backed_display() { let mut cd: CursorData = Default::default(); - cd.id = c.id; + cd.id = cursor::cache_id(c.id, scale); + cd.scale = scale; cd.width = c.width; cd.height = c.height; cd.hotx = c.hotx; cd.hoty = c.hoty; cd.colors = c.colors.into(); - #[cfg(feature = "flutter")] - { - cd.id = cursor::cache_id(cd.id, scale); - cd.scale = scale; - } return Ok(cd); } } } - #[cfg(feature = "flutter")] let scale = cursor::x11_cursor_scale(); - #[cfg(feature = "flutter")] let matches = |id| cursor::cache_id(id, scale) == hcursor; - #[cfg(not(feature = "flutter"))] - let matches = |id| id == hcursor; let mut res = None; DISPLAY.with(|conn| { if let Ok(ref mut d) = conn.try_borrow_mut() { @@ -673,10 +652,7 @@ pub fn get_cursor_data(hcursor: u64) -> ResultType { cd.height = (*img).height as _; // to-do: how about if it is 0 cd.id = hcursor; - #[cfg(feature = "flutter")] - { - cd.scale = scale; - } + cd.scale = scale; let pixels = std::slice::from_raw_parts((*img).pixels, (cd.width * cd.height) as _); // cd.colors.resize(pixels.len() * 4, 0); diff --git a/src/server/drm_capturer.rs b/src/server/drm_capturer.rs index 1aa2a6295..1427e94d8 100644 --- a/src/server/drm_capturer.rs +++ b/src/server/drm_capturer.rs @@ -14,7 +14,6 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Condvar, Mutex}; use std::time::{Duration, Instant}; -#[cfg(feature = "flutter")] mod cursor_metadata; const HANDSHAKE_TIMEOUT_MS: u64 = 3000; @@ -958,26 +957,8 @@ fn fold_cursor_id(id: u64, t: i32) -> u64 { } } -fn with_drm_cursor(f: impl Fn(&DrmCursorData) -> T) -> Option { - let map = DRM_CURSOR.lock().unwrap(); - map.values() - .map(|(_, c)| c) - .find(|c| c.id != scrap::drm_reader::HIDDEN_CURSOR_ID) - .or_else(|| map.values().map(|(_, c)| c).next()) - .map(f) -} - -pub fn drm_cursor_id() -> Option { - with_drm_cursor(|c| c.id) -} - -/// Snapshot of the DRM hardware cursor, or None. The pixels are premultiplied ARGB and are passed -/// through as-is, like the XFixes path, so the client sees one cursor format from either backend. -pub fn drm_cursor() -> Option { - with_drm_cursor(|c| c.clone()) -} - -#[cfg(feature = "flutter")] +/// Snapshot of the DRM hardware cursor and optional display metadata. Pixels retain the +/// premultiplied format used by the XFixes path. pub fn drm_cursor_snapshot( f: impl Fn(&DrmCursorData) -> T, ) -> Option<(T, Option)> { @@ -1019,7 +1000,6 @@ pub fn drm_cursor_snapshot( Some((value, monitor)) } -#[cfg(feature = "flutter")] fn cursor_monitor( display: usize, state: &ProbeState, @@ -2332,7 +2312,6 @@ mod drm_capturer_tests { assert!(m2[0].is_none() && m2[1].is_none()); } - #[cfg(feature = "flutter")] #[test] fn cursor_monitor_reserves_other_displays_before_guessing_density() { let state = ProbeState::Available(