From ea7251d6e0e021c8586c60eba4cecf872504056b Mon Sep 17 00:00:00 2001 From: kossLAN Date: Fri, 29 May 2026 21:32:44 -0400 Subject: [PATCH] xwayland: split window property loading --- src/xwayland/xwm.rs | 415 +-------------------------------- src/xwayland/xwm/properties.rs | 413 ++++++++++++++++++++++++++++++++ src/xwayland/xwm/selection.rs | 6 +- 3 files changed, 429 insertions(+), 405 deletions(-) create mode 100644 src/xwayland/xwm/properties.rs diff --git a/src/xwayland/xwm.rs b/src/xwayland/xwm.rs index d35b4d2a..cfd31f4b 100644 --- a/src/xwayland/xwm.rs +++ b/src/xwayland/xwm.rs @@ -1,6 +1,7 @@ #![allow(clippy::await_holding_refcell_ref)] // all borrows are to data that is only used by this task mod selection; +mod properties; mod transfer; use selection::SelectionData; @@ -9,7 +10,6 @@ use { crate::{ async_engine::SpawnedFuture, client::Client, - criteria::tlm::{TL_CHANGED_CLASS_INST, TL_CHANGED_ROLE}, ifs::{ data_transfer::{ DataOfferId, DataSourceId, DynDataOffer, DynDataSource, TransferLocation, TransferVtable, @@ -22,7 +22,7 @@ use { wl_seat::{SeatId, WlSeatGlobal}, wl_surface::{ WlSurface, - x_surface::xwindow::{XInputModel, Xwindow, XwindowData}, + x_surface::xwindow::{Xwindow, XwindowData}, }, }, rect::Rect, @@ -46,7 +46,7 @@ use { ChangeProperty, ChangeWindowAttributes, ClientMessage, CompositeRedirectSubwindows, ConfigureNotify, ConfigureRequest, ConfigureWindow, ConfigureWindowValues, ConvertSelection, CreateNotify, CreateWindow, CreateWindowValues, DestroyNotify, - Extension, FocusIn, GetAtomName, GetGeometry, InternAtom, KillClient, MapNotify, + Extension, FocusIn, GetGeometry, InternAtom, KillClient, MapNotify, MapRequest, MapWindow, PropertyNotify, ResClientIdSpec, ResQueryClientIds, SelectSelectionInput, SelectionNotify, SelectionRequest, SetInputFocus, SetSelectionOwner, UnmapNotify, XfixesQueryVersion, XfixesSelectionNotify, @@ -56,14 +56,13 @@ use { consts::{ _NET_WM_STATE_ADD, _NET_WM_STATE_REMOVE, _NET_WM_STATE_TOGGLE, ATOM_ATOM, ATOM_NONE, ATOM_STRING, ATOM_WINDOW, ATOM_WM_CLASS, ATOM_WM_NAME, - ATOM_WM_SIZE_HINTS, ATOM_WM_TRANSIENT_FOR, COMPOSITE_REDIRECT_MANUAL, - CONFIG_WINDOW_HEIGHT, CONFIG_WINDOW_WIDTH, CONFIG_WINDOW_X, CONFIG_WINDOW_Y, - EVENT_MASK_FOCUS_CHANGE, EVENT_MASK_PROPERTY_CHANGE, - EVENT_MASK_SUBSTRUCTURE_NOTIFY, EVENT_MASK_SUBSTRUCTURE_REDIRECT, - ICCCM_WM_HINT_INPUT, ICCCM_WM_STATE_ICONIC, ICCCM_WM_STATE_NORMAL, - ICCCM_WM_STATE_WITHDRAWN, INPUT_FOCUS_POINTER_ROOT, MWM_HINTS_DECORATIONS_FIELD, - MWM_HINTS_FLAGS_FIELD, NOTIFY_DETAIL_POINTER, NOTIFY_MODE_GRAB, NOTIFY_MODE_UNGRAB, - PROP_MODE_REPLACE, RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID, SELECTION_CLIENT_CLOSE_MASK, + ATOM_WM_TRANSIENT_FOR, COMPOSITE_REDIRECT_MANUAL, CONFIG_WINDOW_HEIGHT, + CONFIG_WINDOW_WIDTH, CONFIG_WINDOW_X, CONFIG_WINDOW_Y, EVENT_MASK_FOCUS_CHANGE, + EVENT_MASK_PROPERTY_CHANGE, EVENT_MASK_SUBSTRUCTURE_NOTIFY, + EVENT_MASK_SUBSTRUCTURE_REDIRECT, ICCCM_WM_STATE_ICONIC, ICCCM_WM_STATE_NORMAL, + ICCCM_WM_STATE_WITHDRAWN, INPUT_FOCUS_POINTER_ROOT, NOTIFY_DETAIL_POINTER, + NOTIFY_MODE_GRAB, NOTIFY_MODE_UNGRAB, PROP_MODE_REPLACE, + RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID, SELECTION_CLIENT_CLOSE_MASK, SELECTION_WINDOW_DESTROY_MASK, SET_SELECTION_OWNER_MASK, STACK_MODE_ABOVE, STACK_MODE_BELOW, WINDOW_CLASS_INPUT_OUTPUT, }, @@ -71,7 +70,7 @@ use { xwayland::{XWaylandError, XWaylandEvent}, }, ahash::{AHashMap, AHashSet}, - bstr::{ByteSlice, ByteVec}, + bstr::ByteSlice, futures_util::{FutureExt, select}, smallvec::SmallVec, std::{ @@ -838,398 +837,6 @@ impl Wm { } } - fn compute_input_model(&self, data: &Rc) { - let has_wm_take_focus = data.info.protocols.contains(&self.atoms.WM_TAKE_FOCUS); - let accepts_input = data.info.icccm_hints.input.get(); - let model = match (accepts_input, has_wm_take_focus) { - (false, false) => XInputModel::None, - (true, false) => XInputModel::Passive, - (true, true) => XInputModel::Local, - (false, true) => XInputModel::Global, - }; - data.info.input_model.set(model); - } - - async fn load_window_wm_window_role(&self, data: &Rc) { - let property_changed = || { - if let Some(window) = data.window.get() { - window.toplevel_data.property_changed(TL_CHANGED_ROLE); - } - }; - let mut buf = vec![]; - match self - .c - .get_property::(data.window_id, self.atoms.WM_WINDOW_ROLE, 0, &mut buf) - .await - { - Ok(ty) if ty == ATOM_STRING => {} - Ok(ty) if ty == self.atoms.UTF8_STRING => {} - Ok(ty) => { - self.unexpected_type(data.window_id, "WM_WINDOW_ROLE", ty) - .await; - return; - } - Err(XconError::PropertyUnavailable) => { - data.info.role.borrow_mut().take(); - property_changed(); - return; - } - Err(e) => { - log::error!( - "Could not retrieve WM_WINDOW_ROLE property: {}", - ErrorFmt(e) - ); - return; - } - } - // log::info!("{} role {}", data.window_id, buf.as_bstr()); - *data.info.role.borrow_mut() = Some(buf.into_string_lossy()); - property_changed(); - } - - async fn load_window_wm_class(&self, data: &Rc) { - let mut buf = vec![]; - let property_changed = || { - if let Some(window) = data.window.get() { - let class = data.info.class.borrow(); - for handle in window.toplevel_data.manager_handles.lock().values() { - handle.send_app_id(class.as_deref().unwrap_or_default()); - handle.send_done(); - } - window.toplevel_data.property_changed(TL_CHANGED_CLASS_INST); - } - }; - match self - .c - .get_property::(data.window_id, ATOM_WM_CLASS, 0, &mut buf) - .await - { - Ok(ty) if ty == ATOM_STRING => {} - Ok(ty) if ty == self.atoms.UTF8_STRING => {} - Ok(ty) => { - self.unexpected_type(data.window_id, "WM_CLASS", ty).await; - return; - } - Err(XconError::PropertyUnavailable) => { - data.info.instance.borrow_mut().take(); - data.info.class.borrow_mut().take(); - property_changed(); - return; - } - Err(e) => { - log::error!("Could not retrieve WM_CLASS property: {}", ErrorFmt(e)); - return; - } - } - let mut iter = buf.split(|c| *c == 0); - let mut map = || Some(iter.next().unwrap_or(&[]).to_str_lossy().into_owned()); - *data.info.instance.borrow_mut() = map(); - *data.info.class.borrow_mut() = map(); - property_changed(); - } - - async fn load_window_wm_name2(&self, data: &Rc, prop: u32, name: &str) { - let mut buf = vec![]; - match self - .c - .get_property::(data.window_id, prop, 0, &mut buf) - .await - { - Ok(ty) if ty == ATOM_STRING && data.info.utf8_title.get() => return, - Ok(ty) if ty == ATOM_STRING => {} - Ok(ty) if ty == self.atoms.COMPOUND_TEXT => return, // used by java. - Ok(ty) if ty == self.atoms.UTF8_STRING => { - data.info.utf8_title.set(true); - } - Ok(ty) => { - self.unexpected_type(data.window_id, name, ty).await; - return; - } - Err(XconError::PropertyUnavailable) => return, - Err(e) => { - log::error!("Could not retrieve {} property: {}", name, ErrorFmt(e)); - return; - } - } - let title = buf.as_bstr().to_string(); - if let Some(window) = data.window.get() { - window.toplevel_data.set_title(&title); - window.tl_title_changed(); - } - *data.info.title.borrow_mut() = Some(title); - data.title_changed(); - } - - async fn unexpected_type(&self, window: u32, prop: &str, ty: u32) { - let mut ty_name = "unknown".as_bytes().as_bstr(); - let res = self.c.call(&GetAtomName { atom: ty }).await; - if let Ok(res) = &res { - ty_name = res.get().name; - } - log::error!( - "Property {} of window {} has unexpected type {} ({})", - prop, - window, - ty_name, - ty - ); - } - - async fn load_window_wm_name(&self, data: &Rc) { - self.load_window_wm_name2(data, ATOM_WM_NAME, "WM_NAME") - .await; - } - - async fn load_window_net_wm_name(&self, data: &Rc) { - self.load_window_wm_name2(data, self.atoms._NET_WM_NAME, "_NET_WM_NAME") - .await; - } - - async fn load_window_wm_transient_for(&self, data: &Rc) { - let mut buf = vec![]; - if let Err(e) = self - .c - .get_property::(data.window_id, ATOM_WM_TRANSIENT_FOR, ATOM_WINDOW, &mut buf) - .await - { - if not_matches!(e, XconError::PropertyUnavailable) { - log::error!( - "Could not retrieve WM_TRANSIENT_FOR property: {}", - ErrorFmt(e) - ); - } - } - if let Some(old) = data.parent.take() { - old.children.remove(&data.window_id); - } - if let Some(w) = buf.first() - && let Some(w) = self.windows.get(w) - { - if data.is_ancestor_of(w.clone()) { - log::error!("Cannot set WM_TRANSIENT_FOR because it would create a cycle"); - return; - } - w.children.set(data.window_id, data.clone()); - data.parent.set(Some(w.clone())); - } - } - - async fn load_window_wm_protocols(&self, data: &Rc) { - let mut buf = vec![]; - if let Err(e) = self - .c - .get_property::(data.window_id, self.atoms.WM_PROTOCOLS, ATOM_ATOM, &mut buf) - .await - { - if not_matches!(e, XconError::PropertyUnavailable) { - log::error!("Could not retrieve WM_PROTOCOLS property: {}", ErrorFmt(e)); - } - return; - } - data.info.protocols.clear(); - data.info - .protocols - .lock() - .extend(buf.iter().copied().map(|v| (v, ()))); - self.compute_input_model(data); - } - - async fn load_window_wm_hints(&self, data: &Rc) { - let mut buf = vec![]; - if let Err(e) = self - .c - .get_property::(data.window_id, self.atoms.WM_HINTS, 0, &mut buf) - .await - { - if not_matches!(e, XconError::PropertyUnavailable) { - log::error!("Could not retrieve WM_HINTS property: {}", ErrorFmt(e)); - } - data.info.icccm_hints.input.set(true); - self.compute_input_model(data); - return; - } - let mut values = [0; 9]; - let len = values.len().min(buf.len()); - values[..len].copy_from_slice(&buf[..len]); - data.info.icccm_hints.flags.set(values[0] as i32); - data.info.icccm_hints.input.set(values[1] != 0); - data.info.icccm_hints.initial_state.set(values[2] as i32); - data.info.icccm_hints.icon_pixmap.set(values[3]); - data.info.icccm_hints.icon_window.set(values[4]); - data.info.icccm_hints.icon_x.set(values[5] as i32); - data.info.icccm_hints.icon_y.set(values[6] as i32); - data.info.icccm_hints.icon_mask.set(values[7]); - data.info.icccm_hints.window_group.set(values[8]); - if data - .info - .icccm_hints - .flags - .get() - .not_contains(ICCCM_WM_HINT_INPUT) - { - data.info.icccm_hints.input.set(true); - } - self.compute_input_model(data); - } - - async fn load_window_wm_normal_hints(&self, data: &Rc) { - let mut buf = vec![]; - if let Err(e) = self - .c - .get_property::( - data.window_id, - self.atoms.WM_NORMAL_HINTS, - ATOM_WM_SIZE_HINTS, - &mut buf, - ) - .await - { - if not_matches!(e, XconError::PropertyUnavailable) { - log::error!( - "Could not retrieve WM_NORMAL_HINTS property: {}", - ErrorFmt(e) - ); - } - return; - } - let mut values = [0; 18]; - let len = values.len().min(buf.len()); - values[..len].copy_from_slice(&buf[..len]); - data.info.normal_hints.flags.set(values[0]); - data.info.normal_hints.x.set(values[1] as i32); - data.info.normal_hints.y.set(values[2] as i32); - data.info.normal_hints.width.set(values[3] as i32); - data.info.normal_hints.height.set(values[4] as i32); - data.info.normal_hints.min_width.set(values[5] as i32); - data.info.normal_hints.min_height.set(values[6] as i32); - data.info.normal_hints.max_width.set(values[7] as i32); - data.info.normal_hints.max_height.set(values[8] as i32); - data.info.normal_hints.width_inc.set(values[9] as i32); - data.info.normal_hints.height_inc.set(values[10] as i32); - data.info.normal_hints.min_aspect_num.set(values[11] as i32); - data.info.normal_hints.min_aspect_den.set(values[12] as i32); - data.info.normal_hints.max_aspect_num.set(values[13] as i32); - data.info.normal_hints.max_aspect_den.set(values[14] as i32); - data.info.normal_hints.base_width.set(values[15] as i32); - data.info.normal_hints.base_height.set(values[16] as i32); - data.info.normal_hints.win_gravity.set(values[17]); - self.update_wants_floating(data); - } - - async fn load_window_motif_wm_hints(&self, data: &Rc) { - let mut buf = vec![]; - if let Err(e) = self - .c - .get_property::(data.window_id, self.atoms._MOTIF_WM_HINTS, 0, &mut buf) - .await - { - if not_matches!(e, XconError::PropertyUnavailable) { - log::error!( - "Could not retrieve _MOTIF_WM_HINTS property: {}", - ErrorFmt(e) - ); - } - return; - } - let mut values = [0; 5]; - let len = values.len().min(buf.len()); - values[..len].copy_from_slice(&buf[..len]); - data.info - .motif_hints - .flags - .set(values[MWM_HINTS_FLAGS_FIELD]); - data.info - .motif_hints - .decorations - .set(values[MWM_HINTS_DECORATIONS_FIELD]); - } - - async fn load_window_net_startup_id(&self, data: &Rc) { - let mut buf = vec![]; - match self - .c - .get_property::(data.window_id, self.atoms._NET_STARTUP_ID, 0, &mut buf) - .await - { - Ok(ty) if ty == ATOM_STRING => {} - Ok(ty) if ty == self.atoms.UTF8_STRING => {} - Ok(ty) => { - self.unexpected_type(data.window_id, "_NET_STARTUP_ID", ty) - .await; - return; - } - Err(XconError::PropertyUnavailable) => return, - Err(e) => { - log::error!( - "Could not retrieve _NET_STARTUP_ID property: {}", - ErrorFmt(e) - ); - return; - } - } - *data.info.startup_id.borrow_mut() = Some(buf.into()); - } - - async fn load_window_net_wm_state(&self, data: &Rc) { - data.info.fullscreen.set(false); - let mut buf = vec![]; - if let Err(e) = self - .c - .get_property::(data.window_id, self.atoms._NET_WM_STATE, 0, &mut buf) - .await - { - if not_matches!(e, XconError::PropertyUnavailable) { - log::error!("Could not retrieve _NET_WM_STATE property: {}", ErrorFmt(e)); - } - return; - } - for prop in buf { - if prop == self.atoms._NET_WM_STATE_MODAL { - data.info.modal.set(true); - self.update_wants_floating(data); - } else if prop == self.atoms._NET_WM_STATE_FULLSCREEN { - data.info.fullscreen.set(true); - } else if prop == self.atoms._NET_WM_STATE_MAXIMIZED_VERT { - data.info.maximized_vert.set(true); - } else if prop == self.atoms._NET_WM_STATE_MAXIMIZED_HORZ { - data.info.maximized_horz.set(true); - } else if prop == self.atoms._NET_WM_STATE_HIDDEN { - data.info.minimized.set(true); - } - } - } - - async fn load_window_net_wm_window_type(&self, data: &Rc) { - let mut buf = vec![]; - if let Err(e) = self - .c - .get_property::( - data.window_id, - self.atoms._NET_WM_WINDOW_TYPE, - ATOM_ATOM, - &mut buf, - ) - .await - { - if not_matches!(e, XconError::PropertyUnavailable) { - log::error!( - "Could not retrieve _NET_WM_WINDOW_TYPE property: {}", - ErrorFmt(e) - ); - } - return; - } - data.info - .never_focus - .set(buf.iter().any(|t| self.never_focus.contains(t))); - data.info.window_types.clear(); - data.info - .window_types - .lock() - .extend(buf.iter().copied().map(|v| (v, ()))); - self.update_wants_floating(data); - } - async fn create_window(&mut self, data: &Rc, surface: Rc) { if data.window.is_some() { log::error!("The xwindow has already been constructed"); diff --git a/src/xwayland/xwm/properties.rs b/src/xwayland/xwm/properties.rs new file mode 100644 index 00000000..fc8cf48d --- /dev/null +++ b/src/xwayland/xwm/properties.rs @@ -0,0 +1,413 @@ +use { + super::Wm, + crate::{ + criteria::tlm::{TL_CHANGED_CLASS_INST, TL_CHANGED_ROLE}, + ifs::wl_surface::x_surface::xwindow::{XInputModel, XwindowData}, + tree::ToplevelNode, + utils::{bitflags::BitflagsExt, errorfmt::ErrorFmt}, + wire_xcon::GetAtomName, + xcon::{ + XconError, + consts::{ + ATOM_ATOM, ATOM_STRING, ATOM_WINDOW, ATOM_WM_CLASS, ATOM_WM_NAME, + ATOM_WM_SIZE_HINTS, ATOM_WM_TRANSIENT_FOR, ICCCM_WM_HINT_INPUT, + MWM_HINTS_DECORATIONS_FIELD, MWM_HINTS_FLAGS_FIELD, + }, + }, + }, + bstr::{ByteSlice, ByteVec}, + std::rc::Rc, +}; + +impl Wm { + pub(super) fn compute_input_model(&self, data: &Rc) { + let has_wm_take_focus = data.info.protocols.contains(&self.atoms.WM_TAKE_FOCUS); + let accepts_input = data.info.icccm_hints.input.get(); + let model = match (accepts_input, has_wm_take_focus) { + (false, false) => XInputModel::None, + (true, false) => XInputModel::Passive, + (true, true) => XInputModel::Local, + (false, true) => XInputModel::Global, + }; + data.info.input_model.set(model); + } + + pub(super) async fn load_window_wm_window_role(&self, data: &Rc) { + let property_changed = || { + if let Some(window) = data.window.get() { + window.toplevel_data.property_changed(TL_CHANGED_ROLE); + } + }; + let mut buf = vec![]; + match self + .c + .get_property::(data.window_id, self.atoms.WM_WINDOW_ROLE, 0, &mut buf) + .await + { + Ok(ty) if ty == ATOM_STRING => {} + Ok(ty) if ty == self.atoms.UTF8_STRING => {} + Ok(ty) => { + self.unexpected_type(data.window_id, "WM_WINDOW_ROLE", ty) + .await; + return; + } + Err(XconError::PropertyUnavailable) => { + data.info.role.borrow_mut().take(); + property_changed(); + return; + } + Err(e) => { + log::error!( + "Could not retrieve WM_WINDOW_ROLE property: {}", + ErrorFmt(e) + ); + return; + } + } + *data.info.role.borrow_mut() = Some(buf.into_string_lossy()); + property_changed(); + } + + pub(super) async fn load_window_wm_class(&self, data: &Rc) { + let mut buf = vec![]; + let property_changed = || { + if let Some(window) = data.window.get() { + let class = data.info.class.borrow(); + for handle in window.toplevel_data.manager_handles.lock().values() { + handle.send_app_id(class.as_deref().unwrap_or_default()); + handle.send_done(); + } + window.toplevel_data.property_changed(TL_CHANGED_CLASS_INST); + } + }; + match self + .c + .get_property::(data.window_id, ATOM_WM_CLASS, 0, &mut buf) + .await + { + Ok(ty) if ty == ATOM_STRING => {} + Ok(ty) if ty == self.atoms.UTF8_STRING => {} + Ok(ty) => { + self.unexpected_type(data.window_id, "WM_CLASS", ty).await; + return; + } + Err(XconError::PropertyUnavailable) => { + data.info.instance.borrow_mut().take(); + data.info.class.borrow_mut().take(); + property_changed(); + return; + } + Err(e) => { + log::error!("Could not retrieve WM_CLASS property: {}", ErrorFmt(e)); + return; + } + } + let mut iter = buf.split(|c| *c == 0); + let mut map = || Some(iter.next().unwrap_or(&[]).to_str_lossy().into_owned()); + *data.info.instance.borrow_mut() = map(); + *data.info.class.borrow_mut() = map(); + property_changed(); + } + + async fn load_window_wm_name2(&self, data: &Rc, prop: u32, name: &str) { + let mut buf = vec![]; + match self + .c + .get_property::(data.window_id, prop, 0, &mut buf) + .await + { + Ok(ty) if ty == ATOM_STRING && data.info.utf8_title.get() => return, + Ok(ty) if ty == ATOM_STRING => {} + Ok(ty) if ty == self.atoms.COMPOUND_TEXT => return, + Ok(ty) if ty == self.atoms.UTF8_STRING => { + data.info.utf8_title.set(true); + } + Ok(ty) => { + self.unexpected_type(data.window_id, name, ty).await; + return; + } + Err(XconError::PropertyUnavailable) => return, + Err(e) => { + log::error!("Could not retrieve {} property: {}", name, ErrorFmt(e)); + return; + } + } + let title = buf.as_bstr().to_string(); + if let Some(window) = data.window.get() { + window.toplevel_data.set_title(&title); + window.tl_title_changed(); + } + *data.info.title.borrow_mut() = Some(title); + data.title_changed(); + } + + async fn unexpected_type(&self, window: u32, prop: &str, ty: u32) { + let mut ty_name = "unknown".as_bytes().as_bstr(); + let res = self.c.call(&GetAtomName { atom: ty }).await; + if let Ok(res) = &res { + ty_name = res.get().name; + } + log::error!( + "Property {} of window {} has unexpected type {} ({})", + prop, + window, + ty_name, + ty + ); + } + + pub(super) async fn load_window_wm_name(&self, data: &Rc) { + self.load_window_wm_name2(data, ATOM_WM_NAME, "WM_NAME") + .await; + } + + pub(super) async fn load_window_net_wm_name(&self, data: &Rc) { + self.load_window_wm_name2(data, self.atoms._NET_WM_NAME, "_NET_WM_NAME") + .await; + } + + pub(super) async fn load_window_wm_transient_for(&self, data: &Rc) { + let mut buf = vec![]; + if let Err(e) = self + .c + .get_property::(data.window_id, ATOM_WM_TRANSIENT_FOR, ATOM_WINDOW, &mut buf) + .await + { + if not_matches!(e, XconError::PropertyUnavailable) { + log::error!( + "Could not retrieve WM_TRANSIENT_FOR property: {}", + ErrorFmt(e) + ); + } + } + if let Some(old) = data.parent.take() { + old.children.remove(&data.window_id); + } + if let Some(w) = buf.first() + && let Some(w) = self.windows.get(w) + { + if data.is_ancestor_of(w.clone()) { + log::error!("Cannot set WM_TRANSIENT_FOR because it would create a cycle"); + return; + } + w.children.set(data.window_id, data.clone()); + data.parent.set(Some(w.clone())); + } + } + + pub(super) async fn load_window_wm_protocols(&self, data: &Rc) { + let mut buf = vec![]; + if let Err(e) = self + .c + .get_property::(data.window_id, self.atoms.WM_PROTOCOLS, ATOM_ATOM, &mut buf) + .await + { + if not_matches!(e, XconError::PropertyUnavailable) { + log::error!("Could not retrieve WM_PROTOCOLS property: {}", ErrorFmt(e)); + } + return; + } + data.info.protocols.clear(); + data.info + .protocols + .lock() + .extend(buf.iter().copied().map(|v| (v, ()))); + self.compute_input_model(data); + } + + pub(super) async fn load_window_wm_hints(&self, data: &Rc) { + let mut buf = vec![]; + if let Err(e) = self + .c + .get_property::(data.window_id, self.atoms.WM_HINTS, 0, &mut buf) + .await + { + if not_matches!(e, XconError::PropertyUnavailable) { + log::error!("Could not retrieve WM_HINTS property: {}", ErrorFmt(e)); + } + data.info.icccm_hints.input.set(true); + self.compute_input_model(data); + return; + } + let mut values = [0; 9]; + let len = values.len().min(buf.len()); + values[..len].copy_from_slice(&buf[..len]); + data.info.icccm_hints.flags.set(values[0] as i32); + data.info.icccm_hints.input.set(values[1] != 0); + data.info.icccm_hints.initial_state.set(values[2] as i32); + data.info.icccm_hints.icon_pixmap.set(values[3]); + data.info.icccm_hints.icon_window.set(values[4]); + data.info.icccm_hints.icon_x.set(values[5] as i32); + data.info.icccm_hints.icon_y.set(values[6] as i32); + data.info.icccm_hints.icon_mask.set(values[7]); + data.info.icccm_hints.window_group.set(values[8]); + if data + .info + .icccm_hints + .flags + .get() + .not_contains(ICCCM_WM_HINT_INPUT) + { + data.info.icccm_hints.input.set(true); + } + self.compute_input_model(data); + } + + pub(super) async fn load_window_wm_normal_hints(&self, data: &Rc) { + let mut buf = vec![]; + if let Err(e) = self + .c + .get_property::( + data.window_id, + self.atoms.WM_NORMAL_HINTS, + ATOM_WM_SIZE_HINTS, + &mut buf, + ) + .await + { + if not_matches!(e, XconError::PropertyUnavailable) { + log::error!( + "Could not retrieve WM_NORMAL_HINTS property: {}", + ErrorFmt(e) + ); + } + return; + } + let mut values = [0; 18]; + let len = values.len().min(buf.len()); + values[..len].copy_from_slice(&buf[..len]); + data.info.normal_hints.flags.set(values[0]); + data.info.normal_hints.x.set(values[1] as i32); + data.info.normal_hints.y.set(values[2] as i32); + data.info.normal_hints.width.set(values[3] as i32); + data.info.normal_hints.height.set(values[4] as i32); + data.info.normal_hints.min_width.set(values[5] as i32); + data.info.normal_hints.min_height.set(values[6] as i32); + data.info.normal_hints.max_width.set(values[7] as i32); + data.info.normal_hints.max_height.set(values[8] as i32); + data.info.normal_hints.width_inc.set(values[9] as i32); + data.info.normal_hints.height_inc.set(values[10] as i32); + data.info.normal_hints.min_aspect_num.set(values[11] as i32); + data.info.normal_hints.min_aspect_den.set(values[12] as i32); + data.info.normal_hints.max_aspect_num.set(values[13] as i32); + data.info.normal_hints.max_aspect_den.set(values[14] as i32); + data.info.normal_hints.base_width.set(values[15] as i32); + data.info.normal_hints.base_height.set(values[16] as i32); + data.info.normal_hints.win_gravity.set(values[17]); + self.update_wants_floating(data); + } + + pub(super) async fn load_window_motif_wm_hints(&self, data: &Rc) { + let mut buf = vec![]; + if let Err(e) = self + .c + .get_property::(data.window_id, self.atoms._MOTIF_WM_HINTS, 0, &mut buf) + .await + { + if not_matches!(e, XconError::PropertyUnavailable) { + log::error!( + "Could not retrieve _MOTIF_WM_HINTS property: {}", + ErrorFmt(e) + ); + } + return; + } + let mut values = [0; 5]; + let len = values.len().min(buf.len()); + values[..len].copy_from_slice(&buf[..len]); + data.info + .motif_hints + .flags + .set(values[MWM_HINTS_FLAGS_FIELD]); + data.info + .motif_hints + .decorations + .set(values[MWM_HINTS_DECORATIONS_FIELD]); + } + + pub(super) async fn load_window_net_startup_id(&self, data: &Rc) { + let mut buf = vec![]; + match self + .c + .get_property::(data.window_id, self.atoms._NET_STARTUP_ID, 0, &mut buf) + .await + { + Ok(ty) if ty == ATOM_STRING => {} + Ok(ty) if ty == self.atoms.UTF8_STRING => {} + Ok(ty) => { + self.unexpected_type(data.window_id, "_NET_STARTUP_ID", ty) + .await; + return; + } + Err(XconError::PropertyUnavailable) => return, + Err(e) => { + log::error!( + "Could not retrieve _NET_STARTUP_ID property: {}", + ErrorFmt(e) + ); + return; + } + } + *data.info.startup_id.borrow_mut() = Some(buf.into()); + } + + pub(super) async fn load_window_net_wm_state(&self, data: &Rc) { + data.info.fullscreen.set(false); + let mut buf = vec![]; + if let Err(e) = self + .c + .get_property::(data.window_id, self.atoms._NET_WM_STATE, 0, &mut buf) + .await + { + if not_matches!(e, XconError::PropertyUnavailable) { + log::error!("Could not retrieve _NET_WM_STATE property: {}", ErrorFmt(e)); + } + return; + } + for prop in buf { + if prop == self.atoms._NET_WM_STATE_MODAL { + data.info.modal.set(true); + self.update_wants_floating(data); + } else if prop == self.atoms._NET_WM_STATE_FULLSCREEN { + data.info.fullscreen.set(true); + } else if prop == self.atoms._NET_WM_STATE_MAXIMIZED_VERT { + data.info.maximized_vert.set(true); + } else if prop == self.atoms._NET_WM_STATE_MAXIMIZED_HORZ { + data.info.maximized_horz.set(true); + } else if prop == self.atoms._NET_WM_STATE_HIDDEN { + data.info.minimized.set(true); + } + } + } + + pub(super) async fn load_window_net_wm_window_type(&self, data: &Rc) { + let mut buf = vec![]; + if let Err(e) = self + .c + .get_property::( + data.window_id, + self.atoms._NET_WM_WINDOW_TYPE, + ATOM_ATOM, + &mut buf, + ) + .await + { + if not_matches!(e, XconError::PropertyUnavailable) { + log::error!( + "Could not retrieve _NET_WM_WINDOW_TYPE property: {}", + ErrorFmt(e) + ); + } + return; + } + data.info + .never_focus + .set(buf.iter().any(|t| self.never_focus.contains(t))); + data.info.window_types.clear(); + data.info + .window_types + .lock() + .extend(buf.iter().copied().map(|v| (v, ()))); + self.update_wants_floating(data); + } +} diff --git a/src/xwayland/xwm/selection.rs b/src/xwayland/xwm/selection.rs index 0bbaa1ea..7102cd09 100644 --- a/src/xwayland/xwm/selection.rs +++ b/src/xwayland/xwm/selection.rs @@ -1,4 +1,8 @@ -use {super::*, super::transfer::{WaylandToXTransfer, XToWaylandTransfer}}; +use { + super::*, + super::transfer::{WaylandToXTransfer, XToWaylandTransfer}, + crate::wire_xcon::GetAtomName, +}; pub(super) struct EnhancedOffer { offer: Rc,