mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-09-11 23:11:01 +03:00
fix(flutter): make Adjust Window reliable across desktop platforms (#15853)
* fix(flutter): make Adjust Window reliable across desktop platforms
- Fix incorrect sizing on scaled displays by calculating the target from the
rendered canvas scale and platform-specific window coordinate units.
- Fix adjustments using the wrong monitor by querying the current remote
window's screen, with the main window as fallback.
- Fix stale geometry after fullscreen or maximized transitions by refreshing
metrics before calculating and applying the target frame.
- Fix fullscreen availability checks on Windows and macOS by predicting the
restored window borders and caching each macOS window's pre-fullscreen work area.
- Fix incorrect Linux work areas by handling GNOME Wayland fractional scaling
and caching compositor/X11 work-area measurements when visibleFrame is wrong.
- Prevent unsafe adjustments by rejecting invalid, oversized, or implausibly
small target frames.
- Avoid failures during window teardown by skipping adjustment when the view,
screen, or native window frame is unavailable.
Signed-off-by: 21pages <sunboeasy@gmail.com>
* fix(flutter): harden Adjust Window handling
- Use the dynamic Linux resize edge when predicting restored window bounds.
- Treat GNOME fractional-scaling lookup failures as unknown without repeating
the lookup for the remote window.
- Stop adjustment safely when native window calls fail during window teardown.
Signed-off-by: 21pages <sunboeasy@gmail.com>
* fix(flutter): correct Linux monitor selection
Update window_size to use monitor height for vertical bounds, preventing incorrect screen selection with vertically stacked displays.
Signed-off-by: 21pages <sunboeasy@gmail.com>
* docs(flutter): simplify Linux screen handling comments
Keep the source rationale concise and move platform measurements and investigation details out of the implementation.
Signed-off-by: 21pages <sunboeasy@gmail.com>
* fix(flutter): align Adjust Window resize padding
Use the shared drag-to-resize padding for Linux restored-window predictions so menu validation matches the applied frame dimensions.
Signed-off-by: 21pages <sunboeasy@gmail.com>
* fix(flutter): remove Adjust Window screen fallback
Return null when the current window screen is unavailable instead of using the main window's scale factor and work area.
Signed-off-by: 21pages <sunboeasy@gmail.com>
* fix(linux): query Mutter monitor layout mode
Use DisplayConfig.GetCurrentState instead of inferring scaling from
experimental features, and handle Ubuntu's UI-scaled logical mode.
Signed-off-by: 21pages <sunboeasy@gmail.com>
* fix(flutter): use native maximized state for Wayland cache
Signed-off-by: 21pages <sunboeasy@gmail.com>
* fix(flutter): allow Adjust Window to fill work area
Signed-off-by: 21pages <sunboeasy@gmail.com>
* fix(flutter): avoid racing screen info updates
Signed-off-by: 21pages <sunboeasy@gmail.com>
* refactor(flutter): remove dead Adjust Window web plumbing
Signed-off-by: 21pages <sunboeasy@gmail.com>
* fix(flutter): tolerate near-unity Wayland scale factors
Signed-off-by: 21pages <sunboeasy@gmail.com>
* fix(flutter): harden window screen detection
Signed-off-by: 21pages <sunboeasy@gmail.com>
* fix(linux): drop deprecated GNOME session detection
Signed-off-by: 21pages <sunboeasy@gmail.com>
* fix(flutter): remove GNOME monitor layout mode flutter cache
Signed-off-by: 21pages <sunboeasy@gmail.com>
---------
Signed-off-by: 21pages <sunboeasy@gmail.com>
This commit is contained in:
@@ -2655,6 +2655,14 @@ pub fn main_get_common(key: String) -> String {
|
||||
return crate::platform::linux::has_gnome_shortcuts_inhibitor_permission().to_string();
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
return false.to_string();
|
||||
} else if key == "gnome-monitor-layout-mode" {
|
||||
#[cfg(target_os = "linux")]
|
||||
return match crate::platform::linux::gnome_monitor_layout_mode() {
|
||||
Some(mode) => mode.as_str().to_owned(),
|
||||
None => String::new(),
|
||||
};
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
return String::new();
|
||||
} else if key == "permanent-password-set" {
|
||||
return ui_interface::is_permanent_password_set().to_string();
|
||||
} else if key == "local-permanent-password-set" {
|
||||
|
||||
@@ -96,6 +96,9 @@ lazy_static::lazy_static! {
|
||||
};
|
||||
static ref ACTIVE_USER_LOOKUP_CACHE: std::sync::Mutex<Option<ActiveUserLookupCache>> =
|
||||
std::sync::Mutex::new(None);
|
||||
static ref GNOME_MONITOR_LAYOUT_MODE_CACHE: std::sync::Mutex<
|
||||
Option<(Instant, Option<GnomeMonitorLayoutMode>)>,
|
||||
> = Default::default();
|
||||
// https://github.com/rustdesk/rustdesk/issues/13705
|
||||
// Check if `sudo -E` actually preserves environment.
|
||||
//
|
||||
@@ -128,6 +131,141 @@ lazy_static::lazy_static! {
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum GnomeMonitorLayoutMode {
|
||||
Logical,
|
||||
Physical,
|
||||
}
|
||||
|
||||
impl GnomeMonitorLayoutMode {
|
||||
pub fn as_str(self) -> &'static str {
|
||||
match self {
|
||||
Self::Logical => "logical",
|
||||
Self::Physical => "physical",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn gnome_monitor_layout_mode_from_value(value: u32) -> Option<GnomeMonitorLayoutMode> {
|
||||
// Upstream: https://gitlab.gnome.org/GNOME/mutter/-/blob/main/data/dbus-interfaces/org.gnome.Mutter.DisplayConfig.xml
|
||||
// Ubuntu mode 3: https://git.launchpad.net/ubuntu/+source/mutter/tree/debian/patches/x11-Add-support-for-fractional-scaling-using-Randr.patch
|
||||
match value {
|
||||
1 | 3 => Some(GnomeMonitorLayoutMode::Logical),
|
||||
2 => Some(GnomeMonitorLayoutMode::Physical),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn gnome_monitor_layout_mode() -> Option<GnomeMonitorLayoutMode> {
|
||||
if let Ok(cache) = GNOME_MONITOR_LAYOUT_MODE_CACHE.lock() {
|
||||
if let Some((updated_at, result)) = *cache {
|
||||
if updated_at.elapsed() < Duration::from_secs(10) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let result = (|| {
|
||||
let is_gnome_desktop = std::env::var("XDG_CURRENT_DESKTOP")
|
||||
.unwrap_or_default()
|
||||
.split(':')
|
||||
.any(|desktop| {
|
||||
desktop.eq_ignore_ascii_case("gnome") || desktop.eq_ignore_ascii_case("unity")
|
||||
});
|
||||
let is_gnome_session = std::env::var("DESKTOP_SESSION")
|
||||
.unwrap_or_default()
|
||||
.to_ascii_lowercase();
|
||||
if !is_gnome_desktop && !is_gnome_session.contains("gnome") {
|
||||
return None;
|
||||
}
|
||||
use dbus::{arg::PropMap, blocking::BlockingSender};
|
||||
|
||||
let conn = match dbus::blocking::Connection::new_session() {
|
||||
Ok(conn) => conn,
|
||||
Err(err) => {
|
||||
log::warn!("Failed to connect to the session bus for GNOME monitor layout: {err}");
|
||||
return None;
|
||||
}
|
||||
};
|
||||
let message = match dbus::Message::new_method_call(
|
||||
"org.gnome.Mutter.DisplayConfig",
|
||||
"/org/gnome/Mutter/DisplayConfig",
|
||||
"org.gnome.Mutter.DisplayConfig",
|
||||
"GetCurrentState",
|
||||
) {
|
||||
Ok(message) => message,
|
||||
Err(err) => {
|
||||
log::warn!("Failed to create GNOME monitor layout query: {err}");
|
||||
return None;
|
||||
}
|
||||
};
|
||||
let reply = match conn.send_with_reply_and_block(message, Duration::from_secs(2)) {
|
||||
Ok(reply) => reply,
|
||||
Err(err) => {
|
||||
log::warn!("Failed to query GNOME monitor layout: {err}");
|
||||
return None;
|
||||
}
|
||||
};
|
||||
let mut args = reply.iter_init();
|
||||
for _ in 0..3 {
|
||||
if !args.next() {
|
||||
log::warn!("GNOME monitor layout reply is missing properties");
|
||||
return None;
|
||||
}
|
||||
}
|
||||
let properties: PropMap = match args.read() {
|
||||
Ok(properties) => properties,
|
||||
Err(err) => {
|
||||
log::warn!("Failed to read GNOME monitor layout properties: {err}");
|
||||
return None;
|
||||
}
|
||||
};
|
||||
let Some(value) = dbus::arg::prop_cast::<u32>(&properties, "layout-mode").copied() else {
|
||||
log::warn!("GNOME monitor layout reply has no layout-mode");
|
||||
return None;
|
||||
};
|
||||
let mode = gnome_monitor_layout_mode_from_value(value);
|
||||
if mode.is_none() {
|
||||
log::warn!("GNOME monitor layout reply has unknown layout-mode {value}");
|
||||
}
|
||||
mode
|
||||
})();
|
||||
if let Ok(mut cache) = GNOME_MONITOR_LAYOUT_MODE_CACHE.lock() {
|
||||
*cache = Some((Instant::now(), result));
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod gnome_monitor_layout_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn maps_logical_layouts() {
|
||||
assert_eq!(
|
||||
gnome_monitor_layout_mode_from_value(1),
|
||||
Some(GnomeMonitorLayoutMode::Logical)
|
||||
);
|
||||
assert_eq!(
|
||||
gnome_monitor_layout_mode_from_value(3),
|
||||
Some(GnomeMonitorLayoutMode::Logical)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn maps_physical_layout() {
|
||||
assert_eq!(
|
||||
gnome_monitor_layout_mode_from_value(2),
|
||||
Some(GnomeMonitorLayoutMode::Physical)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rejects_unknown_layout() {
|
||||
assert_eq!(gnome_monitor_layout_mode_from_value(4), None);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn update_active_user_lookup_cache(desktop: &Desktop) {
|
||||
if let Ok(mut cache) = ACTIVE_USER_LOOKUP_CACHE.lock() {
|
||||
|
||||
Reference in New Issue
Block a user