From 59c2b53350450d1c4dc3d70453a169d4afef7cbe Mon Sep 17 00:00:00 2001 From: entailz Date: Thu, 30 Apr 2026 03:00:44 -0700 Subject: [PATCH] Add support for experimental xx_foreign_toplevel_geometry_tracker_v1 as well as tracking_manager_v1. --- jay-config/src/client.rs | 2 + src/client.rs | 12 +- src/globals.rs | 2 + src/ifs.rs | 2 + ...xx_foreign_toplevel_geometry_tracker_v1.rs | 99 ++++++++++++++ ...n_toplevel_geometry_tracking_manager_v1.rs | 125 ++++++++++++++++++ src/tree/toplevel.rs | 23 +++- .../src/config/parsers/capabilities.rs | 4 +- ...x_foreign_toplevel_geometry_tracker_v1.txt | 16 +++ ..._toplevel_geometry_tracking_manager_v1.txt | 7 + 10 files changed, 284 insertions(+), 8 deletions(-) create mode 100644 src/ifs/xx_foreign_toplevel_geometry_tracker_v1.rs create mode 100644 src/ifs/xx_foreign_toplevel_geometry_tracking_manager_v1.rs create mode 100644 wire/xx_foreign_toplevel_geometry_tracker_v1.txt create mode 100644 wire/xx_foreign_toplevel_geometry_tracking_manager_v1.txt diff --git a/jay-config/src/client.rs b/jay-config/src/client.rs index 7093687f..38a82d42 100644 --- a/jay-config/src/client.rs +++ b/jay-config/src/client.rs @@ -233,5 +233,7 @@ bitflags! { pub const CC_GAMMA_CONTROL_MANAGER = 1 << 14, /// Grants access to the `zwlr_virtual_pointer_manager_v1` global. pub const CC_VIRTUAL_POINTER = 1 << 15, + /// Grants access to the `ext_foreign_toplevel_geometry_tracking_manager_v1` global. + pub const CC_FOREIGN_TOPLEVEL_GEOMETRY_TRACKING = 1 << 16, } } diff --git a/src/client.rs b/src/client.rs index 0d408eb1..e21665ba 100644 --- a/src/client.rs +++ b/src/client.rs @@ -64,10 +64,11 @@ bitflags! { CAP_DRM_LEASE = 1 << 9, CAP_INPUT_METHOD = 1 << 10, CAP_WORKSPACE = 1 << 11, - CAP_FOREIGN_TOPLEVEL_MANAGER = 1 << 12, - CAP_HEAD_MANAGER = 1 << 13, - CAP_GAMMA_CONTROL_MANAGER = 1 << 14, - CAP_VIRTUAL_POINTER_MANAGER = 1 << 15, + CAP_FOREIGN_TOPLEVEL_MANAGER = 1 << 12, + CAP_HEAD_MANAGER = 1 << 13, + CAP_GAMMA_CONTROL_MANAGER = 1 << 14, + CAP_VIRTUAL_POINTER_MANAGER = 1 << 15, + CAP_FOREIGN_TOPLEVEL_GEOMETRY_TRACKING = 1 << 16, } impl StaticText for ClientCapsEnum { @@ -89,6 +90,9 @@ impl StaticText for ClientCapsEnum { ClientCapsEnum::CAP_HEAD_MANAGER => "head-manager", ClientCapsEnum::CAP_GAMMA_CONTROL_MANAGER => "gamma-control-manager", ClientCapsEnum::CAP_VIRTUAL_POINTER_MANAGER => "virtual-pointer", + ClientCapsEnum::CAP_FOREIGN_TOPLEVEL_GEOMETRY_TRACKING => { + "foreign-toplevel-geometry-tracking" + } } } } diff --git a/src/globals.rs b/src/globals.rs index af74f100..8a6962c6 100644 --- a/src/globals.rs +++ b/src/globals.rs @@ -65,6 +65,7 @@ use { xdg_toplevel_tag_manager_v1::XdgToplevelTagManagerV1Global, xdg_wm_base::XdgWmBaseGlobal, xdg_wm_dialog_v1::XdgWmDialogV1Global, + xx_foreign_toplevel_geometry_tracking_manager_v1::XxForeignToplevelGeometryTrackingManagerV1Global, zwlr_foreign_toplevel_manager_v1::ZwlrForeignToplevelManagerV1Global, zwlr_gamma_control_manager_v1::ZwlrGammaControlManagerV1Global, zwlr_layer_shell_v1::ZwlrLayerShellV1Global, @@ -252,6 +253,7 @@ singletons! { WpLinuxDrmSyncobjManagerV1, WpPresentation, ZwlrVirtualPointerManagerV1, + XxForeignToplevelGeometryTrackingManagerV1, } pub struct Globals { diff --git a/src/ifs.rs b/src/ifs.rs index 32464c2c..f29b0d67 100644 --- a/src/ifs.rs +++ b/src/ifs.rs @@ -88,6 +88,8 @@ pub mod xdg_toplevel_drag_v1; pub mod xdg_toplevel_tag_manager_v1; pub mod xdg_wm_base; pub mod xdg_wm_dialog_v1; +pub mod xx_foreign_toplevel_geometry_tracker_v1; +pub mod xx_foreign_toplevel_geometry_tracking_manager_v1; pub mod zwlr_foreign_toplevel_handle_v1; pub mod zwlr_foreign_toplevel_manager_v1; pub mod zwlr_gamma_control_manager_v1; diff --git a/src/ifs/xx_foreign_toplevel_geometry_tracker_v1.rs b/src/ifs/xx_foreign_toplevel_geometry_tracker_v1.rs new file mode 100644 index 00000000..db9f8274 --- /dev/null +++ b/src/ifs/xx_foreign_toplevel_geometry_tracker_v1.rs @@ -0,0 +1,99 @@ +use { + crate::{ + client::{Client, ClientError}, + leaks::Tracker, + object::{Object, Version}, + rect::Rect, + state::State, + tree::ToplevelOpt, + wire::{ + XxForeignToplevelGeometryTrackerV1Id, + xx_foreign_toplevel_geometry_tracker_v1::*, + }, + }, + std::rc::Rc, + thiserror::Error, +}; + +pub struct XxForeignToplevelGeometryTrackerV1 { + pub id: XxForeignToplevelGeometryTrackerV1Id, + pub client: Rc, + pub tracker: Tracker, + pub toplevel: ToplevelOpt, + pub version: Version, +} + +impl XxForeignToplevelGeometryTrackerV1 { + fn detach(&self) { + if let Some(tl) = self.toplevel.get() { + tl.tl_data() + .geometry_trackers + .remove(&(self.client.id, self.id)); + } + } + + pub fn send_finished(&self) { + self.client.event(Finished { self_id: self.id }); + } + + pub fn send_geometry_update(&self, rect: &Rect, state: &State) { + if rect.is_empty() { + self.client.event(Done { self_id: self.id }); + return; + } + for output in state.globals.outputs.lock().values() { + let output_pos = output.pos.get(); + if !rect.intersects(&output_pos) { + continue; + } + let scale = output.persistent.scale.get().to_f64(); + let rel_x = rect.x1() - output_pos.x1(); + let rel_y = rect.y1() - output_pos.y1(); + let hw_x = (rel_x as f64 * scale).round() as i32; + let hw_y = (rel_y as f64 * scale).round() as i32; + let hw_w = (rect.width() as f64 * scale).round() as u32; + let hw_h = (rect.height() as f64 * scale).round() as u32; + self.client.event(Geometry { + self_id: self.id, + output: output.name.raw(), + x: hw_x, + y: hw_y, + width: hw_w, + height: hw_h, + }); + } + self.client.event(Done { self_id: self.id }); + } +} + +impl XxForeignToplevelGeometryTrackerV1RequestHandler + for XxForeignToplevelGeometryTrackerV1 +{ + type Error = XxForeignToplevelGeometryTrackerV1Error; + + fn destroy(&self, _req: Destroy, _slf: &Rc) -> Result<(), Self::Error> { + self.detach(); + self.client.remove_obj(self)?; + Ok(()) + } +} + +object_base! { + self = XxForeignToplevelGeometryTrackerV1; + version = self.version; +} + +impl Object for XxForeignToplevelGeometryTrackerV1 { + fn break_loops(&self) { + self.detach(); + } +} + +simple_add_obj!(XxForeignToplevelGeometryTrackerV1); + +#[derive(Debug, Error)] +pub enum XxForeignToplevelGeometryTrackerV1Error { + #[error(transparent)] + ClientError(Box), +} +efrom!(XxForeignToplevelGeometryTrackerV1Error, ClientError); diff --git a/src/ifs/xx_foreign_toplevel_geometry_tracking_manager_v1.rs b/src/ifs/xx_foreign_toplevel_geometry_tracking_manager_v1.rs new file mode 100644 index 00000000..ca57468c --- /dev/null +++ b/src/ifs/xx_foreign_toplevel_geometry_tracking_manager_v1.rs @@ -0,0 +1,125 @@ +use { + crate::{ + client::{CAP_FOREIGN_TOPLEVEL_GEOMETRY_TRACKING, Client, ClientCaps, ClientError}, + globals::{Global, GlobalName}, + ifs::{ + xx_foreign_toplevel_geometry_tracker_v1::XxForeignToplevelGeometryTrackerV1, + ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1, + }, + leaks::Tracker, + object::{Object, Version}, + wire::{ + XxForeignToplevelGeometryTrackingManagerV1Id, + xx_foreign_toplevel_geometry_tracking_manager_v1::*, + }, + }, + std::rc::Rc, + thiserror::Error, +}; + +pub struct XxForeignToplevelGeometryTrackingManagerV1Global { + pub name: GlobalName, +} + +impl XxForeignToplevelGeometryTrackingManagerV1Global { + pub fn new(name: GlobalName) -> Self { + Self { name } + } + + fn bind_( + self: Rc, + id: XxForeignToplevelGeometryTrackingManagerV1Id, + client: &Rc, + version: Version, + ) -> Result<(), XxForeignToplevelGeometryTrackingManagerV1Error> { + let obj = Rc::new(XxForeignToplevelGeometryTrackingManagerV1 { + id, + client: client.clone(), + tracker: Default::default(), + version, + }); + track!(client, obj); + client.add_client_obj(&obj)?; + Ok(()) + } +} + +pub struct XxForeignToplevelGeometryTrackingManagerV1 { + pub id: XxForeignToplevelGeometryTrackingManagerV1Id, + pub client: Rc, + pub tracker: Tracker, + pub version: Version, +} + +impl XxForeignToplevelGeometryTrackingManagerV1RequestHandler + for XxForeignToplevelGeometryTrackingManagerV1 +{ + type Error = XxForeignToplevelGeometryTrackingManagerV1Error; + + fn destroy(&self, _req: Destroy, _slf: &Rc) -> Result<(), Self::Error> { + self.client.remove_obj(self)?; + Ok(()) + } + + fn get_geometry_tracker( + &self, + req: GetGeometryTracker, + _slf: &Rc, + ) -> Result<(), Self::Error> { + let handle: Rc = self.client.lookup(req.toplevel)?; + let toplevel = handle.toplevel.clone(); + let tracker_obj = Rc::new(XxForeignToplevelGeometryTrackerV1 { + id: req.tracker, + client: self.client.clone(), + tracker: Default::default(), + toplevel: toplevel.clone(), + version: self.version, + }); + track!(self.client, tracker_obj); + self.client.add_client_obj(&tracker_obj)?; + if let Some(tl) = toplevel.get() { + let data = tl.tl_data(); + data.geometry_trackers + .set((self.client.id, req.tracker), tracker_obj.clone()); + let rect = tl.node_absolute_position(); + tracker_obj.send_geometry_update(&rect, &data.state); + } else { + tracker_obj.send_finished(); + } + Ok(()) + } +} + +global_base!( + XxForeignToplevelGeometryTrackingManagerV1Global, + XxForeignToplevelGeometryTrackingManagerV1, + XxForeignToplevelGeometryTrackingManagerV1Error +); + +impl Global for XxForeignToplevelGeometryTrackingManagerV1Global { + fn version(&self) -> u32 { + 1 + } + + fn required_caps(&self) -> ClientCaps { + CAP_FOREIGN_TOPLEVEL_GEOMETRY_TRACKING + } +} + +simple_add_global!(XxForeignToplevelGeometryTrackingManagerV1Global); + +object_base! { + self = XxForeignToplevelGeometryTrackingManagerV1; + version = self.version; +} + +impl Object for XxForeignToplevelGeometryTrackingManagerV1 {} + +simple_add_obj!(XxForeignToplevelGeometryTrackingManagerV1); + +#[derive(Debug, Error)] +pub enum XxForeignToplevelGeometryTrackingManagerV1Error { + #[error(transparent)] + ClientError(Box), +} +efrom!(XxForeignToplevelGeometryTrackingManagerV1Error, ClientError); diff --git a/src/tree/toplevel.rs b/src/tree/toplevel.rs index a196286d..02bba848 100644 --- a/src/tree/toplevel.rs +++ b/src/tree/toplevel.rs @@ -21,6 +21,7 @@ use { xdg_surface::xdg_toplevel::XdgToplevelToplevelData, }, wp_content_type_v1::ContentType, + xx_foreign_toplevel_geometry_tracker_v1::XxForeignToplevelGeometryTrackerV1, zwlr_foreign_toplevel_handle_v1::ZwlrForeignToplevelHandleV1, zwlr_foreign_toplevel_manager_v1::ZwlrForeignToplevelManagerV1, }, @@ -37,7 +38,7 @@ use { }, wire::{ ExtForeignToplevelHandleV1Id, ExtImageCopyCaptureSessionV1Id, JayScreencastId, - JayToplevelId, ZwlrForeignToplevelHandleV1Id, + JayToplevelId, XxForeignToplevelGeometryTrackerV1Id, ZwlrForeignToplevelHandleV1Id, }, }, jay_config::{window, window::WindowType}, @@ -195,7 +196,13 @@ impl ToplevelNode for T { data.float_width.set(rect.width()); data.float_height.set(rect.height()); } - self.tl_change_extents_impl(rect) + let _ = data; + let slf = self.clone(); + self.tl_change_extents_impl(rect); + let data = slf.tl_data(); + for tracker in data.geometry_trackers.lock().values() { + tracker.send_geometry_update(rect, &data.state); + } } fn tl_set_visible(&self, visible: bool) { @@ -401,6 +408,10 @@ pub struct ToplevelData { pub identifier: Cell, pub handles: CopyHashMap<(ClientId, ExtForeignToplevelHandleV1Id), Rc>, + pub geometry_trackers: CopyHashMap< + (ClientId, XxForeignToplevelGeometryTrackerV1Id), + Rc, + >, pub manager_handles: CopyHashMap<(ClientId, ZwlrForeignToplevelHandleV1Id), Rc>, pub render_highlight: NumCell, @@ -459,6 +470,7 @@ impl ToplevelData { app_id: Default::default(), identifier: Cell::new(id), handles: Default::default(), + geometry_trackers: Default::default(), manager_handles: Default::default(), render_highlight: Default::default(), jay_toplevels: Default::default(), @@ -563,6 +575,12 @@ impl ToplevelData { handle.send_closed(); } } + { + let mut trackers = self.geometry_trackers.lock(); + for tracker in trackers.drain_values() { + tracker.send_finished(); + } + } self.detach_node(node); self.property_changed(TL_CHANGED_DESTROYED); } @@ -951,7 +969,6 @@ impl ToplevelData { }; parent.node_is_workspace() } - } impl Drop for ToplevelData { diff --git a/toml-config/src/config/parsers/capabilities.rs b/toml-config/src/config/parsers/capabilities.rs index 4cde9158..f3f7bb71 100644 --- a/toml-config/src/config/parsers/capabilities.rs +++ b/toml-config/src/config/parsers/capabilities.rs @@ -7,7 +7,8 @@ use { }, }, jay_config::client::{ - CC_DATA_CONTROL, CC_DRM_LEASE, CC_FOREIGN_TOPLEVEL_LIST, CC_FOREIGN_TOPLEVEL_MANAGER, + CC_DATA_CONTROL, CC_DRM_LEASE, CC_FOREIGN_TOPLEVEL_GEOMETRY_TRACKING, + CC_FOREIGN_TOPLEVEL_LIST, CC_FOREIGN_TOPLEVEL_MANAGER, CC_GAMMA_CONTROL_MANAGER, CC_HEAD_MANAGER, CC_IDLE_NOTIFIER, CC_INPUT_METHOD, CC_LAYER_SHELL, CC_SCREENCOPY, CC_SEAT_MANAGER, CC_SESSION_LOCK, CC_VIRTUAL_KEYBOARD, CC_VIRTUAL_POINTER, CC_WORKSPACE_MANAGER, ClientCapabilities, @@ -49,6 +50,7 @@ impl Parser for CapabilitiesParser { "head-manager" => CC_HEAD_MANAGER, "gamma-control-manager" => CC_GAMMA_CONTROL_MANAGER, "virtual-pointer" => CC_VIRTUAL_POINTER, + "foreign-toplevel-geometry-tracking" => CC_FOREIGN_TOPLEVEL_GEOMETRY_TRACKING, _ => { return Err( CapabilitiesParserError::UnknownCapability(string.to_owned()).spanned(span), diff --git a/wire/xx_foreign_toplevel_geometry_tracker_v1.txt b/wire/xx_foreign_toplevel_geometry_tracker_v1.txt new file mode 100644 index 00000000..b3956c2f --- /dev/null +++ b/wire/xx_foreign_toplevel_geometry_tracker_v1.txt @@ -0,0 +1,16 @@ +request destroy (destructor) { +} + +event finished { +} + +event done { +} + +event geometry { + output: u32, + x: i32, + y: i32, + width: u32, + height: u32, +} diff --git a/wire/xx_foreign_toplevel_geometry_tracking_manager_v1.txt b/wire/xx_foreign_toplevel_geometry_tracking_manager_v1.txt new file mode 100644 index 00000000..befd6942 --- /dev/null +++ b/wire/xx_foreign_toplevel_geometry_tracking_manager_v1.txt @@ -0,0 +1,7 @@ +request destroy (destructor) { +} + +request get_geometry_tracker { + tracker: id(xx_foreign_toplevel_geometry_tracker_v1) (new), + toplevel: id(ext_foreign_toplevel_handle_v1), +}