diff --git a/Cargo.toml b/Cargo.toml index b5a70f2..fc0e7bb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -81,3 +81,8 @@ winapi = { version = "0.3", features = [ [target.'cfg(target_os = "macos")'.dependencies] osascript = "0.3" + +[target.'cfg(target_os = "linux")'.dependencies] +sctk = { package = "smithay-client-toolkit", version = "0.20.0", default-features = false, features = [ + "calloop", +] } diff --git a/protos/message.proto b/protos/message.proto index 2e4a84b..003bb33 100644 --- a/protos/message.proto +++ b/protos/message.proto @@ -607,6 +607,7 @@ message SwitchDisplay { SupportedResolutions resolutions = 7; // Do not care about the origin point for now. Resolution original_resolution = 8; + double scale = 9; } message CaptureDisplays { diff --git a/src/config.rs b/src/config.rs index 1e6493e..5166749 100644 --- a/src/config.rs +++ b/src/config.rs @@ -90,11 +90,15 @@ pub const LINK_DOCS_HOME: &str = "https://rustdesk.com/docs/en/"; pub const LINK_DOCS_X11_REQUIRED: &str = "https://rustdesk.com/docs/en/manual/linux/#x11-required"; pub const LINK_HEADLESS_LINUX_SUPPORT: &str = "https://github.com/rustdesk/rustdesk/wiki/Headless-Linux-Support"; +pub const LINK_KDE_PLASMA_WAYLAND_SELECT_ALL_DISPLAYS: &str = + "https://github.com/rustdesk/rustdesk/discussions/13468"; + lazy_static::lazy_static! { pub static ref HELPER_URL: HashMap<&'static str, &'static str> = HashMap::from([ ("rustdesk docs home", LINK_DOCS_HOME), ("rustdesk docs x11-required", LINK_DOCS_X11_REQUIRED), ("rustdesk x11 headless", LINK_HEADLESS_LINUX_SUPPORT), + ("rustdesk discussion Plasma Wayland all displays", LINK_KDE_PLASMA_WAYLAND_SELECT_ALL_DISPLAYS), ]); } diff --git a/src/platform/linux.rs b/src/platform/linux.rs index 98a9672..e29b17b 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -1,6 +1,15 @@ use crate::ResultType; use std::{collections::HashMap, process::Command}; +use sctk::{ + output::OutputData, + output::{OutputHandler, OutputState}, + reexports::client::protocol::wl_output::WlOutput, + reexports::client::{globals, Proxy}, + reexports::client::{Connection, QueueHandle}, + registry::{ProvidesRegistryState, RegistryState}, +}; + lazy_static::lazy_static! { pub static ref DISTRO: Distro = Distro::new(); } @@ -337,6 +346,88 @@ pub fn system_message(title: &str, msg: &str, forever: bool) -> ResultType<()> { crate::bail!("failed to post system message"); } +#[derive(Debug, Clone)] +pub struct WaylandDisplayInfo { + pub name: String, + pub x: i32, + pub y: i32, + pub width: i32, + pub height: i32, + pub logical_size: Option<(i32, i32)>, + pub refresh_rate: i32, +} + +// Retrieves information about all connected displays via the Wayland protocol. +pub fn get_wayland_displays() -> ResultType> { + struct WaylandEnv { + registry_state: RegistryState, + output_state: OutputState, + } + + impl OutputHandler for WaylandEnv { + fn output_state(&mut self) -> &mut OutputState { + &mut self.output_state + } + + fn new_output(&mut self, _: &Connection, _: &QueueHandle, _: WlOutput) {} + fn update_output(&mut self, _: &Connection, _: &QueueHandle, _: WlOutput) {} + fn output_destroyed(&mut self, _: &Connection, _: &QueueHandle, _: WlOutput) {} + } + + impl ProvidesRegistryState for WaylandEnv { + fn registry(&mut self) -> &mut RegistryState { + &mut self.registry_state + } + + sctk::registry_handlers!(); + } + + sctk::delegate_output!(WaylandEnv); + sctk::delegate_registry!(WaylandEnv); + + let conn = Connection::connect_to_env()?; + let (globals, mut event_queue) = globals::registry_queue_init(&conn)?; + let queue_handle = event_queue.handle(); + + let registry_state = RegistryState::new(&globals); + let output_state = OutputState::new(&globals, &queue_handle); + + let mut environment = WaylandEnv { + registry_state, + output_state, + }; + + event_queue.roundtrip(&mut environment)?; + + let outputs: Vec<_> = environment.output_state.outputs().collect(); + let mut display_infos = Vec::new(); + + for output in outputs { + if let Some(output_data) = output.data::() { + output_data.with_output_info(|info| { + if let Some(mode) = info.modes.iter().find(|m| m.current) { + let (x, y) = info.location; + let (width, height) = mode.dimensions; + let refresh_rate = mode.refresh_rate; + let name = info.name.clone().unwrap_or_default(); + let logical_size = info.logical_size; + display_infos.push(WaylandDisplayInfo { + name, + x, + y, + width, + height, + logical_size, + refresh_rate, + }); + } + }); + } + } + + Ok(display_infos) +} + #[cfg(test)] mod tests { use super::*;