From 799027d1ae84c094967a4a4e274985838c0e70cb Mon Sep 17 00:00:00 2001 From: Shawn Wallace Date: Fri, 13 Jun 2025 20:43:21 -0400 Subject: [PATCH] server: refactor to use ECS This should simplify how some of the code reads, as well as allowing for future feature additions to be accomplished in a less restrictive manner. The slotmap + Object enum pattern was kind of like a really bad ecs in a way anyway. Also I was looking for an excuse to use an ecs. --- Cargo.lock | 83 +- Cargo.toml | 4 +- macros/Cargo.toml | 1 + macros/src/lib.rs | 38 +- src/clientside/data_device.rs | 133 -- src/clientside/xdg_activation.rs | 42 - src/lib.rs | 1 - .../mod.rs => server/clientside.rs} | 331 +++- src/server/dispatch.rs | 894 +++++----- src/server/event.rs | 1573 +++++++++-------- src/server/mod.rs | 916 +++++----- 11 files changed, 2046 insertions(+), 1970 deletions(-) delete mode 100644 src/clientside/data_device.rs delete mode 100644 src/clientside/xdg_activation.rs rename src/{clientside/mod.rs => server/clientside.rs} (52%) diff --git a/Cargo.lock b/Cargo.lock index efaf692..2745353 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,18 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "aho-corasick" version = "1.1.3" @@ -294,6 +306,37 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", +] + +[[package]] +name = "hecs" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cbc675ee8d97b4d206a985137f8ad59666538f56f906474f554467a63c776d" +dependencies = [ + "hashbrown", + "hecs-macros", + "spin", +] + +[[package]] +name = "hecs-macros" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "052fc25b12dc326082605cd2098eb76050a72fa0c0e9ea7faaa3f58b565fc970" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "hermit-abi" version = "0.5.1" @@ -421,6 +464,7 @@ checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" name = "macros" version = "0.1.0" dependencies = [ + "proc-macro2", "quote", "syn", ] @@ -655,15 +699,6 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" -[[package]] -name = "slotmap" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a" -dependencies = [ - "version_check", -] - [[package]] name = "smallvec" version = "1.15.0" @@ -693,6 +728,12 @@ dependencies = [ "xkeysym", ] +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + [[package]] name = "strsim" version = "0.11.1" @@ -1161,21 +1202,39 @@ dependencies = [ "anyhow", "bitflags 2.9.1", "env_logger 0.11.8", - "libc", + "hecs", "log", "macros", "pretty_env_logger", "rustix", "sd-notify", - "slotmap", "smithay-client-toolkit", "testwl", "vergen-gitcl", "wayland-client", "wayland-protocols", - "wayland-scanner", "wayland-server", "wl_drm", "xcb", "xcb-util-cursor", ] + +[[package]] +name = "zerocopy" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml index 845a6b9..7276eb0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,20 +26,18 @@ bitflags = "2.5.0" rustix = { workspace = true, features = ["event"] } wayland-client.workspace = true wayland-protocols = { workspace = true, features = ["client", "server", "staging", "unstable"] } -wayland-scanner.workspace = true wayland-server.workspace = true xcb = { version = "1.3.0", features = ["composite", "randr", "res"] } wl_drm = { path = "wl_drm" } -libc = "0.2.153" log = "0.4.21" env_logger = "0.11.3" pretty_env_logger = "0.5.0" -slotmap = "1.0.7" xcb-util-cursor = "0.3.2" smithay-client-toolkit = { version = "0.19.1", default-features = false } sd-notify = { version = "0.4.2", optional = true } macros = { version = "0.1.0", path = "macros" } +hecs = { version = "0.10.5", features = ["macros"] } [features] default = [] diff --git a/macros/Cargo.toml b/macros/Cargo.toml index 8aae76e..799c657 100644 --- a/macros/Cargo.toml +++ b/macros/Cargo.toml @@ -10,5 +10,6 @@ workspace = true proc-macro = true [dependencies] +proc-macro2 = "1.0.95" quote = "1.0.37" syn = "2.0.79" diff --git a/macros/src/lib.rs b/macros/src/lib.rs index c30d94d..4444803 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -46,9 +46,32 @@ impl Parse for EventVariant { } } +enum ShuntObject { + Ident(syn::Ident), + KSelf(Token![self]), +} + +impl Parse for ShuntObject { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + if input.peek(Token![self]) { + Ok(Self::KSelf(input.parse()?)) + } else { + Ok(Self::Ident(input.parse()?)) + } + } +} + +impl quote::ToTokens for ShuntObject { + fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { + match self { + Self::Ident(i) => i.to_tokens(tokens), + Self::KSelf(s) => s.to_tokens(tokens), + } + } +} struct Input { object: syn::Expr, - event_object: syn::Ident, + event_object: ShuntObject, event_type: syn::Type, events: Punctuated, } @@ -58,8 +81,12 @@ impl Parse for Input { let object = input.parse()?; input.parse::()?; let event_object = input.parse()?; - input.parse::()?; - let event_type = input.parse()?; + let event_type = if matches!(event_object, ShuntObject::Ident(..)) { + input.parse::()?; + input.parse()? + } else { + parse_quote!(Self) + }; input.parse::]>()?; let events; bracketed!(events in input); @@ -119,14 +146,13 @@ pub fn simple_event_shunt(tokens: TokenStream) -> TokenStream { let fn_name = format_ident!("{keyword_pfx}{fn_name}"); quote! { - #name { #field_names } => { #object.#fn_name(#fn_args); } + #event_type::#name { #field_names } => { #object.#fn_name(#fn_args); } } }); quote! {{ - use #event_type::*; match #event_object { #(#match_arms)* - _ => log::warn!(concat!("unhandled ", stringify!(#event_type), ": {:?}"), #event_object) + _ => log::warn!("unhandled {}: {:?}", std::any::type_name::<#event_type>(), #event_object) } }} .into() diff --git a/src/clientside/data_device.rs b/src/clientside/data_device.rs deleted file mode 100644 index 208db5e..0000000 --- a/src/clientside/data_device.rs +++ /dev/null @@ -1,133 +0,0 @@ -use crate::clientside::Globals; -use smithay_client_toolkit::{ - data_device_manager::{ - data_device::DataDeviceHandler, data_offer::DataOfferHandler, - data_source::DataSourceHandler, - }, - delegate_data_device, -}; -delegate_data_device!(Globals); - -impl DataDeviceHandler for Globals { - fn selection( - &mut self, - _: &wayland_client::Connection, - _: &wayland_client::QueueHandle, - data_device: &wayland_client::protocol::wl_data_device::WlDataDevice, - ) { - self.selection = Some(data_device.clone()); - } - - fn drop_performed( - &mut self, - _: &wayland_client::Connection, - _: &wayland_client::QueueHandle, - _: &wayland_client::protocol::wl_data_device::WlDataDevice, - ) { - } - - fn motion( - &mut self, - _: &wayland_client::Connection, - _: &wayland_client::QueueHandle, - _: &wayland_client::protocol::wl_data_device::WlDataDevice, - _: f64, - _: f64, - ) { - } - - fn leave( - &mut self, - _: &wayland_client::Connection, - _: &wayland_client::QueueHandle, - _: &wayland_client::protocol::wl_data_device::WlDataDevice, - ) { - } - - fn enter( - &mut self, - _: &wayland_client::Connection, - _: &wayland_client::QueueHandle, - _: &wayland_client::protocol::wl_data_device::WlDataDevice, - _: f64, - _: f64, - _: &wayland_client::protocol::wl_surface::WlSurface, - ) { - } -} - -impl DataSourceHandler for Globals { - fn send_request( - &mut self, - _: &wayland_client::Connection, - _: &wayland_client::QueueHandle, - _: &wayland_client::protocol::wl_data_source::WlDataSource, - mime: String, - fd: smithay_client_toolkit::data_device_manager::WritePipe, - ) { - self.selection_requests.push((mime, fd)); - } - - fn cancelled( - &mut self, - _: &wayland_client::Connection, - _: &wayland_client::QueueHandle, - _: &wayland_client::protocol::wl_data_source::WlDataSource, - ) { - self.cancelled = true; - } - - fn action( - &mut self, - _: &wayland_client::Connection, - _: &wayland_client::QueueHandle, - _: &wayland_client::protocol::wl_data_source::WlDataSource, - _: wayland_client::protocol::wl_data_device_manager::DndAction, - ) { - } - - fn dnd_finished( - &mut self, - _: &wayland_client::Connection, - _: &wayland_client::QueueHandle, - _: &wayland_client::protocol::wl_data_source::WlDataSource, - ) { - } - - fn dnd_dropped( - &mut self, - _: &wayland_client::Connection, - _: &wayland_client::QueueHandle, - _: &wayland_client::protocol::wl_data_source::WlDataSource, - ) { - } - - fn accept_mime( - &mut self, - _: &wayland_client::Connection, - _: &wayland_client::QueueHandle, - _: &wayland_client::protocol::wl_data_source::WlDataSource, - _: Option, - ) { - } -} - -impl DataOfferHandler for Globals { - fn selected_action( - &mut self, - _: &wayland_client::Connection, - _: &wayland_client::QueueHandle, - _: &mut smithay_client_toolkit::data_device_manager::data_offer::DragOffer, - _: wayland_client::protocol::wl_data_device_manager::DndAction, - ) { - } - - fn source_actions( - &mut self, - _: &wayland_client::Connection, - _: &wayland_client::QueueHandle, - _: &mut smithay_client_toolkit::data_device_manager::data_offer::DragOffer, - _: wayland_client::protocol::wl_data_device_manager::DndAction, - ) { - } -} diff --git a/src/clientside/xdg_activation.rs b/src/clientside/xdg_activation.rs deleted file mode 100644 index afebe1e..0000000 --- a/src/clientside/xdg_activation.rs +++ /dev/null @@ -1,42 +0,0 @@ -use smithay_client_toolkit::{ - activation::{ActivationHandler, RequestData, RequestDataExt}, - delegate_activation, -}; -use xcb::x; - -use crate::clientside::Globals; - -delegate_activation!(Globals, ActivationData); - -pub struct ActivationData { - window: x::Window, - data: RequestData, -} - -impl ActivationData { - pub fn new(window: x::Window, data: RequestData) -> Self { - Self { window, data } - } -} - -impl RequestDataExt for ActivationData { - fn app_id(&self) -> Option<&str> { - self.data.app_id() - } - - fn seat_and_serial(&self) -> Option<(&wayland_client::protocol::wl_seat::WlSeat, u32)> { - self.data.seat_and_serial() - } - - fn surface(&self) -> Option<&wayland_client::protocol::wl_surface::WlSurface> { - self.data.surface() - } -} - -impl ActivationHandler for Globals { - type RequestData = ActivationData; - - fn new_token(&mut self, token: String, data: &Self::RequestData) { - self.pending_activations.push((data.window, token)); - } -} diff --git a/src/lib.rs b/src/lib.rs index 71b36bb..ab38c3a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,3 @@ -mod clientside; mod server; pub mod xstate; diff --git a/src/clientside/mod.rs b/src/server/clientside.rs similarity index 52% rename from src/clientside/mod.rs rename to src/server/clientside.rs index 3b9bf00..5a66203 100644 --- a/src/clientside/mod.rs +++ b/src/server/clientside.rs @@ -1,8 +1,13 @@ -mod data_device; -pub mod xdg_activation; - -use crate::server::{ObjectEvent, ObjectKey}; -use std::os::unix::net::UnixStream; +use super::ObjectEvent; +use hecs::{Entity, World}; +use smithay_client_toolkit::{ + activation::{ActivationHandler, RequestData, RequestDataExt}, + data_device_manager::{ + data_device::DataDeviceHandler, data_offer::DataOfferHandler, + data_source::DataSourceHandler, + }, + delegate_activation, delegate_data_device, +}; use std::sync::{mpsc, Mutex, OnceLock}; use wayland_client::protocol::{ wl_buffer::WlBuffer, wl_callback::WlCallback, wl_compositor::WlCompositor, @@ -12,16 +17,14 @@ use wayland_client::protocol::{ }; use wayland_client::{ delegate_noop, event_created_child, - globals::{registry_queue_init, Global, GlobalList, GlobalListContents}, - Connection, Dispatch, EventQueue, Proxy, QueueHandle, + globals::{Global, GlobalList, GlobalListContents}, + Connection, Dispatch, Proxy, QueueHandle, }; -use wayland_protocols::wp::relative_pointer::zv1::client::{ - zwp_relative_pointer_manager_v1::ZwpRelativePointerManagerV1, - zwp_relative_pointer_v1::ZwpRelativePointerV1, -}; -use wayland_protocols::xdg::decoration::zv1::client::zxdg_decoration_manager_v1::ZxdgDecorationManagerV1; -use wayland_protocols::xdg::decoration::zv1::client::zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1; use wayland_protocols::{ + wp::relative_pointer::zv1::client::{ + zwp_relative_pointer_manager_v1::ZwpRelativePointerManagerV1, + zwp_relative_pointer_v1::ZwpRelativePointerV1, + }, wp::{ fractional_scale::v1::client::{ wp_fractional_scale_manager_v1::WpFractionalScaleManagerV1, @@ -52,6 +55,8 @@ use wayland_protocols::{ }, viewporter::client::{wp_viewport::WpViewport, wp_viewporter::WpViewporter}, }, + xdg::decoration::zv1::client::zxdg_decoration_manager_v1::ZxdgDecorationManagerV1, + xdg::decoration::zv1::client::zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1, xdg::{ activation::v1::client::xdg_activation_v1::XdgActivationV1, shell::client::{ @@ -65,55 +70,56 @@ use wayland_protocols::{ }; use wayland_server::protocol as server; use wl_drm::client::wl_drm::WlDrm; +use xcb::x; -#[derive(Default)] -pub struct Globals { - events: Vec<(ObjectKey, ObjectEvent)>, - queued_events: Vec>, +pub(super) struct MyWorld { + pub world: World, + pub global_list: GlobalList, pub new_globals: Vec, + events: Vec<(Entity, ObjectEvent)>, + queued_events: Vec>, pub selection: Option, pub selection_requests: Vec<( String, smithay_client_toolkit::data_device_manager::WritePipe, )>, - pub cancelled: bool, + pub selection_cancelled: bool, pub pending_activations: Vec<(xcb::x::Window, String)>, } -pub type ClientQueueHandle = QueueHandle; - -pub struct ClientState { - _connection: Connection, - pub queue: EventQueue, - pub qh: ClientQueueHandle, - pub globals: Globals, - pub global_list: GlobalList, -} - -impl ClientState { - pub fn new(server_connection: Option) -> Self { - let connection = if let Some(stream) = server_connection { - Connection::from_socket(stream) - } else { - Connection::connect_to_env() - } - .unwrap(); - let (global_list, queue) = registry_queue_init::(&connection).unwrap(); - let globals = Globals::default(); - let qh = queue.handle(); - +impl MyWorld { + pub fn new(global_list: GlobalList) -> Self { Self { - _connection: connection, - queue, - qh, - globals, + world: World::new(), global_list, + new_globals: Vec::new(), + events: Vec::new(), + queued_events: Vec::new(), + selection: None, + selection_requests: Vec::new(), + selection_cancelled: false, + pending_activations: Vec::new(), } } +} - pub fn read_events(&mut self) -> Vec<(ObjectKey, ObjectEvent)> { - let mut events = std::mem::take(&mut self.globals.events); - self.globals.queued_events.retain(|rx| { +impl std::ops::Deref for MyWorld { + type Target = World; + fn deref(&self) -> &Self::Target { + &self.world + } +} + +impl std::ops::DerefMut for MyWorld { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.world + } +} + +impl MyWorld { + pub(crate) fn read_events(&mut self) -> Vec<(Entity, ObjectEvent)> { + let mut events = std::mem::take(&mut self.events); + self.queued_events.retain(|rx| { match rx.try_recv() { Ok(event) => { events.push(event); @@ -132,25 +138,25 @@ impl ClientState { pub type Event = ::Event; -delegate_noop!(Globals: WlCompositor); -delegate_noop!(Globals: WlRegion); -delegate_noop!(Globals: ignore WlShm); -delegate_noop!(Globals: ignore ZwpLinuxDmabufV1); -delegate_noop!(Globals: ZwpRelativePointerManagerV1); -delegate_noop!(Globals: ignore dmabuf::zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1); -delegate_noop!(Globals: XdgPositioner); -delegate_noop!(Globals: WlShmPool); -delegate_noop!(Globals: WpViewporter); -delegate_noop!(Globals: WpViewport); -delegate_noop!(Globals: ZxdgOutputManagerV1); -delegate_noop!(Globals: ZwpPointerConstraintsV1); -delegate_noop!(Globals: ZwpTabletManagerV2); -delegate_noop!(Globals: XdgActivationV1); -delegate_noop!(Globals: ZxdgDecorationManagerV1); -delegate_noop!(Globals: WpFractionalScaleManagerV1); -delegate_noop!(Globals: ignore ZxdgToplevelDecorationV1); +delegate_noop!(MyWorld: WlCompositor); +delegate_noop!(MyWorld: WlRegion); +delegate_noop!(MyWorld: ignore WlShm); +delegate_noop!(MyWorld: ignore ZwpLinuxDmabufV1); +delegate_noop!(MyWorld: ZwpRelativePointerManagerV1); +delegate_noop!(MyWorld: ignore dmabuf::zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1); +delegate_noop!(MyWorld: XdgPositioner); +delegate_noop!(MyWorld: WlShmPool); +delegate_noop!(MyWorld: WpViewporter); +delegate_noop!(MyWorld: WpViewport); +delegate_noop!(MyWorld: ZxdgOutputManagerV1); +delegate_noop!(MyWorld: ZwpPointerConstraintsV1); +delegate_noop!(MyWorld: ZwpTabletManagerV2); +delegate_noop!(MyWorld: XdgActivationV1); +delegate_noop!(MyWorld: ZxdgDecorationManagerV1); +delegate_noop!(MyWorld: WpFractionalScaleManagerV1); +delegate_noop!(MyWorld: ignore ZxdgToplevelDecorationV1); -impl Dispatch for Globals { +impl Dispatch for MyWorld { fn event( state: &mut Self, _: &WlRegistry, @@ -174,7 +180,7 @@ impl Dispatch for Globals { } } -impl Dispatch for Globals { +impl Dispatch for MyWorld { fn event( _: &mut Self, base: &XdgWmBase, @@ -189,7 +195,7 @@ impl Dispatch for Globals { } } -impl Dispatch for Globals { +impl Dispatch for MyWorld { fn event( _: &mut Self, _: &WlCallback, @@ -206,12 +212,12 @@ impl Dispatch for Globals { macro_rules! push_events { ($type:ident) => { - impl Dispatch<$type, ObjectKey> for Globals { + impl Dispatch<$type, Entity> for MyWorld { fn event( state: &mut Self, _: &$type, event: <$type as Proxy>::Event, - key: &ObjectKey, + key: &Entity, _: &Connection, _: &QueueHandle, ) { @@ -240,16 +246,16 @@ push_events!(ZwpLockedPointerV1); push_events!(WpFractionalScaleV1); pub(crate) struct LateInitObjectKey { - key: OnceLock, + key: OnceLock, queued_events: Mutex>, - sender: Mutex>>, + sender: Mutex>>, } impl LateInitObjectKey

where P::Event: Into, { - pub fn init(&self, key: ObjectKey) { + pub fn init(&self, key: Entity) { self.key.set(key).expect("Object key should not be set"); if let Some(sender) = self.sender.lock().unwrap().take() { for event in self.queued_events.lock().unwrap().drain(..) { @@ -266,7 +272,7 @@ where } } - fn push_or_queue_event(&self, state: &mut Globals, event: P::Event) { + fn push_or_queue_event(&self, state: &mut MyWorld, event: P::Event) { if let Some(key) = self.key.get().copied() { state.events.push((key, event.into())); } else { @@ -282,7 +288,7 @@ where } impl std::ops::Deref for LateInitObjectKey

{ - type Target = ObjectKey; + type Target = Entity; #[track_caller] fn deref(&self) -> &Self::Target { @@ -290,19 +296,19 @@ impl std::ops::Deref for LateInitObjectKey

{ } } -impl Dispatch for Globals { +impl Dispatch for MyWorld { fn event( state: &mut Self, _: &ZwpTabletSeatV2, event: ::Event, - key: &ObjectKey, + key: &Entity, _: &Connection, _: &QueueHandle, ) { state.events.push((*key, event.into())); } - event_created_child!(Globals, ZwpTabletSeatV2, [ + event_created_child!(MyWorld, ZwpTabletSeatV2, [ EVT_TABLET_ADDED_OPCODE => (ZwpTabletV2, LateInitObjectKey::new()), EVT_PAD_ADDED_OPCODE => (ZwpTabletPadV2, LateInitObjectKey::new()), EVT_TOOL_ADDED_OPCODE => (ZwpTabletToolV2, LateInitObjectKey::new()) @@ -311,7 +317,7 @@ impl Dispatch for Globals { macro_rules! push_or_queue_events { ($type:ty) => { - impl Dispatch<$type, LateInitObjectKey<$type>> for Globals { + impl Dispatch<$type, LateInitObjectKey<$type>> for MyWorld { fn event( state: &mut Self, _: &$type, @@ -331,7 +337,7 @@ push_or_queue_events!(ZwpTabletToolV2); push_or_queue_events!(ZwpTabletPadRingV2); push_or_queue_events!(ZwpTabletPadStripV2); -impl Dispatch> for Globals { +impl Dispatch> for MyWorld { fn event( state: &mut Self, _: &ZwpTabletPadV2, @@ -343,12 +349,12 @@ impl Dispatch> for Globals { key.push_or_queue_event(state, event); } - event_created_child!(Globals, ZwpTabletPadV2, [ + event_created_child!(MyWorld, ZwpTabletPadV2, [ EVT_GROUP_OPCODE => (ZwpTabletPadGroupV2, LateInitObjectKey::new()) ]); } -impl Dispatch> for Globals { +impl Dispatch> for MyWorld { fn event( state: &mut Self, _: &ZwpTabletPadGroupV2, @@ -360,8 +366,169 @@ impl Dispatch> for G key.push_or_queue_event(state, event); } - event_created_child!(Globals, ZwpTabletPadGroupV2, [ + event_created_child!(MyWorld, ZwpTabletPadGroupV2, [ EVT_RING_OPCODE => (ZwpTabletPadRingV2, LateInitObjectKey::new()), EVT_STRIP_OPCODE => (ZwpTabletPadStripV2, LateInitObjectKey::new()) ]); } + +delegate_data_device!(MyWorld); + +impl DataDeviceHandler for MyWorld { + fn selection( + &mut self, + _: &wayland_client::Connection, + _: &wayland_client::QueueHandle, + data_device: &wayland_client::protocol::wl_data_device::WlDataDevice, + ) { + self.selection = Some(data_device.clone()); + } + + fn drop_performed( + &mut self, + _: &wayland_client::Connection, + _: &wayland_client::QueueHandle, + _: &wayland_client::protocol::wl_data_device::WlDataDevice, + ) { + } + + fn motion( + &mut self, + _: &wayland_client::Connection, + _: &wayland_client::QueueHandle, + _: &wayland_client::protocol::wl_data_device::WlDataDevice, + _: f64, + _: f64, + ) { + } + + fn leave( + &mut self, + _: &wayland_client::Connection, + _: &wayland_client::QueueHandle, + _: &wayland_client::protocol::wl_data_device::WlDataDevice, + ) { + } + + fn enter( + &mut self, + _: &wayland_client::Connection, + _: &wayland_client::QueueHandle, + _: &wayland_client::protocol::wl_data_device::WlDataDevice, + _: f64, + _: f64, + _: &wayland_client::protocol::wl_surface::WlSurface, + ) { + } +} + +impl DataSourceHandler for MyWorld { + fn send_request( + &mut self, + _: &wayland_client::Connection, + _: &wayland_client::QueueHandle, + _: &wayland_client::protocol::wl_data_source::WlDataSource, + mime: String, + fd: smithay_client_toolkit::data_device_manager::WritePipe, + ) { + self.selection_requests.push((mime, fd)); + } + + fn cancelled( + &mut self, + _: &wayland_client::Connection, + _: &wayland_client::QueueHandle, + _: &wayland_client::protocol::wl_data_source::WlDataSource, + ) { + self.selection_cancelled = true; + } + + fn action( + &mut self, + _: &wayland_client::Connection, + _: &wayland_client::QueueHandle, + _: &wayland_client::protocol::wl_data_source::WlDataSource, + _: wayland_client::protocol::wl_data_device_manager::DndAction, + ) { + } + + fn dnd_finished( + &mut self, + _: &wayland_client::Connection, + _: &wayland_client::QueueHandle, + _: &wayland_client::protocol::wl_data_source::WlDataSource, + ) { + } + + fn dnd_dropped( + &mut self, + _: &wayland_client::Connection, + _: &wayland_client::QueueHandle, + _: &wayland_client::protocol::wl_data_source::WlDataSource, + ) { + } + + fn accept_mime( + &mut self, + _: &wayland_client::Connection, + _: &wayland_client::QueueHandle, + _: &wayland_client::protocol::wl_data_source::WlDataSource, + _: Option, + ) { + } +} + +impl DataOfferHandler for MyWorld { + fn selected_action( + &mut self, + _: &wayland_client::Connection, + _: &wayland_client::QueueHandle, + _: &mut smithay_client_toolkit::data_device_manager::data_offer::DragOffer, + _: wayland_client::protocol::wl_data_device_manager::DndAction, + ) { + } + + fn source_actions( + &mut self, + _: &wayland_client::Connection, + _: &wayland_client::QueueHandle, + _: &mut smithay_client_toolkit::data_device_manager::data_offer::DragOffer, + _: wayland_client::protocol::wl_data_device_manager::DndAction, + ) { + } +} + +delegate_activation!(MyWorld, ActivationData); + +pub struct ActivationData { + window: x::Window, + data: RequestData, +} + +impl ActivationData { + pub fn new(window: x::Window, data: RequestData) -> Self { + Self { window, data } + } +} + +impl RequestDataExt for ActivationData { + fn app_id(&self) -> Option<&str> { + self.data.app_id() + } + + fn seat_and_serial(&self) -> Option<(&wayland_client::protocol::wl_seat::WlSeat, u32)> { + self.data.seat_and_serial() + } + + fn surface(&self) -> Option<&wayland_client::protocol::wl_surface::WlSurface> { + self.data.surface() + } +} + +impl ActivationHandler for MyWorld { + type RequestData = ActivationData; + + fn new_token(&mut self, token: String, data: &Self::RequestData) { + self.pending_activations.push((data.window, token)); + } +} diff --git a/src/server/dispatch.rs b/src/server/dispatch.rs index 8431cd0..5171020 100644 --- a/src/server/dispatch.rs +++ b/src/server/dispatch.rs @@ -1,12 +1,16 @@ use super::*; +use hecs::CommandBuffer; use log::{debug, error, trace, warn}; use macros::simple_event_shunt; use std::sync::{Arc, OnceLock}; use wayland_client::globals::Global; use wayland_protocols::{ wp::{ + fractional_scale::v1::client::wp_fractional_scale_v1::WpFractionalScaleV1, linux_dmabuf::zv1::{client as c_dmabuf, server as s_dmabuf}, pointer_constraints::zv1::{ + client::zwp_confined_pointer_v1::ZwpConfinedPointerV1 as ConfinedPointerClient, + client::zwp_locked_pointer_v1::ZwpLockedPointerV1 as LockedPointerClient, client::zwp_pointer_constraints_v1::ZwpPointerConstraintsV1 as PointerConstraintsClient, server::{ zwp_confined_pointer_v1::{ @@ -20,12 +24,16 @@ use wayland_protocols::{ }, relative_pointer::zv1::{ client::zwp_relative_pointer_manager_v1::ZwpRelativePointerManagerV1 as RelativePointerManClient, + client::zwp_relative_pointer_v1::ZwpRelativePointerV1 as RelativePointerClient, server::zwp_relative_pointer_manager_v1::ZwpRelativePointerManagerV1 as RelativePointerManServer, + server::zwp_relative_pointer_v1::ZwpRelativePointerV1 as RelativePointerServer, }, tablet::zv2::{client as c_tablet, server as s_tablet}, + viewporter::client::wp_viewport::WpViewport, }, xdg::xdg_output::zv1::{ client::zxdg_output_manager_v1::ZxdgOutputManagerV1 as OutputManClient, + client::zxdg_output_v1::ZxdgOutputV1 as XdgOutputClient, server::{ zxdg_output_manager_v1::{ self as s_output_man, ZxdgOutputManagerV1 as OutputManServer, @@ -56,40 +64,6 @@ use wayland_server::{ Dispatch, DisplayHandle, GlobalDispatch, Resource, }; -macro_rules! only_destroy_request_impl { - ($object_type:ty) => { - impl Dispatch<<$object_type as GenericObjectExt>::Server, ObjectKey> - for ServerState - { - fn request( - state: &mut Self, - _: &Client, - _: &<$object_type as GenericObjectExt>::Server, - request: <<$object_type as GenericObjectExt>::Server as Resource>::Request, - key: &ObjectKey, - _: &DisplayHandle, - _: &mut wayland_server::DataInit<'_, Self>, - ) { - if !matches!( - request, - <<$object_type as GenericObjectExt>::Server as Resource>::Request::Destroy - ) { - warn!( - "unrecognized {} request: {:?}", - stringify!($object_type), - request - ); - return; - } - - let obj: &$object_type = state.objects[*key].as_ref(); - obj.client.destroy(); - state.objects.remove(*key); - } - } - }; -} - // noop impl Dispatch for ServerState { fn request( @@ -105,37 +79,42 @@ impl Dispatch for ServerState { } } -impl Dispatch for ServerState { +impl Dispatch for ServerState { fn request( state: &mut Self, _: &wayland_server::Client, _: &WlSurface, request: ::Request, - key: &ObjectKey, + entity: &Entity, _: &DisplayHandle, data_init: &mut wayland_server::DataInit<'_, Self>, ) { - let surface: &SurfaceData = state.objects[*key].as_ref(); - let configured = - surface.role.is_none() || surface.xdg().is_none() || surface.xdg().unwrap().configured; + let data = state.world.entity(*entity).unwrap(); + let mut role = data.get::<&mut SurfaceRole>(); + let xdg = role.as_ref().and_then(|role| role.xdg()); + let configured = xdg.is_none_or(|xdg| xdg.configured); + let client = data.get::<&client::wl_surface::WlSurface>().unwrap(); + + let mut cmd = CommandBuffer::new(); match request { Request::::Attach { buffer, x, y } => { if buffer.is_none() { - trace!("xwayland attached null buffer to {:?}", surface.client); + trace!("xwayland attached null buffer to {client:?}"); } let buffer = buffer.as_ref().map(|b| { - let key: &ObjectKey = b.data().unwrap(); - let data: &Buffer = state.objects[*key].as_ref(); - &data.client + let entity: Entity = b.data().copied().unwrap(); + state + .world + .get::<&client::wl_buffer::WlBuffer>(entity) + .unwrap() }); if configured { - surface.client.attach(buffer, x, y); + client.attach(buffer.as_deref(), x, y); } else { - let buffer = buffer.cloned(); - let surface: &mut SurfaceData = state.objects[*key].as_mut(); - surface.attach = Some(SurfaceAttach { buffer, x, y }); + let buffer = buffer.as_deref().cloned(); + cmd.insert(*entity, (SurfaceAttach { buffer, x, y },)); } } Request::::DamageBuffer { @@ -145,49 +124,61 @@ impl Dispatch for ServerState { height, } => { if configured { - surface.client.damage_buffer(x, y, width, height); + client.damage_buffer(x, y, width, height); } } Request::::Frame { callback } => { let cb = data_init.init(callback, ()); if configured { - surface.client.frame(&state.qh, cb); + client.frame(&state.qh, cb); } else { - let surface: &mut SurfaceData = state.objects[*key].as_mut(); - surface.frame_callback = Some(cb); + cmd.insert(*entity, (cb,)); } } Request::::Commit => { if configured { - surface.client.commit(); + client.commit(); } } Request::::Destroy => { - let mut object = state.objects.remove(*key).unwrap(); - let surface: &mut SurfaceData = object.as_mut(); - if let Some(window_data) = surface.window.and_then(|w| state.windows.get_mut(&w)) { - window_data.surface_key.take(); + if !data.has::() { + cmd.despawn(*entity); } - surface.destroy_role(); - surface.client.destroy(); - surface.viewport.destroy(); - if let Some(f) = &mut surface.fractional { - f.destroy(); + + if let Some(role) = role.as_mut() { + role.destroy(); } + client.destroy(); + debug!( - "deleting key: {key:?} (surface {:?})", - surface.server.id().protocol_id() + "deleting (surface {:?})", + data.get::<&WlSurface>().unwrap().id().protocol_id() ); + + let mut query = data.query::<(&WpViewport, Option<&WpFractionalScaleV1>)>(); + let (viewport, fractional) = query.get().unwrap(); + viewport.destroy(); + + cmd.remove::(*entity); + if let Some(f) = fractional { + f.destroy(); + cmd.remove_one::(*entity); + } } Request::::SetBufferScale { scale } => { - surface.client.set_buffer_scale(scale); + client.set_buffer_scale(scale); } Request::::SetInputRegion { region } => { let region = region.as_ref().map(|r| r.data().unwrap()); - surface.client.set_input_region(region); + client.set_input_region(region); } other => warn!("unhandled surface request: {other:?}"), } + + drop(client); + drop(role); + + cmd.run_on(&mut state.world); } } @@ -226,36 +217,28 @@ impl ) { match request { Request::::CreateSurface { id } => { - let mut surface_id = None; + let entity = state.world.reserve_entity(); + let client = client.create_surface(&state.qh, entity); + let server = data_init.init(id, entity); + debug!("new surface ({})", server.id()); + let viewport = state.viewporter.get_viewport(&client, &state.qh, ()); + let fractional = state + .fractional_scale + .as_ref() + .map(|f| f.get_fractional_scale(&client, &state.qh, entity)); - state.objects.insert_with_key(|key| { - let client = client.create_surface(&state.qh, key); - let server = data_init.init(id, key); - let viewport = state.viewporter.get_viewport(&client, &state.qh, ()); - surface_id = Some(server.id().protocol_id()); - debug!("new surface with key {key:?} ({surface_id:?})"); - let fractional = state - .fractional_scale - .as_ref() - .map(|f| f.get_fractional_scale(&client, &state.qh, key)); - - SurfaceData { + state.world.spawn_at( + entity, + event::SurfaceBundle { client, server, - key, - serial: Default::default(), - attach: None, - frame_callback: None, - role: None, - xwl: None, - window: None, - output_key: None, - scale_factor: 1.0, viewport, - fractional, - } - .into() - }); + scale: SurfaceScaleFactor(1.0), + }, + ); + if let Some(f) = fractional { + state.world.insert(entity, (f,)).unwrap(); + } } Request::::CreateRegion { id } => { let c_region = client.create_region(&state.qh, ()); @@ -268,21 +251,24 @@ impl } } -impl Dispatch for ServerState { +impl Dispatch for ServerState { fn request( state: &mut Self, _: &wayland_server::Client, _: &WlBuffer, request: ::Request, - key: &ObjectKey, + entity: &Entity, _: &DisplayHandle, _: &mut wayland_server::DataInit<'_, Self>, ) { assert!(matches!(request, Request::::Destroy)); - let buf: &Buffer = state.objects[*key].as_ref(); - buf.client.destroy(); - state.objects.remove(*key); + state + .world + .get::<&client::wl_buffer::WlBuffer>(*entity) + .unwrap() + .destroy(); + state.world.despawn(*entity).unwrap(); } } @@ -305,26 +291,25 @@ impl Dispatch for Ser stride, format, } => { - state.objects.insert_with_key(|key| { - let client = c_pool.create_buffer( - offset, - width, - height, - stride, - convert_wenum(format), - &state.qh, - key, - ); - let server = data_init.init(id, key); - Buffer { server, client }.into() - }); + let entity = state.world.reserve_entity(); + let client = c_pool.create_buffer( + offset, + width, + height, + stride, + convert_wenum(format), + &state.qh, + entity, + ); + let server = data_init.init(id, entity); + state.world.spawn_at(entity, (client, server)); } Request::::Resize { size } => { c_pool.resize(size); } Request::::Destroy => { c_pool.destroy(); - state.clientside.queue.flush().unwrap(); + state.queue.flush().unwrap(); } other => warn!("unhandled shmpool request: {other:?}"), } @@ -355,19 +340,20 @@ impl Dispatch> } } -impl Dispatch for ServerState { +impl Dispatch for ServerState { fn request( state: &mut Self, _: &wayland_server::Client, _: &WlPointer, request: ::Request, - key: &ObjectKey, + entity: &Entity, _: &DisplayHandle, _: &mut wayland_server::DataInit<'_, Self>, ) { - let Pointer { - client: c_pointer, .. - }: &Pointer = state.objects[*key].as_ref(); + let c_pointer = state + .world + .get::<&client::wl_pointer::WlPointer>(*entity) + .unwrap(); match request { Request::::SetCursor { @@ -376,115 +362,155 @@ impl Dispatch for ServerState { hotspot_y, surface, } => { - let c_surface = surface.and_then(|s| state.get_client_surface_from_server(s)); - c_pointer.set_cursor(serial, c_surface, hotspot_x, hotspot_y); + let c_surface = surface.and_then(|s| { + let e = s.data().copied()?; + Some( + state + .world + .get::<&client::wl_surface::WlSurface>(e) + .unwrap(), + ) + }); + c_pointer.set_cursor(serial, c_surface.as_deref(), hotspot_x, hotspot_y); } Request::::Release => { c_pointer.release(); - state.objects.remove(*key); + drop(c_pointer); + let _ = state.world.despawn(*entity); } _ => warn!("unhandled cursor request: {request:?}"), } } } -impl Dispatch for ServerState { +impl Dispatch for ServerState { fn request( state: &mut Self, _: &wayland_server::Client, _: &WlKeyboard, request: ::Request, - key: &ObjectKey, + entity: &Entity, _: &DisplayHandle, _: &mut wayland_server::DataInit<'_, Self>, ) { match request { Request::::Release => { - let Keyboard { client, .. }: &_ = state.objects[*key].as_ref(); - client.release(); - state.objects.remove(*key); + state + .world + .get::<&client::wl_keyboard::WlKeyboard>(*entity) + .unwrap() + .release(); + state.world.despawn(*entity).unwrap(); } _ => unreachable!(), } } } -impl Dispatch for ServerState { +impl Dispatch for ServerState { fn request( state: &mut Self, _: &wayland_server::Client, _: &WlTouch, request: ::Request, - key: &ObjectKey, + entity: &Entity, _: &DisplayHandle, _: &mut wayland_server::DataInit<'_, Self>, ) { match request { Request::::Release => { - let Touch { client, .. }: &_ = state.objects[*key].as_ref(); - client.release(); - state.objects.remove(*key); + state + .world + .get::<&client::wl_touch::WlTouch>(*entity) + .unwrap() + .release(); + state.world.despawn(*entity).unwrap(); } _ => unreachable!(), } } } -impl Dispatch for ServerState { +impl Dispatch for ServerState { fn request( state: &mut Self, _: &wayland_server::Client, _: &WlSeat, request: ::Request, - key: &ObjectKey, + entity: &Entity, _: &DisplayHandle, data_init: &mut wayland_server::DataInit<'_, Self>, ) { match request { Request::::GetPointer { id } => { - state - .objects - .insert_from_other_objects([*key], |[seat_obj], key| { - let Seat { client, .. }: &Seat = seat_obj.try_into().unwrap(); - let client = client.get_pointer(&state.qh, key); - let server = data_init.init(id, key); - trace!("new pointer: {server:?}"); - Pointer::new(server, client).into() - }); + let new_entity = state.world.reserve_entity(); + let client = { + state + .world + .get::<&client::wl_seat::WlSeat>(*entity) + .unwrap() + .get_pointer(&state.qh, new_entity) + }; + let server = data_init.init(id, new_entity); + state.world.spawn_at(new_entity, (client, server)); } Request::::GetKeyboard { id } => { - state - .objects - .insert_from_other_objects([*key], |[seat_obj], key| { - let Seat { - client: client_seat, - .. - }: &Seat = seat_obj.try_into().unwrap(); - let client = client_seat.get_keyboard(&state.qh, key); - let server = data_init.init(id, key); - Keyboard { - client, - server, - seat: client_seat.clone(), - } - .into() - }); + let client = { + state + .world + .get::<&client::wl_seat::WlSeat>(*entity) + .unwrap() + .get_keyboard(&state.qh, *entity) + }; + let server = data_init.init(id, *entity); + state.world.insert(*entity, (client, server)).unwrap(); } Request::::GetTouch { id } => { - state - .objects - .insert_from_other_objects([*key], |[seat_obj], key| { - let Seat { client, .. }: &Seat = seat_obj.try_into().unwrap(); - let client = client.get_touch(&state.qh, key); - let server = data_init.init(id, key); - Touch { client, server }.into() - }); + let new_entity = state.world.reserve_entity(); + let client = { + state + .world + .get::<&client::wl_seat::WlSeat>(*entity) + .unwrap() + .get_touch(&state.qh, new_entity) + }; + let server = data_init.init(id, new_entity); + state.world.spawn_at(new_entity, (client, server)); } other => warn!("unhandled seat request: {other:?}"), } } } -only_destroy_request_impl!(RelativePointer); + +macro_rules! only_destroy_request_impl { + ($server:ty, $client:ty) => { + impl Dispatch<$server, Entity> for ServerState { + fn request( + state: &mut Self, + _: &Client, + _: &$server, + request: <$server as Resource>::Request, + entity: &Entity, + _: &DisplayHandle, + _: &mut wayland_server::DataInit<'_, Self>, + ) { + if !matches!(request, <$server as Resource>::Request::Destroy) { + warn!( + "unrecognized {} request: {:?}", + stringify!($server), + request + ); + return; + } + + state.world.get::<&$client>(*entity).unwrap().destroy(); + state.world.despawn(*entity).unwrap(); + } + } + }; +} + +only_destroy_request_impl!(RelativePointerServer, RelativePointerClient); impl Dispatch> @@ -501,35 +527,41 @@ impl ) { match request { Request::::GetRelativePointer { id, pointer } => { - let p_key: ObjectKey = pointer.data().copied().unwrap(); - state - .objects - .insert_from_other_objects([p_key], |[pointer_obj], key| { - let pointer: &Pointer = pointer_obj.try_into().unwrap(); - let client = client.get_relative_pointer(&pointer.client, &state.qh, key); - let server = data_init.init(id, key); - RelativePointer { client, server }.into() - }); + let pointer_entity: Entity = pointer.data().copied().unwrap(); + let entity = state.world.reserve_entity(); + let client = { + let client_pointer = state + .world + .get::<&client::wl_pointer::WlPointer>(pointer_entity) + .unwrap(); + + client.get_relative_pointer(&client_pointer, &state.qh, entity) + }; + let server = data_init.init(id, entity); + state.world.spawn_at(entity, (server, client)); } _ => warn!("unhandled relative pointer request: {request:?}"), } } } -impl Dispatch for ServerState { +impl Dispatch for ServerState { fn request( state: &mut Self, _: &wayland_server::Client, _: &WlOutput, request: ::Request, - key: &ObjectKey, + entity: &Entity, _: &DisplayHandle, _: &mut wayland_server::DataInit<'_, Self>, ) { match request { wayland_server::protocol::wl_output::Request::Release => { - let Output { client, .. }: &_ = state.objects[*key].as_ref(); - client.release(); + state + .world + .get::<&client::wl_output::WlOutput>(*entity) + .unwrap() + .release(); todo!("handle wloutput destruction"); } _ => warn!("unhandled output request {request:?}"), @@ -538,7 +570,7 @@ impl Dispatch for ServerState { } impl - Dispatch + Dispatch for ServerState { fn request( @@ -546,16 +578,21 @@ impl _: &wayland_server::Client, _: &s_dmabuf::zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1, request: ::Request, - key: &ObjectKey, + entity: &Entity, _: &DisplayHandle, _: &mut wayland_server::DataInit<'_, Self>, ) { use s_dmabuf::zwp_linux_dmabuf_feedback_v1::Request::*; match request { Destroy => { - let dmabuf: &DmabufFeedback = state.objects[*key].as_ref(); - dmabuf.client.destroy(); - state.objects.remove(*key); + state + .world + .get::<&c_dmabuf::zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1>( + *entity, + ) + .unwrap() + .destroy(); + state.world.despawn(*entity).unwrap(); } _ => unreachable!(), } @@ -588,18 +625,17 @@ impl format, flags, } => { - state.objects.insert_with_key(|key| { - let client = c_params.create_immed( - width, - height, - format, - convert_wenum(flags), - &state.qh, - key, - ); - let server = data_init.init(buffer_id, key); - Buffer { server, client }.into() - }); + let entity = state.world.reserve_entity(); + let client = c_params.create_immed( + width, + height, + format, + convert_wenum(flags), + &state.qh, + entity, + ); + let server = data_init.init(buffer_id, entity); + state.world.spawn_at(entity, (client, server)); } Add { fd, @@ -651,37 +687,37 @@ impl data_init.init(params_id, c_params); } GetDefaultFeedback { id } => { - state.objects.insert_with_key(|key| { - let client = client.get_default_feedback(&state.qh, key); - let server = data_init.init(id, key); - DmabufFeedback { client, server }.into() - }); + let entity = state.world.reserve_entity(); + let client = client.get_default_feedback(&state.qh, entity); + let server = data_init.init(id, entity); + state.world.spawn_at(entity, (client, server)); } GetSurfaceFeedback { id, surface } => { - let surf_key: ObjectKey = surface.data().copied().unwrap(); - state - .objects - .insert_from_other_objects([surf_key], |[surface_obj], key| { - let SurfaceData { - client: c_surface, .. - }: &SurfaceData = surface_obj.try_into().unwrap(); - let client = client.get_surface_feedback(c_surface, &state.qh, key); - let server = data_init.init(id, key); - DmabufFeedback { client, server }.into() - }); + let entity = state.world.reserve_entity(); + let surface_entity: Entity = surface.data().copied().unwrap(); + let client = { + let c_surface = state + .world + .get::<&client::wl_surface::WlSurface>(surface_entity) + .unwrap(); + client.get_surface_feedback(&c_surface, &state.qh, entity) + }; + let server = data_init.init(id, entity); + + state.world.spawn_at(entity, (client, server)); } _ => warn!("unhandled dmabuf request: {request:?}"), } } } -impl Dispatch for ServerState { +impl Dispatch for ServerState { fn request( state: &mut Self, _: &wayland_server::Client, _: &WlDrmServer, request: ::Request, - key: &ObjectKey, + entity: &Entity, _: &DisplayHandle, data_init: &mut wayland_server::DataInit<'_, Self>, ) { @@ -689,8 +725,8 @@ impl Dispatch for ServerState { type DrmFn = dyn FnOnce( &wl_drm::client::wl_drm::WlDrm, - ObjectKey, - &ClientQueueHandle, + Entity, + &QueueHandle, ) -> client::wl_buffer::WlBuffer; let mut bufs: Option<(Box, wayland_server::New)> = None; @@ -767,32 +803,37 @@ impl Dispatch for ServerState { )); } Authenticate { id } => { - let drm: &Drm = state.objects[*key].as_ref(); - drm.client.authenticate(id); + state + .world + .get::<&wl_drm::client::wl_drm::WlDrm>(*entity) + .unwrap() + .authenticate(id); } _ => unreachable!(), } if let Some((buf_create, id)) = bufs { - state - .objects - .insert_from_other_objects([*key], |[drm_obj], key| { - let drm: &Drm = drm_obj.try_into().unwrap(); - let client = buf_create(&drm.client, key, &state.qh); - let server = data_init.init(id, key); - Buffer { client, server }.into() - }); + let new_entity = state.world.reserve_entity(); + let client = { + let drm_client = state + .world + .get::<&wl_drm::client::wl_drm::WlDrm>(*entity) + .unwrap(); + buf_create(&drm_client, new_entity, &state.qh) + }; + let server = data_init.init(id, new_entity); + state.world.spawn_at(new_entity, (client, server)); } } } -impl Dispatch for ServerState { +impl Dispatch for ServerState { fn request( state: &mut Self, _: &wayland_server::Client, _: &XdgOutputServer, request: ::Request, - key: &ObjectKey, + entity: &Entity, _: &DisplayHandle, _: &mut wayland_server::DataInit<'_, Self>, ) { @@ -800,9 +841,11 @@ impl Dispatch for ServerState { unreachable!(); }; - let output: &mut Output = state.objects[*key].as_mut(); - let xdg_output = output.xdg.take().unwrap(); - xdg_output.client.destroy(); + let (client, _) = state + .world + .query_one_mut::<(&XdgOutputClient, &XdgOutputServer)>(*entity) + .unwrap(); + client.destroy(); } } @@ -820,11 +863,16 @@ impl Dispatch { - let output_key: ObjectKey = output.data().copied().unwrap(); - let output: &mut Output = state.objects[output_key].as_mut(); - let client = client.get_xdg_output(&output.client, &state.qh, output_key); - let server = data_init.init(id, output_key); - output.xdg = Some(XdgOutput { client, server }); + let entity: Entity = output.data().copied().unwrap(); + let client = { + let c_output = state + .world + .get::<&client::wl_output::WlOutput>(entity) + .unwrap(); + client.get_xdg_output(&c_output, &state.qh, entity) + }; + let server = data_init.init(id, entity); + state.world.insert(entity, (client, server)).unwrap(); } s_output_man::Request::Destroy => {} _ => unreachable!(), @@ -832,19 +880,19 @@ impl Dispatch Dispatch for ServerState { +impl Dispatch for ServerState { fn request( state: &mut Self, _: &wayland_server::Client, _: &ConfinedPointerServer, request: ::Request, - key: &ObjectKey, + entity: &Entity, _: &DisplayHandle, _: &mut wayland_server::DataInit<'_, Self>, ) { - let confined_ptr: &ConfinedPointer = state.objects[*key].as_ref(); + let client = state.world.get::<&ConfinedPointerClient>(*entity).unwrap(); simple_event_shunt! { - confined_ptr.client, request: cp::Request => [ + client, request: cp::Request => [ SetRegion { |region| region.as_ref().map(|r| r.data().unwrap()) }, @@ -854,19 +902,19 @@ impl Dispatch for ServerState< } } -impl Dispatch for ServerState { +impl Dispatch for ServerState { fn request( state: &mut Self, _: &wayland_server::Client, _: &LockedPointerServer, request: ::Request, - key: &ObjectKey, + entity: &Entity, _: &DisplayHandle, _: &mut wayland_server::DataInit<'_, Self>, ) { - let locked_ptr: &LockedPointer = state.objects[*key].as_ref(); + let client = state.world.get::<&LockedPointerClient>(*entity).unwrap(); simple_event_shunt! { - locked_ptr.client, request: lp::Request => [ + client, request: lp::Request => [ SetCursorPositionHint { surface_x, surface_y }, SetRegion { |region| region.as_ref().map(|r| r.data().unwrap()) @@ -900,29 +948,31 @@ impl region, lifetime, } => { - let surf_key: ObjectKey = surface.data().copied().unwrap(); - let ptr_key: ObjectKey = pointer.data().copied().unwrap(); - state.objects.insert_from_other_objects( - [surf_key, ptr_key], - |[surf_obj, ptr_obj], key| { - let SurfaceData { - client: c_surface, .. - }: &SurfaceData = surf_obj.try_into().unwrap(); - let Pointer { client: c_ptr, .. }: &Pointer = ptr_obj.try_into().unwrap(); + let surf_key: Entity = surface.data().copied().unwrap(); + let ptr_key: Entity = pointer.data().copied().unwrap(); - let client = client.confine_pointer( - c_surface, - c_ptr, - region.as_ref().map(|r| r.data().unwrap()), - convert_wenum(lifetime), - &state.qh, - key, - ); - let server = data_init.init(id, key); + let entity = state.world.reserve_entity(); + let client = { + let c_surface = state + .world + .get::<&client::wl_surface::WlSurface>(surf_key) + .unwrap(); + let c_ptr = state + .world + .get::<&client::wl_pointer::WlPointer>(ptr_key) + .unwrap(); + client.confine_pointer( + &c_surface, + &c_ptr, + region.as_ref().map(|r| r.data().unwrap()), + convert_wenum(lifetime), + &state.qh, + entity, + ) + }; + let server = data_init.init(id, entity); - ConfinedPointer { client, server }.into() - }, - ); + state.world.spawn_at(entity, (client, server)); } Request::LockPointer { id, @@ -931,27 +981,31 @@ impl region, lifetime, } => { - let surf_key: ObjectKey = surface.data().copied().unwrap(); - let ptr_key: ObjectKey = pointer.data().copied().unwrap(); - state.objects.insert_from_other_objects( - [surf_key, ptr_key], - |[surf_obj, ptr_obj], key| { - let SurfaceData { - client: c_surface, .. - }: &SurfaceData = surf_obj.try_into().unwrap(); - let Pointer { client: c_ptr, .. }: &Pointer = ptr_obj.try_into().unwrap(); - let client = client.lock_pointer( - c_surface, - c_ptr, - region.as_ref().map(|r| r.data().unwrap()), - convert_wenum(lifetime), - &state.qh, - key, - ); - let server = data_init.init(id, key); - LockedPointer { client, server }.into() - }, - ); + let surf_key: Entity = surface.data().copied().unwrap(); + let ptr_key: Entity = pointer.data().copied().unwrap(); + + let entity = state.world.reserve_entity(); + let client = { + let c_surface = state + .world + .get::<&client::wl_surface::WlSurface>(surf_key) + .unwrap(); + let c_ptr = state + .world + .get::<&client::wl_pointer::WlPointer>(ptr_key) + .unwrap(); + client.lock_pointer( + &c_surface, + &c_ptr, + region.as_ref().map(|r| r.data().unwrap()), + convert_wenum(lifetime), + &state.qh, + entity, + ) + }; + let server = data_init.init(id, entity); + + state.world.spawn_at(entity, (client, server)); } Request::Destroy => { client.destroy(); @@ -979,15 +1033,19 @@ impl use s_tablet::zwp_tablet_manager_v2::Request::*; match request { GetTabletSeat { tablet_seat, seat } => { - let seat_key: ObjectKey = seat.data().copied().unwrap(); - state - .objects - .insert_from_other_objects([seat_key], |[seat_obj], key| { - let Seat { client: c_seat, .. }: &Seat = seat_obj.try_into().unwrap(); - let client = client.get_tablet_seat(c_seat, &state.qh, key); - let server = data_init.init(tablet_seat, key); - TabletSeat { client, server }.into() - }); + let seat_key: Entity = seat.data().copied().unwrap(); + + let entity = state.world.reserve_entity(); + let client = { + let c_seat = state + .world + .get::<&client::wl_seat::WlSeat>(seat_key) + .unwrap(); + client.get_tablet_seat(&c_seat, &state.qh, entity) + }; + let server = data_init.init(tablet_seat, entity); + + state.world.spawn_at(entity, (client, server)); } other => { warn!("unhandled tablet request: {other:?}"); @@ -996,11 +1054,20 @@ impl } } -only_destroy_request_impl!(TabletSeat); -only_destroy_request_impl!(Tablet); -only_destroy_request_impl!(TabletPadGroup); +only_destroy_request_impl!( + s_tablet::zwp_tablet_seat_v2::ZwpTabletSeatV2, + c_tablet::zwp_tablet_seat_v2::ZwpTabletSeatV2 +); +only_destroy_request_impl!( + s_tablet::zwp_tablet_v2::ZwpTabletV2, + c_tablet::zwp_tablet_v2::ZwpTabletV2 +); +only_destroy_request_impl!( + s_tablet::zwp_tablet_pad_group_v2::ZwpTabletPadGroupV2, + c_tablet::zwp_tablet_pad_group_v2::ZwpTabletPadGroupV2 +); -impl Dispatch +impl Dispatch for ServerState { fn request( @@ -1008,29 +1075,33 @@ impl Dispatch::Request, - key: &ObjectKey, + entity: &Entity, _: &DisplayHandle, _: &mut wayland_server::DataInit<'_, Self>, ) { - let pad: &TabletPad = state.objects[*key].as_ref(); + let client = state + .world + .get::<&c_tablet::zwp_tablet_pad_v2::ZwpTabletPadV2>(*entity) + .unwrap(); match request { s_tablet::zwp_tablet_pad_v2::Request::SetFeedback { button, description, serial, } => { - pad.client.set_feedback(button, description, serial); + client.set_feedback(button, description, serial); } s_tablet::zwp_tablet_pad_v2::Request::Destroy => { - pad.client.destroy(); - state.objects.remove(*key); + client.destroy(); + drop(client); + state.world.despawn(*entity).unwrap(); } other => warn!("unhandled tablet pad request: {other:?}"), } } } -impl Dispatch +impl Dispatch for ServerState { fn request( @@ -1038,11 +1109,14 @@ impl Dispatch::Request, - key: &ObjectKey, + entity: &Entity, _: &DisplayHandle, _: &mut wayland_server::DataInit<'_, Self>, ) { - let tool: &TabletTool = state.objects[*key].as_ref(); + let client = state + .world + .get::<&c_tablet::zwp_tablet_tool_v2::ZwpTabletToolV2>(*entity) + .unwrap(); match request { s_tablet::zwp_tablet_tool_v2::Request::SetCursor { serial, @@ -1050,24 +1124,26 @@ impl Dispatch { - let surf_key: Option = surface.map(|s| s.data().copied().unwrap()); + let surf_key: Option = surface.map(|s| s.data().copied().unwrap()); let c_surface = surf_key.map(|key| { - let d: &SurfaceData = state.objects[key].as_ref(); - &d.client + state + .world + .get::<&client::wl_surface::WlSurface>(key) + .unwrap() }); - tool.client - .set_cursor(serial, c_surface, hotspot_x, hotspot_y); + client.set_cursor(serial, c_surface.as_deref(), hotspot_x, hotspot_y); } s_tablet::zwp_tablet_tool_v2::Request::Destroy => { - tool.client.destroy(); - state.objects.remove(*key); + client.destroy(); + drop(client); + state.world.despawn(*entity).unwrap(); } other => warn!("unhandled tablet tool request: {other:?}"), } } } -impl Dispatch +impl Dispatch for ServerState { fn request( @@ -1075,28 +1151,32 @@ impl Dispatch::Request, - key: &ObjectKey, + entity: &Entity, _: &DisplayHandle, _: &mut wayland_server::DataInit<'_, Self>, ) { - let ring: &TabletPadRing = state.objects[*key].as_ref(); + let client = state + .world + .get::<&c_tablet::zwp_tablet_pad_ring_v2::ZwpTabletPadRingV2>(*entity) + .unwrap(); match request { s_tablet::zwp_tablet_pad_ring_v2::Request::SetFeedback { description, serial, } => { - ring.client.set_feedback(description, serial); + client.set_feedback(description, serial); } s_tablet::zwp_tablet_pad_ring_v2::Request::Destroy => { - ring.client.destroy(); - state.objects.remove(*key); + client.destroy(); + drop(client); + state.world.despawn(*entity).unwrap(); } other => warn!("unhandled tablet pad ring request: {other:?}"), } } } -impl Dispatch +impl Dispatch for ServerState { fn request( @@ -1104,21 +1184,26 @@ impl Dispatch::Request, - key: &ObjectKey, + entity: &Entity, _: &DisplayHandle, _: &mut wayland_server::DataInit<'_, Self>, ) { - let strip: &TabletPadStrip = state.objects[*key].as_ref(); + let client = state + .world + .get::<&c_tablet::zwp_tablet_pad_strip_v2::ZwpTabletPadStripV2>(*entity) + .unwrap(); + match request { s_tablet::zwp_tablet_pad_strip_v2::Request::SetFeedback { description, serial, } => { - strip.client.set_feedback(description, serial); + client.set_feedback(description, serial); } s_tablet::zwp_tablet_pad_strip_v2::Request::Destroy => { - strip.client.destroy(); - state.objects.remove(*key); + client.destroy(); + drop(client); + state.world.despawn(*entity).unwrap(); } other => warn!("unhandled tablet pad strip request: {other:?}"), } @@ -1145,7 +1230,7 @@ macro_rules! global_dispatch_no_events { impl GlobalDispatch<$server, Global> for ServerState where ServerState: Dispatch<$server, ClientGlobalWrapper<$client>>, - Globals: wayland_client::Dispatch<$client, ()>, + MyWorld: wayland_client::Dispatch<$client, ()>, { fn bind( state: &mut Self, @@ -1159,13 +1244,12 @@ macro_rules! global_dispatch_no_events { let server = data_init.init(resource, client.clone()); client .0 - .set( - state - .clientside - .global_list - .registry() - .bind::<$client, _, _>(data.name, server.version(), &state.qh, ()), - ) + .set(state.world.global_list.registry().bind::<$client, _, _>( + data.name, + server.version(), + &state.qh, + (), + )) .unwrap(); } } @@ -1178,9 +1262,8 @@ macro_rules! global_dispatch_with_events { where $server: Resource, $client: Proxy, - ServerState: Dispatch<$server, ObjectKey>, - Globals: wayland_client::Dispatch<$client, ObjectKey>, - GenericObject<$server, $client>: Into, + ServerState: Dispatch<$server, Entity>, + MyWorld: wayland_client::Dispatch<$client, Entity>, { fn bind( state: &mut Self, @@ -1190,15 +1273,15 @@ macro_rules! global_dispatch_with_events { data: &Global, data_init: &mut wayland_server::DataInit<'_, Self>, ) { - state.objects.insert_with_key(|key| { - let server = data_init.init(resource, key); - let client = state - .clientside - .global_list - .registry() - .bind::<$client, _, _>(data.name, server.version(), &state.qh, key); - GenericObject { server, client }.into() - }); + let entity = state.world.reserve_entity(); + let server = data_init.init(resource, entity); + let client = state.world.global_list.registry().bind::<$client, _, _>( + data.name, + server.version(), + &state.qh, + entity, + ); + state.world.spawn_at(entity, (server, client)); } } }; @@ -1218,14 +1301,7 @@ global_dispatch_no_events!( c_tablet::zwp_tablet_manager_v2::ZwpTabletManagerV2 ); -impl GlobalDispatch for ServerState -where - WlSeat: Resource, - client::wl_seat::WlSeat: Proxy, - ServerState: Dispatch, - Globals: wayland_client::Dispatch, - GenericObject: Into, -{ +impl GlobalDispatch for ServerState { fn bind( state: &mut Self, _: &DisplayHandle, @@ -1234,20 +1310,20 @@ where data: &Global, data_init: &mut wayland_server::DataInit<'_, Self>, ) { - state.objects.insert_with_key(|key| { - let server = data_init.init(resource, key); - let client = state - .clientside - .global_list - .registry() - .bind::(data.name, server.version(), &state.qh, key); - if let Some(c) = &mut state.clipboard_data { - c.device = Some(c.manager.get_data_device(&state.qh, &client)); - } - GenericObject { server, client }.into() - }); + let entity = state.world.reserve_entity(); + let server = data_init.init(resource, entity); + let client = state + .world + .global_list + .registry() + .bind::(data.name, server.version(), &state.qh, entity); + if let Some(c) = &mut state.clipboard_data { + c.device = Some(c.manager.get_data_device(&state.qh, &client)); + } + state.world.spawn_at(entity, (server, client)); } } + impl GlobalDispatch for ServerState { fn bind( state: &mut Self, @@ -1257,21 +1333,27 @@ impl GlobalDispatch for ServerState { data: &Global, data_init: &mut wayland_server::DataInit<'_, Self>, ) { - let key = state.objects.insert_with_key(|key| { - let server = data_init.init(resource, key); - let client = state - .clientside - .global_list - .registry() - .bind::( - data.name, - server.version(), - &state.qh, - key, - ); - Output::new(client, server).into() - }); - state.output_keys.insert(key, ()); + let entity = state.world.reserve_entity(); + let server = data_init.init(resource, entity); + let client = state + .world + .global_list + .registry() + .bind::( + data.name, + server.version(), + &state.qh, + entity, + ); + state.world.spawn_at( + entity, + ( + server, + client, + event::OutputScaleFactor(1), + event::OutputDimensions::default(), + ), + ); state.output_scales_updated = true; } } @@ -1303,9 +1385,8 @@ impl Dispatch for ServerState { use xwayland_shell_v1::Request; match request { Request::GetXwaylandSurface { id, surface } => { - let key: ObjectKey = surface.data().copied().unwrap(); - let data: &mut SurfaceData = state.objects[key].as_mut(); - if data.xwl.is_some() { + let e: Entity = surface.data().copied().unwrap(); + if state.world.entity(e).unwrap().has::() { error!("Surface {surface:?} already has the xwayland surface role!"); client.kill( dhandle, @@ -1319,8 +1400,7 @@ impl Dispatch for ServerState { return; } - let xwl = data_init.init(id, key); - data.xwl = Some(xwl); + data_init.init(id, e); } Request::Destroy => {} _ => unreachable!(), @@ -1328,45 +1408,49 @@ impl Dispatch for ServerState { } } -impl Dispatch for ServerState { +impl Dispatch for ServerState { fn request( state: &mut Self, _: &wayland_server::Client, _: &XwaylandSurfaceV1, request: ::Request, - key: &ObjectKey, + entity: &Entity, _: &DisplayHandle, _: &mut wayland_server::DataInit<'_, Self>, ) { use xwayland_surface_v1::Request; - let data: &mut SurfaceData = state.objects[*key].as_mut(); match request { Request::SetSerial { serial_lo, serial_hi, } => { - let serial = [serial_lo, serial_hi]; - data.serial = Some(serial); - if let Some((win, window_data)) = - state.windows.iter_mut().find_map(|(win, data)| { - Some(*win) - .zip((data.surface_serial.is_some_and(|s| s == serial)).then_some(data)) - }) - { - debug!( - "associate surface {} with {:?}", - data.server.id().protocol_id(), - win - ); - window_data.surface_key = Some(*key); - state.associated_windows.insert(*key, win); - if window_data.mapped { - state.create_role_window(win, *key); + let surface_id = state.world.get::<&WlSurface>(*entity).unwrap().id(); + let serial = SurfaceSerial([serial_lo, serial_hi]); + let win_entity = state + .world + .query_mut::<&SurfaceSerial>() + .without::<&WlSurface>() + .into_iter() + .find(|(_, surface_serial)| **surface_serial == serial) + .map(|i| i.0); + + if let Some(win_entity) = win_entity { + let win = *state.world.get::<&x::Window>(win_entity).unwrap(); + debug!("associate {surface_id} with {win:?}"); + state.windows.insert(win, *entity); + let mut builder = hecs::EntityBuilder::new(); + builder.add_bundle(state.world.take(win_entity).unwrap()); + state.world.insert(*entity, builder.build()).unwrap(); + let data = state.world.entity(*entity).unwrap(); + if data.get::<&WindowData>().unwrap().mapped { + state.create_role_window(win, *entity); } + } else { + state.world.insert(*entity, (serial,)).unwrap(); } } Request::Destroy => { - data.xwl.take(); + state.world.remove_one::(*entity).unwrap(); } _ => unreachable!(), } diff --git a/src/server/event.rs b/src/server/event.rs index 4d84631..7b33264 100644 --- a/src/server/event.rs +++ b/src/server/event.rs @@ -1,38 +1,25 @@ +use super::clientside::LateInitObjectKey; use super::*; -use crate::clientside::LateInitObjectKey; +use hecs::CommandBuffer; use log::{debug, trace, warn}; use macros::simple_event_shunt; -use std::collections::HashSet; use std::os::fd::AsFd; use wayland_client::{protocol as client, Proxy}; use wayland_protocols::{ wp::{ fractional_scale::v1::client::wp_fractional_scale_v1, - pointer_constraints::zv1::{ - client::{ - zwp_confined_pointer_v1::{self, ZwpConfinedPointerV1 as ConfinedPointerClient}, - zwp_locked_pointer_v1::{self, ZwpLockedPointerV1 as LockedPointerClient}, - }, - server::{ - zwp_confined_pointer_v1::ZwpConfinedPointerV1 as ConfinedPointerServer, - zwp_locked_pointer_v1::ZwpLockedPointerV1 as LockedPointerServer, - }, + pointer_constraints::zv1::server::{ + zwp_confined_pointer_v1::ZwpConfinedPointerV1 as ConfinedPointerServer, + zwp_locked_pointer_v1::ZwpLockedPointerV1 as LockedPointerServer, }, relative_pointer::zv1::{ - client::zwp_relative_pointer_v1::{ - self, ZwpRelativePointerV1 as RelativePointerClient, - }, + client::zwp_relative_pointer_v1, server::zwp_relative_pointer_v1::ZwpRelativePointerV1 as RelativePointerServer, }, tablet::zv2::{ client::{ - zwp_tablet_pad_group_v2::{self, ZwpTabletPadGroupV2 as TabletPadGroupClient}, - zwp_tablet_pad_ring_v2::{self, ZwpTabletPadRingV2 as TabletPadRingClient}, - zwp_tablet_pad_strip_v2::{self, ZwpTabletPadStripV2 as TabletPadStripClient}, - zwp_tablet_pad_v2::{self, ZwpTabletPadV2 as TabletPadClient}, - zwp_tablet_seat_v2::{self, ZwpTabletSeatV2 as TabletSeatClient}, - zwp_tablet_tool_v2::{self, ZwpTabletToolV2 as TabletToolClient}, - zwp_tablet_v2::{self, ZwpTabletV2 as TabletClient}, + zwp_tablet_pad_group_v2, zwp_tablet_pad_ring_v2, zwp_tablet_pad_strip_v2, + zwp_tablet_pad_v2, zwp_tablet_seat_v2, zwp_tablet_tool_v2, zwp_tablet_v2, }, server::{ zwp_tablet_pad_group_v2::ZwpTabletPadGroupV2 as TabletPadGroupServer, @@ -48,8 +35,7 @@ use wayland_protocols::{ xdg::{ shell::client::{xdg_popup, xdg_surface, xdg_toplevel}, xdg_output::zv1::{ - client::zxdg_output_v1::{self, ZxdgOutputV1 as ClientXdgOutput}, - server::zxdg_output_v1::ZxdgOutputV1 as ServerXdgOutput, + client::zxdg_output_v1, server::zxdg_output_v1::ZxdgOutputV1 as XdgOutputServer, }, }, }; @@ -58,6 +44,14 @@ use wayland_server::protocol::{ wl_seat::WlSeat, wl_touch::WlTouch, }; +#[derive(hecs::Bundle)] +pub(super) struct SurfaceBundle { + pub client: client::wl_surface::WlSurface, + pub server: WlSurface, + pub scale: SurfaceScaleFactor, + pub viewport: WpViewport, +} + #[derive(Debug)] pub(crate) enum SurfaceEvents { WlSurface(client::wl_surface::Event), @@ -81,24 +75,25 @@ impl_from!(xdg_toplevel::Event, Toplevel); impl_from!(xdg_popup::Event, Popup); impl_from!(wp_fractional_scale_v1::Event, FractionalScale); -impl HandleEvent for SurfaceData { - type Event = SurfaceEvents; - fn handle_event(&mut self, event: Self::Event, state: &mut ServerState) { - match event { - SurfaceEvents::WlSurface(event) => self.surface_event(event, state), - SurfaceEvents::XdgSurface(event) => self.xdg_event(event, state), - SurfaceEvents::Toplevel(event) => self.toplevel_event(event, state), - SurfaceEvents::Popup(event) => self.popup_event(event, state), +impl Event for SurfaceEvents { + fn handle(self, target: Entity, state: &mut ServerState) { + match self { + SurfaceEvents::WlSurface(event) => Self::surface_event(event, target, state), + SurfaceEvents::XdgSurface(event) => Self::xdg_event(event, target, state), + SurfaceEvents::Toplevel(event) => Self::toplevel_event(event, target, state), + SurfaceEvents::Popup(event) => Self::popup_event(event, target, state), SurfaceEvents::FractionalScale(event) => match event { wp_fractional_scale_v1::Event::PreferredScale { scale } => { - self.scale_factor = scale as f64 / 120.0; - log::debug!("{} scale factor: {}", self.server.id(), self.scale_factor); - if let Some(win_data) = self - .window - .as_ref() - .and_then(|win| state.windows.get_mut(win)) - { - self.update_viewport(win_data.attrs.dims, win_data.attrs.size_hints); + let entity = state.world.entity(target).unwrap(); + let factor = scale as f64 / 120.0; + entity.get::<&mut SurfaceScaleFactor>().unwrap().0 = factor; + log::debug!( + "{} scale factor: {}", + entity.get::<&WlSurface>().unwrap().id(), + factor + ); + if entity.has::() { + update_surface_viewport(state.world.query_one(target).unwrap()); } } _ => unreachable!(), @@ -107,147 +102,127 @@ impl HandleEvent for SurfaceData { } } -impl SurfaceData { - fn get_output_name(&self, state: &ServerState) -> Option { - let output_name = self - .output_key - .and_then(|key| state.objects.get(key)) - .map(|obj| <_ as AsRef>::as_ref(obj).name.clone()); - - if output_name.is_none() { - warn!( - "{} has no output name ({:?})", - self.server.id(), - self.output_key - ); - } - - output_name - } - - pub(super) fn update_viewport(&self, dims: WindowDims, size_hints: Option) { - let width = (dims.width as f64 / self.scale_factor) as i32; - let height = (dims.height as f64 / self.scale_factor) as i32; - if width > 0 && height > 0 { - self.viewport.set_destination(width, height); - } - debug!("{} viewport: {width}x{height}", self.server.id()); - if let Some(hints) = size_hints { - let Some(SurfaceRole::Toplevel(Some(data))) = &self.role else { - warn!( - "Trying to update size hints on {}, but toplevel role data is missing", - self.server.id() - ); - return; - }; - - if let Some(min) = hints.min_size { - data.toplevel.set_min_size( - (min.width as f64 / self.scale_factor) as i32, - (min.height as f64 / self.scale_factor) as i32, - ); - } - if let Some(max) = hints.max_size { - data.toplevel.set_max_size( - (max.width as f64 / self.scale_factor) as i32, - (max.height as f64 / self.scale_factor) as i32, - ); - } - } - } - - fn surface_event( - &mut self, +impl SurfaceEvents { + fn surface_event( event: client::wl_surface::Event, - state: &mut ServerState, + target: Entity, + state: &mut ServerState, ) { use client::wl_surface::Event; + let data = state.world.entity(target).unwrap(); + let surface = data.get::<&WlSurface>().unwrap(); + let mut cmd = CommandBuffer::new(); match event { Event::Enter { output } => { - let key: ObjectKey = output.data().copied().unwrap(); - let Some(object) = state.objects.get_mut(key) else { + let output_entity = output.data().copied().unwrap(); + let Ok(output_data) = state.world.entity(output_entity) else { + return; + }; + let Some(output) = output_data.get::<&WlOutput>() else { return; }; - let output: &mut Output = object.as_mut(); - self.server.enter(&output.server); - if state.fractional_scale.is_none() { - self.scale_factor = output.scale as f64; - } - self.output_key = Some(key); - debug!("{} entered {}", self.server.id(), output.server.id()); - let windows = &mut state.windows; - if let Some(win_data) = self.window.as_ref().and_then(|win| windows.get_mut(win)) { - if state.fractional_scale.is_none() { - self.update_viewport(win_data.attrs.dims, win_data.attrs.size_hints); - } + surface.enter(&output); + let on_output = OnOutput(output_entity); + + if state.fractional_scale.is_none() { + data.get::<&mut SurfaceScaleFactor>().unwrap().0 = + output_data.get::<&OutputScaleFactor>().unwrap().0 as f64; + } + + debug!("{} entered {}", surface.id(), output.id()); + + let mut query = data.query::<(&x::Window, &mut WindowData)>(); + if let Some((window, win_data)) = query.get() { + let dimensions = output_data.get::<&OutputDimensions>().unwrap(); win_data.update_output_offset( - key, + *window, WindowOutputOffset { - x: output.dimensions.x - state.global_output_offset.x.value, - y: output.dimensions.y - state.global_output_offset.y.value, + x: dimensions.x - state.global_output_offset.x.value, + y: dimensions.y - state.global_output_offset.y.value, }, state.connection.as_mut().unwrap(), ); - let window = win_data.window; - output.windows.insert(window); - if self.window.is_some() && state.last_focused_toplevel == self.window { - let output = self.get_output_name(state); + if state.last_focused_toplevel == Some(*window) { + let output = get_output_name(Some(&on_output), &state.world); let conn = state.connection.as_mut().unwrap(); debug!("focused window changed outputs - resetting primary output"); - conn.focus_window(window, output); + conn.focus_window(*window, output); + } + + drop(query); + if state.fractional_scale.is_none() { + update_surface_viewport(state.world.query_one(target).unwrap()); } } + cmd.insert_one(target, on_output); } Event::Leave { output } => { - let key: ObjectKey = output.data().copied().unwrap(); - let Some(object) = state.objects.get_mut(key) else { + let output_entity = output.data().copied().unwrap(); + let Ok(output) = state.world.get::<&WlOutput>(output_entity) else { return; }; - let output: &mut Output = object.as_mut(); - self.server.leave(&output.server); - if self.output_key == Some(key) { - self.output_key = None; + surface.leave(&output); + if data + .get::<&OnOutput>() + .is_some_and(|o| o.0 == output_entity) + { + cmd.remove_one::(target); } } Event::PreferredBufferScale { .. } => {} other => warn!("unhandled surface request: {other:?}"), } + + drop(surface); + cmd.run_on(&mut state.world); } - fn xdg_event(&mut self, event: xdg_surface::Event, state: &mut ServerState) { + fn xdg_event( + event: xdg_surface::Event, + target: Entity, + state: &mut ServerState, + ) { let connection = state.connection.as_mut().unwrap(); let xdg_surface::Event::Configure { serial } = event else { unreachable!(); }; - let xdg = self.xdg_mut().unwrap(); + let data = state.world.entity(target).unwrap(); + let mut xdg = hecs::RefMut::map(data.get::<&mut SurfaceRole>().unwrap(), |r| { + r.xdg_mut().unwrap() + }); xdg.surface.ack_configure(serial); xdg.configured = true; - if let Some(pending) = xdg.pending.take() { - let window = state.associated_windows[self.key]; - let window = state.windows.get_mut(&window).unwrap(); - let x = (pending.x as f64 * self.scale_factor) as i32 + window.output_offset.x; - let y = (pending.y as f64 * self.scale_factor) as i32 + window.output_offset.y; + let pending = xdg.pending.take(); + drop(xdg); + + if let Some(pending) = pending { + let mut query = data.query::<(&mut SurfaceScaleFactor, &x::Window, &mut WindowData)>(); + let (scale_factor, window, window_data) = query.get().unwrap(); + let scale_factor = scale_factor.0; + + let window = *window; + let x = (pending.x as f64 * scale_factor) as i32 + window_data.output_offset.x; + let y = (pending.y as f64 * scale_factor) as i32 + window_data.output_offset.y; let width = if pending.width > 0 { - (pending.width as f64 * self.scale_factor) as u16 + (pending.width as f64 * scale_factor) as u16 } else { - window.attrs.dims.width + window_data.attrs.dims.width }; let height = if pending.height > 0 { - (pending.height as f64 * self.scale_factor) as u16 + (pending.height as f64 * scale_factor) as u16 } else { - window.attrs.dims.height + window_data.attrs.dims.height }; debug!( - "configuring {} ({:?}): {x}x{y}, {width}x{height}", - self.server.id(), - window.window + "configuring {} ({window:?}): {x}x{y}, {width}x{height}", + data.get::<&WlSurface>().unwrap().id(), ); connection.set_window_dims( - window.window, + window, PendingSurfaceState { x, y, @@ -255,29 +230,46 @@ impl SurfaceData { height: height as _, }, ); - window.attrs.dims = WindowDims { + window_data.attrs.dims = WindowDims { x: x as i16, y: y as i16, width, height, }; - self.update_viewport(window.attrs.dims, window.attrs.size_hints); + + drop(query); + update_surface_viewport(state.world.query_one(target).unwrap()); } - if let Some(SurfaceAttach { buffer, x, y }) = self.attach.take() { - self.client.attach(buffer.as_ref(), x, y); + let (surface, attach, callback) = state + .world + .query_one_mut::<( + &client::wl_surface::WlSurface, + Option<&SurfaceAttach>, + Option<&WlCallback>, + )>(target) + .unwrap(); + + let mut cmd = CommandBuffer::new(); + + if let Some(SurfaceAttach { buffer, x, y }) = attach { + surface.attach(buffer.as_ref(), *x, *y); + cmd.remove_one::(target); } - if let Some(cb) = self.frame_callback.take() { - self.client.frame(&state.qh, cb); + if let Some(cb) = callback { + surface.frame(&state.qh, cb.clone()); + cmd.remove_one::(target); } - self.client.commit(); + surface.commit(); + cmd.run_on(&mut state.world); } fn toplevel_event( - &mut self, event: xdg_toplevel::Event, + target: Entity, state: &mut ServerState, ) { + let data = state.world.entity(target).unwrap(); match event { xdg_toplevel::Event::Configure { width, @@ -286,30 +278,30 @@ impl SurfaceData { } => { debug!( "configuring toplevel {} {width}x{height}, {states:?}", - self.server.id() + data.get::<&WlSurface>().unwrap().id() ); - if let Some(SurfaceRole::Toplevel(Some(toplevel))) = &mut self.role { + + let mut role = data.get::<&mut SurfaceRole>().unwrap(); + if let SurfaceRole::Toplevel(Some(toplevel)) = &mut *role { let prev_fs = toplevel.fullscreen; toplevel.fullscreen = states.contains(&(u32::from(xdg_toplevel::State::Fullscreen) as u8)); if toplevel.fullscreen != prev_fs { - let window = state.associated_windows[self.key]; - state - .connection - .as_mut() - .unwrap() - .set_fullscreen(window, toplevel.fullscreen); + state.connection.as_mut().unwrap().set_fullscreen( + *data.get::<&x::Window>().unwrap(), + toplevel.fullscreen, + ); } }; - self.xdg_mut().unwrap().pending = Some(PendingSurfaceState { + role.xdg_mut().unwrap().pending = Some(PendingSurfaceState { width, height, ..Default::default() }); } xdg_toplevel::Event::Close => { - let window = state.associated_windows[self.key]; + let window = *data.get::<&x::Window>().unwrap(); state.close_x_window(window); } // TODO: support capabilities (minimize, maximize, etc) @@ -319,7 +311,12 @@ impl SurfaceData { } } - fn popup_event(&mut self, event: xdg_popup::Event, state: &mut ServerState) { + fn popup_event( + event: xdg_popup::Event, + target: Entity, + state: &mut ServerState, + ) { + let data = state.world.entity(target).unwrap(); match event { xdg_popup::Event::Configure { x, @@ -329,9 +326,13 @@ impl SurfaceData { } => { trace!( "popup configure {}: {x}x{y}, {width}x{height}", - self.server.id() + data.get::<&WlSurface>().unwrap().id() ); - self.xdg_mut().unwrap().pending = Some(PendingSurfaceState { + data.get::<&mut SurfaceRole>() + .unwrap() + .xdg_mut() + .unwrap() + .pending = Some(PendingSurfaceState { x, y, width, @@ -344,69 +345,68 @@ impl SurfaceData { .connection .as_mut() .unwrap() - .unmap_window(self.window.unwrap()); + .unmap_window(*data.get::<&x::Window>().unwrap()); } other => todo!("{other:?}"), } } } -pub struct GenericObject { - pub server: Server, - pub client: Client, -} +pub(super) fn update_surface_viewport( + mut surface_query: hecs::QueryOne<( + &WindowData, + &WpViewport, + &SurfaceScaleFactor, + Option<&SurfaceRole>, + &WlSurface, + )>, +) { + let (window_data, viewport, scale_factor, role, surface) = surface_query.get().unwrap(); + let dims = &window_data.attrs.dims; + let size_hints = &window_data.attrs.size_hints; -impl GenericObject { - fn from_client(client: C, state: &mut ServerState) -> &Self - where - Self: Into, - for<'a> &'a Self: TryFrom<&'a Object, Error = String>, - ServerState: wayland_server::Dispatch, - C::Event: Send + Into, - { - let key = state.objects.insert_with_key(|key| { - let server = state - .client - .as_ref() - .unwrap() - .create_resource::<_, _, ServerState>(&state.dh, 1, key) - .unwrap(); - let obj_key: &LateInitObjectKey = client.data().unwrap(); - obj_key.init(key); + let width = (dims.width as f64 / scale_factor.0) as i32; + let height = (dims.height as f64 / scale_factor.0) as i32; + if width > 0 && height > 0 { + viewport.set_destination(width, height); + } + debug!("{} viewport: {width}x{height}", surface.id()); + if let Some(hints) = size_hints { + let Some(SurfaceRole::Toplevel(Some(data))) = &role else { + warn!( + "Trying to update size hints on {}, but toplevel role data is missing", + surface.id() + ); + return; + }; - Self { client, server }.into() - }); - - state.objects[key].as_ref() + if let Some(min) = hints.min_size { + data.toplevel.set_min_size( + (min.width as f64 / scale_factor.0) as i32, + (min.height as f64 / scale_factor.0) as i32, + ); + } + if let Some(max) = hints.max_size { + data.toplevel.set_max_size( + (max.width as f64 / scale_factor.0) as i32, + (max.height as f64 / scale_factor.0) as i32, + ); + } } } -pub trait GenericObjectExt { - type Server: Resource; - type Client: Proxy; -} - -impl GenericObjectExt for GenericObject { - type Server = S; - type Client = C; -} - -pub type Buffer = GenericObject; -impl HandleEvent for Buffer { - type Event = client::wl_buffer::Event; - fn handle_event(&mut self, _: Self::Event, _: &mut ServerState) { +impl Event for client::wl_buffer::Event { + fn handle(self, target: Entity, state: &mut ServerState) { // The only event from a buffer would be the release. - self.server.release(); + state.world.get::<&WlBuffer>(target).unwrap().release(); } } -pub type Seat = GenericObject; -impl HandleEvent for Seat { - type Event = client::wl_seat::Event; - - fn handle_event(&mut self, event: Self::Event, _: &mut ServerState) { +impl Event for client::wl_seat::Event { + fn handle(self, target: Entity, state: &mut ServerState) { + let server = state.world.get::<&WlSeat>(target).unwrap(); simple_event_shunt! { - self.server, event: client::wl_seat::Event => [ + server, self => [ Capabilities { |capabilities| convert_wenum(capabilities) }, Name { name } ] @@ -414,30 +414,10 @@ impl HandleEvent for Seat { } } -pub struct Pointer { - server: WlPointer, - pub client: client::wl_pointer::WlPointer, - pending_enter: PendingEnter, - scale: f64, -} +struct PendingEnter(client::wl_pointer::Event); -impl Pointer { - pub fn new(server: WlPointer, client: client::wl_pointer::WlPointer) -> Self { - Self { - server, - client, - pending_enter: PendingEnter(None), - scale: 1.0, - } - } -} - -struct PendingEnter(Option); - -impl HandleEvent for Pointer { - type Event = client::wl_pointer::Event; - - fn handle_event(&mut self, event: Self::Event, state: &mut ServerState) { +impl Event for client::wl_pointer::Event { + fn handle(self, target: Entity, state: &mut ServerState) { // Workaround GTK (stupidly) autoclosing popups if it receives an wl_pointer.enter // event shortly after creation. // When Niri creates a popup, it immediately sends wl_pointer.enter on the new surface, @@ -446,73 +426,83 @@ impl HandleEvent for Pointer { // destroy the menu if this occurs within a 500 ms interval (which it always does with // Niri). Other compositors do not run into this problem because they appear to not send // wl_pointer.enter until the user actually moves the mouse in the popup. - match event { - client::wl_pointer::Event::Enter { + + match self { + Self::Enter { serial, ref surface, surface_x, surface_y, - } => 'enter: { - let Some(surface_data): Option<&SurfaceData> = surface - .data::() - .copied() - .and_then(|key| state.objects.get(key)) - .map(|o| o.as_ref()) - else { + } => { + let mut cmd = CommandBuffer::new(); + let pending_enter = state.world.remove_one::(target).ok(); + let server = state.world.get::<&WlPointer>(target).unwrap(); + let Some(mut query) = surface.data().copied().and_then(|e| { + state + .world + .query_one::<(&WlSurface, &SurfaceRole, &SurfaceScaleFactor, &x::Window)>(e) + .ok() + }) else { warn!("could not enter surface: stale surface"); - break 'enter; + return; }; - self.scale = surface_data.scale_factor; - let surface_is_popup = matches!(surface_data.role, Some(SurfaceRole::Popup(_))); + let (surface, role, scale, window) = query.get().unwrap(); + cmd.insert(target, (*scale,)); + + let surface_is_popup = matches!(role, SurfaceRole::Popup(_)); let mut do_enter = || { - debug!("pointer entering {} ({serial})", surface_data.server.id()); - self.server.enter( - serial, - &surface_data.server, - surface_x * self.scale, - surface_y * self.scale, - ); - let window = surface_data.window.unwrap(); - state.connection.as_mut().unwrap().raise_to_top(window); + debug!("pointer entering {} ({serial})", surface.id()); + server.enter(serial, surface, surface_x * scale.0, surface_y * scale.0); + state.connection.as_mut().unwrap().raise_to_top(*window); if !surface_is_popup { - state.last_hovered = Some(window); + state.last_hovered = Some(*window); } }; - if surface_is_popup { - match self.pending_enter.0.take() { + if !surface_is_popup { + do_enter(); + } else { + match pending_enter { Some(e) => { - let client::wl_pointer::Event::Enter { + let PendingEnter(client::wl_pointer::Event::Enter { serial: pending_serial, .. - } = e + }) = e else { unreachable!(); }; if serial == pending_serial { do_enter(); } else { - self.pending_enter.0 = Some(event); + cmd.insert(target, (PendingEnter(self),)); } } None => { - self.pending_enter.0 = Some(event); + cmd.insert(target, (PendingEnter(self),)); } } - } else { - self.pending_enter.0.take(); - do_enter(); } + drop(query); + drop(server); + cmd.run_on(&mut state.world); } client::wl_pointer::Event::Leave { serial, surface } => { if !surface.is_alive() { return; } debug!("leaving surface ({serial})"); - self.pending_enter.0.take(); - if let Some(surface) = state.get_server_surface_from_client(surface) { - self.server.leave(serial, surface); + let _ = state.world.remove_one::(target); + if let Some(surface) = surface + .data() + .copied() + .and_then(|key| state.world.get::<&WlSurface>(key).ok()) + { + state + .world + .get::<&WlPointer>(target) + .unwrap() + .leave(serial, &surface); } else { warn!("could not leave surface: stale surface"); } @@ -522,163 +512,144 @@ impl HandleEvent for Pointer { surface_x, surface_y, } => { - if let Some(p) = &self.pending_enter.0 { - let client::wl_pointer::Event::Enter { - serial, - surface, - surface_x, - surface_y, - } = p - else { - unreachable!(); - }; - if surface - .data() - .copied() - .and_then(|key| state.objects.get(key)) - .is_some() - { - trace!("resending enter ({serial}) before motion"); - let enter_event = client::wl_pointer::Event::Enter { - serial: *serial, - surface: surface.clone(), - surface_x: *surface_x, - surface_y: *surface_y, + let pending_enter = state.world.get::<&PendingEnter>(target).ok(); + match pending_enter.as_deref() { + Some(p) => { + let PendingEnter(client::wl_pointer::Event::Enter { + serial, + surface, + surface_x, + surface_y, + }) = p + else { + unreachable!(); }; - self.handle_event(enter_event, state); - self.handle_event(event, state); - } else { - warn!("could not move pointer to surface ({serial}): stale surface"); + if surface + .data() + .copied() + .is_some_and(|key| state.world.contains(key)) + { + trace!("resending enter ({serial}) before motion"); + let enter_event = client::wl_pointer::Event::Enter { + serial: *serial, + surface: surface.clone(), + surface_x: *surface_x, + surface_y: *surface_y, + }; + + drop(pending_enter); + Self::handle(enter_event, target, state); + Self::handle(self, target, state); + } else { + warn!("could not move pointer to surface ({serial}): stale surface"); + } + } + None => { + drop(pending_enter); + let (server, scale) = state + .world + .query_one_mut::<(&WlPointer, &SurfaceScaleFactor)>(target) + .unwrap(); + trace!( + target: "pointer_position", + "pointer motion {} {}", + surface_x * scale.0, + surface_y * scale.0 + ); + server.motion(time, surface_x * scale.0, surface_y * scale.0); } - } else { - trace!( - target: "pointer_position", - "pointer motion {} {}", - surface_x * self.scale, - surface_y * self.scale - ); - self.server - .motion(time, surface_x * self.scale, surface_y * self.scale); } } - _ => simple_event_shunt! { - self.server, event: client::wl_pointer::Event => [ - Enter { - serial, - |surface| { - let Some(surface_data) = state.get_server_surface_from_client(surface) else { - return; - }; - surface_data + _ => { + let server = state.world.get::<&WlPointer>(target).unwrap(); + simple_event_shunt! { + server, self => [ + Frame, + Button { + serial, + time, + button, + |state| convert_wenum(state) }, - surface_x, - surface_y - }, - Leave { - serial, - |surface| { - let Some(surface_data) = state.get_server_surface_from_client(surface) else { - return; - }; - surface_data + Axis { + time, + |axis| convert_wenum(axis), + value + }, + AxisSource { + |axis_source| convert_wenum(axis_source) + }, + AxisStop { + time, + |axis| convert_wenum(axis) + }, + AxisDiscrete { + |axis| convert_wenum(axis), + discrete + }, + AxisValue120 { + |axis| convert_wenum(axis), + value120 + }, + AxisRelativeDirection { + |axis| convert_wenum(axis), + |direction| convert_wenum(direction) } - }, - Motion { - time, - surface_x, - surface_y - }, - Frame, - Button { - serial, - time, - button, - |state| convert_wenum(state) - }, - Axis { - time, - |axis| convert_wenum(axis), - value - }, - AxisSource { - |axis_source| convert_wenum(axis_source) - }, - AxisStop { - time, - |axis| convert_wenum(axis) - }, - AxisDiscrete { - |axis| convert_wenum(axis), - discrete - }, - AxisValue120 { - |axis| convert_wenum(axis), - value120 - }, - AxisRelativeDirection { - |axis| convert_wenum(axis), - |direction| convert_wenum(direction) - } - ] - }, + ] + } + } } } } -pub struct Keyboard { - pub server: WlKeyboard, - pub client: client::wl_keyboard::WlKeyboard, - pub seat: client::wl_seat::WlSeat, -} - -impl HandleEvent for Keyboard { - type Event = client::wl_keyboard::Event; - - fn handle_event(&mut self, event: Self::Event, state: &mut ServerState) { - match event { +impl Event for client::wl_keyboard::Event { + fn handle(self, target: Entity, state: &mut ServerState) { + let data = state.world.entity(target).unwrap(); + let keyboard = data.get::<&WlKeyboard>().unwrap(); + match self { client::wl_keyboard::Event::Enter { serial, surface, keys, } => { - if let Some(data) = surface.data().copied().and_then(|key| { + if let Some(mut query) = surface.data().copied().and_then(|key| { state - .objects - .get(key) - .map(<_ as AsRef>::as_ref) + .world + .query_one::<(&x::Window, &WlSurface, Option<&OnOutput>)>(key) + .ok() }) { + let (window, surface, output) = query.get().unwrap(); state.last_kb_serial = Some(( - state - .last_kb_serial - .take() - .and_then(|(seat, _)| (seat == self.seat).then_some(seat)) - .unwrap_or_else(|| self.seat.clone()), + data.get::<&client::wl_seat::WlSeat>() + .as_deref() + .unwrap() + .clone(), serial, )); - let output_name = data.get_output_name(state); + let output_name = get_output_name(output, &state.world); state.to_focus = Some(FocusData { - window: data.window.unwrap(), + window: *window, output_name, }); - self.server.enter(serial, &data.server, keys); + keyboard.enter(serial, surface, keys); } } client::wl_keyboard::Event::Leave { serial, surface } => { if !surface.is_alive() { return; } - if let Some(data) = surface.data().copied().and_then(|key| { - state - .objects - .get(key) - .map(<_ as AsRef>::as_ref) - }) { - if state.to_focus.as_ref().map(|d| d.window) == Some(data.window.unwrap()) { + if let Some(mut query) = surface + .data() + .copied() + .and_then(|key| state.world.query_one::<(&x::Window, &WlSurface)>(key).ok()) + { + let (window, surface) = query.get().unwrap(); + if state.to_focus.as_ref().map(|d| d.window) == Some(*window) { state.to_focus.take(); } else { state.unfocus = true; } - self.server.leave(serial, &data.server); + keyboard.leave(serial, surface); } } client::wl_keyboard::Event::Key { @@ -688,17 +659,16 @@ impl HandleEvent for Keyboard { state: key_state, } => { state.last_kb_serial = Some(( - state - .last_kb_serial - .take() - .and_then(|(seat, _)| (seat == self.seat).then_some(seat)) - .unwrap_or_else(|| self.seat.clone()), + data.get::<&client::wl_seat::WlSeat>() + .as_deref() + .unwrap() + .clone(), serial, )); - self.server.key(serial, time, key, convert_wenum(key_state)); + keyboard.key(serial, time, key, convert_wenum(key_state)); } _ => simple_event_shunt! { - self.server, event: client::wl_keyboard::Event => [ + keyboard, self => [ Keymap { |format| convert_wenum(format), |fd| fd.as_fd(), @@ -721,57 +691,43 @@ impl HandleEvent for Keyboard { } } -pub type Touch = GenericObject; -impl HandleEvent for Touch { - type Event = client::wl_touch::Event; - fn handle_event(&mut self, event: Self::Event, state: &mut ServerState) { +impl Event for client::wl_touch::Event { + fn handle(self, target: Entity, state: &mut ServerState) { + let touch = state.world.get::<&WlTouch>(target).unwrap(); + + let s_surf; simple_event_shunt! { - self.server, event: client::wl_touch::Event => [ + touch, self => [ Down { serial, time, |surface| { - let Some(surface_data) = state.get_server_surface_from_client(surface) else { - return; - }; - surface_data + s_surf = surface.data().copied().and_then(|key| state.world.get::<&WlSurface>(key).ok()); + if let Some(s) = &s_surf { s } else { return; } }, id, x, y }, - Up { - serial, - time, - id - }, - Motion { - time, - id, - x, - y - }, + Up { serial, time, id }, + Motion { time, id, x, y }, Frame, Cancel, - Shape { - id, - major, - minor - }, - Orientation { - id, - orientation - } + Shape { id, major, minor }, + Orientation { id, orientation } ] } } } -pub struct XdgOutput { - pub client: ClientXdgOutput, - pub server: ServerXdgOutput, +pub(super) struct OnOutput(pub Entity); +struct OutputName(String); +fn get_output_name(output: Option<&OnOutput>, world: &World) -> Option { + output.map(|o| world.get::<&OutputName>(o.0).unwrap().0.clone()) } +pub(super) struct OutputScaleFactor(pub i32); + enum OutputDimensionsSource { // The data in this variant is the values needed for the wl_output.geometry event. Wl { @@ -791,49 +747,149 @@ pub(super) struct OutputDimensions { pub y: i32, pub width: i32, pub height: i32, + rotated_90: bool, } -pub struct Output { - pub client: client::wl_output::WlOutput, - pub server: WlOutput, - pub xdg: Option, - windows: HashSet, - pub(super) dimensions: OutputDimensions, - name: String, - scale: i32, - swap_dimensions: bool, -} - -impl Output { - pub fn new(client: client::wl_output::WlOutput, server: WlOutput) -> Self { +impl Default for OutputDimensions { + fn default() -> Self { Self { - client, - server, - xdg: None, - windows: HashSet::new(), - dimensions: OutputDimensions { - source: OutputDimensionsSource::Wl { - physical_height: 0, - physical_width: 0, - subpixel: WEnum::Value(client::wl_output::Subpixel::Unknown), - make: String::new(), - model: String::new(), - transform: WEnum::Value(client::wl_output::Transform::Normal), - }, - x: 0, - y: 0, - width: 0, - height: 0, + source: OutputDimensionsSource::Wl { + physical_height: 0, + physical_width: 0, + subpixel: WEnum::Value(client::wl_output::Subpixel::Unknown), + make: "".to_string(), + model: "".to_string(), + transform: WEnum::Value(client::wl_output::Transform::Normal), }, - name: "".to_string(), - scale: 1, - swap_dimensions: false, + x: 0, + y: 0, + width: 0, + height: 0, + rotated_90: false, + } + } +} + +fn update_output_offset( + output: Entity, + source: OutputDimensionsSource, + x: i32, + y: i32, + state: &mut ServerState, +) { + { + let mut dimensions = state.world.get::<&mut OutputDimensions>(output).unwrap(); + if matches!(source, OutputDimensionsSource::Wl { .. }) + && matches!(dimensions.source, OutputDimensionsSource::Xdg) + { + return; + } + + let global_offset = &mut state.global_output_offset; + let mut maybe_update_dimension = |value, dim: &mut GlobalOutputOffsetDimension| { + if value < dim.value { + *dim = GlobalOutputOffsetDimension { + owner: Some(output), + value, + }; + state.global_offset_updated = true; + } else if dim.owner == Some(output) && value > dim.value { + *dim = Default::default(); + state.global_offset_updated = true; + } + }; + + maybe_update_dimension(x, &mut global_offset.x); + maybe_update_dimension(y, &mut global_offset.y); + + dimensions.source = source; + dimensions.x = x; + dimensions.y = y; + debug!( + "moving {} to {x}x{y}", + state.world.get::<&WlOutput>(output).unwrap().id() + ); + } + + if let Some(connection) = state.connection.as_mut() { + update_window_output_offsets( + output, + &state.global_output_offset, + &state.world, + connection, + ); + } +} + +fn update_window_output_offsets( + output: Entity, + global_output_offset: &GlobalOutputOffset, + world: &World, + connection: &mut impl XConnection, +) { + let dimensions = world.get::<&OutputDimensions>(output).unwrap(); + let mut query = world.query::<(&x::Window, &mut WindowData, &OnOutput)>(); + + for (_, (window, data, _)) in query + .into_iter() + .filter(|(_, (_, _, on_output))| on_output.0 == output) + { + data.update_output_offset( + *window, + WindowOutputOffset { + x: dimensions.x - global_output_offset.x.value, + y: dimensions.y - global_output_offset.y.value, + }, + connection, + ); + } +} + +pub(super) fn update_global_output_offset( + output: Entity, + global_output_offset: &GlobalOutputOffset, + world: &World, + connection: &mut impl XConnection, +) { + let entity = world.entity(output).unwrap(); + let mut query = entity.query::<(&OutputDimensions, &WlOutput)>(); + let (dimensions, server) = query.get().unwrap(); + + let x = dimensions.x - global_output_offset.x.value; + let y = dimensions.y - global_output_offset.y.value; + + match &dimensions.source { + OutputDimensionsSource::Wl { + physical_width, + physical_height, + subpixel, + make, + model, + transform, + } => { + server.geometry( + x, + y, + *physical_width, + *physical_height, + convert_wenum(*subpixel), + make.clone(), + model.clone(), + convert_wenum(*transform), + ); + } + OutputDimensionsSource::Xdg => { + entity + .get::<&XdgOutputServer>() + .unwrap() + .logical_position(x, y); } } - pub(super) fn scale(&self) -> i32 { - self.scale - } + server.done(); + drop(query); + + update_window_output_offsets(output, global_output_offset, world, connection); } #[derive(Debug)] @@ -854,116 +910,19 @@ impl From for ObjectEvent { } } -impl HandleEvent for Output { - type Event = OutputEvent; - - fn handle_event(&mut self, event: Self::Event, state: &mut ServerState) { - match event { - OutputEvent::Xdg(event) => self.xdg_event(event, state), - OutputEvent::Wl(event) => self.wl_event(event, state), +impl Event for OutputEvent { + fn handle(self, target: Entity, state: &mut ServerState) { + match self { + OutputEvent::Xdg(event) => Self::xdg_event(event, target, state), + OutputEvent::Wl(event) => Self::wl_event(event, target, state), } } } -impl Output { - pub(super) fn global_offset_updated(&mut self, state: &mut ServerState) { - let x = self.dimensions.x - state.global_output_offset.x.value; - let y = self.dimensions.y - state.global_output_offset.y.value; - - match &self.dimensions.source { - OutputDimensionsSource::Wl { - physical_width, - physical_height, - subpixel, - make, - model, - transform, - } => { - self.server.geometry( - x, - y, - *physical_width, - *physical_height, - convert_wenum(*subpixel), - make.clone(), - model.clone(), - convert_wenum(*transform), - ); - } - OutputDimensionsSource::Xdg => { - self.xdg.as_ref().unwrap().server.logical_position(x, y); - } - } - self.server.done(); - - self.update_window_offsets(state); - } - - fn update_window_offsets(&mut self, state: &mut ServerState) { - self.windows.retain(|window| { - let Some(data): Option<&mut WindowData> = state.windows.get_mut(window) else { - return false; - }; - - data.update_output_offset( - self.server.data().copied().unwrap(), - WindowOutputOffset { - x: self.dimensions.x - state.global_output_offset.x.value, - y: self.dimensions.y - state.global_output_offset.y.value, - }, - state.connection.as_mut().unwrap(), - ); - - true - }); - } - - fn update_offset( - &mut self, - source: OutputDimensionsSource, - x: i32, - y: i32, - state: &mut ServerState, - ) { - if matches!(source, OutputDimensionsSource::Wl { .. }) - && matches!(self.dimensions.source, OutputDimensionsSource::Xdg) - { - return; - } - - let key: ObjectKey = self.server.data().copied().unwrap(); - let global_offset = &mut state.global_output_offset; - let mut maybe_update_dimension = |value, dim: &mut GlobalOutputOffsetDimension| { - if value < dim.value { - *dim = GlobalOutputOffsetDimension { - owner: Some(key), - value, - }; - state.global_offset_updated = true; - } else if dim.owner == Some(key) && value > dim.value { - *dim = Default::default(); - state.global_offset_updated = true; - } - }; - - maybe_update_dimension(x, &mut global_offset.x); - maybe_update_dimension(y, &mut global_offset.y); - - self.dimensions.source = source; - self.dimensions.x = x; - self.dimensions.y = y; - let id = match self.dimensions.source { - OutputDimensionsSource::Xdg => self.xdg.as_ref().unwrap().server.id(), - OutputDimensionsSource::Wl { .. } => self.server.id(), - }; - debug!("moving {id} to {x}x{y}"); - - self.update_window_offsets(state); - } - +impl OutputEvent { fn wl_event( - &mut self, event: client::wl_output::Event, + target: Entity, state: &mut ServerState, ) { use client::wl_output::Event; @@ -978,7 +937,8 @@ impl Output { model, transform, } => { - self.update_offset( + update_output_offset( + target, OutputDimensionsSource::Wl { physical_width, physical_height, @@ -991,7 +951,15 @@ impl Output { y, state, ); - self.server.geometry( + + let (output, dimensions, xdg) = state + .world + .query_one_mut::<(&WlOutput, &mut OutputDimensions, Option<&XdgOutputServer>)>( + target, + ) + .unwrap(); + + output.geometry( x - state.global_output_offset.x.value, y - state.global_output_offset.y.value, physical_width, @@ -1001,7 +969,7 @@ impl Output { model, convert_wenum(transform), ); - self.swap_dimensions = transform.into_result().is_ok_and(|t| { + dimensions.rotated_90 = transform.into_result().is_ok_and(|t| { matches!( t, client::wl_output::Transform::_90 @@ -1010,13 +978,11 @@ impl Output { | client::wl_output::Transform::Flipped270 ) }); - if let Some(xdg) = &self.xdg { - if self.swap_dimensions { - xdg.server - .logical_size(self.dimensions.height, self.dimensions.width); + if let Some(xdg) = xdg { + if dimensions.rotated_90 { + xdg.logical_size(dimensions.height, dimensions.width); } else { - xdg.server - .logical_size(self.dimensions.width, self.dimensions.height); + xdg.logical_size(dimensions.width, dimensions.height); } } } @@ -1026,50 +992,58 @@ impl Output { height, refresh, } => { + let (output, dimensions) = state + .world + .query_one_mut::<(&WlOutput, &mut OutputDimensions)>(target) + .unwrap(); + if flags .into_result() .is_ok_and(|f| f.contains(client::wl_output::Mode::Current)) { - self.dimensions.width = width; - self.dimensions.height = height; - debug!("{} dimensions: {width}x{height}", self.server.id()); + dimensions.width = width; + dimensions.height = height; + debug!("{} dimensions: {width}x{height}", output.id()); } - self.server - .mode(convert_wenum(flags), width, height, refresh); + output.mode(convert_wenum(flags), width, height, refresh); } Event::Scale { factor } => { - debug!("{} scale: {factor}", self.server.id()); - self.scale = factor; + debug!( + "{} scale: {factor}", + state.world.get::<&WlOutput>(target).unwrap().id() + ); + state.world.get::<&mut OutputScaleFactor>(target).unwrap().0 = factor; if state.fractional_scale.is_none() { - self.windows.retain(|window| { - let Some(data): Option<&WindowData> = state.windows.get(window) else { - return false; - }; + let mut surfaces = vec![]; + for (entity, (scale, _)) in state + .world + .query_mut::<(&mut SurfaceScaleFactor, &OnOutput)>() + .with::<&WindowData>() + .into_iter() + .filter(|(_, (_, o))| o.0 == target) + { + surfaces.push(entity); + scale.0 = factor as f64; + } + for entity in surfaces { + update_surface_viewport(state.world.query_one(entity).unwrap()); + } - if let Some::<&mut SurfaceData>(surface) = data - .surface_key - .and_then(|key| state.objects.get_mut(key)) - .map(AsMut::as_mut) - { - surface.scale_factor = factor as f64; - surface.update_viewport(data.attrs.dims, data.attrs.size_hints); - } - - true - }); - - self.server.scale(factor); + state.world.get::<&WlOutput>(target).unwrap().scale(factor); } state.output_scales_updated = true; } + Event::Name { name } => { + state + .world + .get::<&WlOutput>(target) + .unwrap() + .name(name.clone()); + state.world.insert(target, (OutputName(name),)).unwrap(); + } _ => simple_event_shunt! { - self.server, event: Event => [ - Name { - |name| { - self.name = name.clone(); - name - } - }, + state.world.get::<&WlOutput>(target).unwrap(), + event: client::wl_output::Event => [ Description { description }, Done ] @@ -1078,30 +1052,38 @@ impl Output { } fn xdg_event( - &mut self, event: zxdg_output_v1::Event, + target: Entity, state: &mut ServerState, ) { use zxdg_output_v1::Event; - let xdg = &self.xdg.as_ref().unwrap().server; match event { Event::LogicalPosition { x, y } => { - self.update_offset(OutputDimensionsSource::Xdg, x, y, state); - self.xdg.as_ref().unwrap().server.logical_position( - x - state.global_output_offset.x.value, - y - state.global_output_offset.y.value, - ); + update_output_offset(target, OutputDimensionsSource::Xdg, x, y, state); + state + .world + .get::<&XdgOutputServer>(target) + .unwrap() + .logical_position( + x - state.global_output_offset.x.value, + y - state.global_output_offset.y.value, + ); } Event::LogicalSize { .. } => { - if self.swap_dimensions { - xdg.logical_size(self.dimensions.height, self.dimensions.width); + let (xdg, dimensions) = state + .world + .query_one_mut::<(&XdgOutputServer, &OutputDimensions)>(target) + .unwrap(); + if dimensions.rotated_90 { + xdg.logical_size(dimensions.height, dimensions.width); } else { - xdg.logical_size(self.dimensions.width, self.dimensions.height); + xdg.logical_size(dimensions.width, dimensions.height); } } _ => simple_event_shunt! { - xdg, event: zxdg_output_v1::Event => [ + state.world.get::<&XdgOutputServer>(target).unwrap(), + event: zxdg_output_v1::Event => [ Done, Name { name }, Description { description } @@ -1111,13 +1093,11 @@ impl Output { } } -pub type Drm = GenericObject; -impl HandleEvent for Drm { - type Event = wl_drm::client::wl_drm::Event; - - fn handle_event(&mut self, event: Self::Event, _: &mut ServerState) { +impl Event for wl_drm::client::wl_drm::Event { + fn handle(self, target: Entity, state: &mut ServerState) { + let server = state.world.get::<&WlDrmServer>(target).unwrap(); simple_event_shunt! { - self.server, event: wl_drm::client::wl_drm::Event => [ + server, self => [ Device { name }, Format { format }, Authenticated, @@ -1127,16 +1107,14 @@ impl HandleEvent for Drm { } } -pub type DmabufFeedback = GenericObject< - s_dmabuf::zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1, - c_dmabuf::zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1, ->; -impl HandleEvent for DmabufFeedback { - type Event = c_dmabuf::zwp_linux_dmabuf_feedback_v1::Event; - - fn handle_event(&mut self, event: Self::Event, _: &mut ServerState) { +impl Event for c_dmabuf::zwp_linux_dmabuf_feedback_v1::Event { + fn handle(self, target: Entity, state: &mut ServerState) { + let server = state + .world + .get::<&s_dmabuf::zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1>(target) + .unwrap(); simple_event_shunt! { - self.server, event: c_dmabuf::zwp_linux_dmabuf_feedback_v1::Event => [ + server, self => [ Done, FormatTable { |fd| fd.as_fd(), size }, MainDevice { device }, @@ -1149,13 +1127,11 @@ impl HandleEvent for DmabufFeedback { } } -pub type RelativePointer = GenericObject; -impl HandleEvent for RelativePointer { - type Event = zwp_relative_pointer_v1::Event; - - fn handle_event(&mut self, event: Self::Event, _: &mut ServerState) { +impl Event for zwp_relative_pointer_v1::Event { + fn handle(self, target: Entity, state: &mut ServerState) { + let server = state.world.get::<&RelativePointerServer>(target).unwrap(); simple_event_shunt! { - self.server, event: zwp_relative_pointer_v1::Event => [ + server, self => [ RelativeMotion { utime_hi, utime_lo, @@ -1169,13 +1145,11 @@ impl HandleEvent for RelativePointer { } } -pub type LockedPointer = GenericObject; -impl HandleEvent for LockedPointer { - type Event = zwp_locked_pointer_v1::Event; - - fn handle_event(&mut self, event: Self::Event, _: &mut ServerState) { +impl Event for zwp_locked_pointer_v1::Event { + fn handle(self, target: Entity, state: &mut ServerState) { + let server = state.world.get::<&LockedPointerServer>(target).unwrap(); simple_event_shunt! { - self.server, event: zwp_locked_pointer_v1::Event => [ + server, self => [ Locked, Unlocked ] @@ -1183,13 +1157,11 @@ impl HandleEvent for LockedPointer { } } -pub type ConfinedPointer = GenericObject; -impl HandleEvent for ConfinedPointer { - type Event = zwp_confined_pointer_v1::Event; - - fn handle_event(&mut self, event: Self::Event, _: &mut ServerState) { +impl Event for zwp_confined_pointer_v1::Event { + fn handle(self, target: Entity, state: &mut ServerState) { + let server = state.world.get::<&ConfinedPointerServer>(target).unwrap(); simple_event_shunt! { - self.server, event: zwp_confined_pointer_v1::Event => [ + server, self => [ Confined, Unconfined ] @@ -1197,34 +1169,38 @@ impl HandleEvent for ConfinedPointer { } } -pub type TabletSeat = GenericObject; -impl HandleEvent for TabletSeat { - type Event = zwp_tablet_seat_v2::Event; - - fn handle_event(&mut self, event: Self::Event, state: &mut ServerState) { - simple_event_shunt! { - self.server, event: zwp_tablet_seat_v2::Event => [ - TabletAdded { - |id| &Tablet::from_client(id, state).server - }, - ToolAdded { - |id| &TabletTool::from_client(id, state).server - }, - PadAdded { - |id| &TabletPad::from_client(id, state).server - } - ] +impl Event for zwp_tablet_seat_v2::Event { + fn handle(self, target: Entity, state: &mut ServerState) { + let seat = state.world.get::<&TabletSeatServer>(target).unwrap(); + match self { + Self::TabletAdded { id } => { + let (e, tab) = from_client::(&id, state); + seat.tablet_added(&tab); + drop(seat); + state.world.spawn_at(e, (tab, id)); + } + Self::ToolAdded { id } => { + let (e, tool) = from_client::(&id, state); + seat.tool_added(&tool); + drop(seat); + state.world.spawn_at(e, (tool, id)); + } + Self::PadAdded { id } => { + let (e, pad) = from_client::(&id, state); + seat.pad_added(&pad); + drop(seat); + state.world.spawn_at(e, (pad, id)); + } + _ => log::warn!("unhandled {}: {self:?}", std::any::type_name::()), } } } -pub type Tablet = GenericObject; -impl HandleEvent for Tablet { - type Event = zwp_tablet_v2::Event; - - fn handle_event(&mut self, event: Self::Event, _: &mut ServerState) { +impl Event for zwp_tablet_v2::Event { + fn handle(self, target: Entity, state: &mut ServerState) { + let tab = state.world.get::<&TabletServer>(target).unwrap(); simple_event_shunt! { - self.server, event: zwp_tablet_v2::Event => [ + tab, self => [ Name { name }, Id { vid, pid }, Path { path }, @@ -1235,123 +1211,162 @@ impl HandleEvent for Tablet { } } -pub type TabletPad = GenericObject; -impl HandleEvent for TabletPad { - type Event = zwp_tablet_pad_v2::Event; - - fn handle_event(&mut self, event: Self::Event, state: &mut ServerState) { - simple_event_shunt! { - self.server, event: zwp_tablet_pad_v2::Event => [ - Group { |pad_group| &TabletPadGroup::from_client(pad_group, state).server }, - Path { path }, - Buttons { buttons }, - Done, - Button { - time, - button, - |state| convert_wenum(state) - }, - Enter { - serial, - |tablet| { - let key: &LateInitObjectKey = tablet.data().unwrap(); - let Some(tablet): Option<&Tablet> = state.objects.get(**key).map(|o| o.as_ref()) else { - return; - }; - &tablet.server +impl Event for zwp_tablet_pad_v2::Event { + fn handle(self, target: Entity, state: &mut ServerState) { + let pad = state.world.get::<&TabletPadServer>(target).unwrap(); + let s_surf; + match self { + Self::Group { pad_group } => { + let (e, s_group) = from_client::(&pad_group, state); + pad.group(&s_group); + drop(pad); + state.world.spawn_at(e, (pad_group, s_group)); + } + Self::Enter { + serial, + tablet, + surface, + } => { + let (e_tab, s_tablet) = from_client::(&tablet, state); + let Some(surface) = surface + .data() + .copied() + .and_then(|key| state.world.get::<&WlSurface>(key).ok()) + else { + return; + }; + pad.enter(serial, &s_tablet, &surface); + drop(pad); + drop(surface); + state.world.spawn_at(e_tab, (tablet, s_tablet)); + } + _ => simple_event_shunt! { + pad, self => [ + Path { path }, + Buttons { buttons }, + Done, + Button { + time, + button, + |state| convert_wenum(state) }, - |surface| { - let Some(surface_data) = state.get_server_surface_from_client(surface) else { - return; - }; - surface_data - } - }, - Leave { - serial, - |surface| { - let Some(surface_data) = state.get_server_surface_from_client(surface) else { - return; - }; - surface_data - } - }, - Removed - ] - } - } -} - -pub type TabletTool = GenericObject; -impl HandleEvent for TabletTool { - type Event = zwp_tablet_tool_v2::Event; - - fn handle_event(&mut self, event: Self::Event, state: &mut ServerState) { - simple_event_shunt! { - self.server, event: zwp_tablet_tool_v2::Event => [ - Type { |tool_type| convert_wenum(tool_type) }, - HardwareSerial { hardware_serial_hi, hardware_serial_lo }, - HardwareIdWacom { hardware_id_hi, hardware_id_lo }, - Capability { |capability| convert_wenum(capability) }, - Done, - Removed, - ProximityIn { - serial, - |tablet| { - let key: &LateInitObjectKey = tablet.data().unwrap(); - let Some(tablet): Option<&Tablet> = state.objects.get(**key).map(|o| o.as_ref()) else { - return; - }; - &tablet.server + Leave { + serial, + |surface| { + s_surf = surface.data().copied().and_then(|key| state.world.get::<&WlSurface>(key).ok()); + if let Some(s) = &s_surf { s } else { return; } + } }, - |surface| { - let Some(surface_data) = state.get_server_surface_from_client(surface) else { - return; - }; - surface_data - } - }, - ProximityOut, - Down { serial }, - Up, - Motion { x, y }, - Pressure { pressure }, - Tilt { tilt_x, tilt_y }, - Rotation { degrees }, - Slider { position }, - Wheel { degrees, clicks }, - Button { serial, button, |state| convert_wenum(state) }, - Frame { time }, - ] + Removed + ] + }, } } } -pub type TabletPadGroup = GenericObject; -impl HandleEvent for TabletPadGroup { - type Event = zwp_tablet_pad_group_v2::Event; - - fn handle_event(&mut self, event: Self::Event, state: &mut ServerState) { - simple_event_shunt! { - self.server, event: zwp_tablet_pad_group_v2::Event => [ - Buttons { buttons }, - Ring { |ring| &TabletPadRing::from_client(ring, state).server }, - Strip { |strip| &TabletPadStrip::from_client(strip, state).server }, - Modes { modes }, - Done, - ModeSwitch { time, serial, mode } - ] +impl Event for zwp_tablet_tool_v2::Event { + fn handle(self, target: Entity, state: &mut ServerState) { + let tool = state.world.get::<&TabletToolServer>(target).unwrap(); + match self { + Self::ProximityIn { + serial, + tablet, + surface, + } => { + let (e_tab, s_tablet) = from_client::(&tablet, state); + let Some(surface) = surface + .data() + .copied() + .and_then(|key| state.world.get::<&WlSurface>(key).ok()) + else { + return; + }; + tool.proximity_in(serial, &s_tablet, &surface); + drop(tool); + drop(surface); + state.world.spawn_at(e_tab, (tablet, s_tablet)); + } + _ => { + simple_event_shunt! { + tool, self => [ + Type { |tool_type| convert_wenum(tool_type) }, + HardwareSerial { hardware_serial_hi, hardware_serial_lo }, + HardwareIdWacom { hardware_id_hi, hardware_id_lo }, + Capability { |capability| convert_wenum(capability) }, + Done, + Removed, + ProximityOut, + Down { serial }, + Up, + Motion { x, y }, + Pressure { pressure }, + Tilt { tilt_x, tilt_y }, + Rotation { degrees }, + Slider { position }, + Wheel { degrees, clicks }, + Button { serial, button, |state| convert_wenum(state) }, + Frame { time }, + ] + } + } } } } -pub type TabletPadRing = GenericObject; -impl HandleEvent for TabletPadRing { - type Event = zwp_tablet_pad_ring_v2::Event; +#[must_use] +fn from_client( + client: &Client, + state: &ServerState, +) -> (Entity, Server) +where + Client::Event: Send + Into, + ServerState: wayland_server::Dispatch, +{ + let entity = state.world.reserve_entity(); + let server = state + .client + .as_ref() + .unwrap() + .create_resource::<_, _, ServerState>(&state.dh, 1, entity) + .unwrap(); + let obj_key: &LateInitObjectKey = client.data().unwrap(); + obj_key.init(entity); + (entity, server) +} - fn handle_event(&mut self, event: Self::Event, _: &mut ServerState) { +impl Event for zwp_tablet_pad_group_v2::Event { + fn handle(self, target: Entity, state: &mut ServerState) { + let group = state.world.get::<&TabletPadGroupServer>(target).unwrap(); + match self { + Self::Buttons { buttons } => group.buttons(buttons), + Self::Ring { ring } => { + let (e, s_ring) = from_client::(&ring, state); + group.ring(&s_ring); + drop(group); + state.world.spawn_at(e, (s_ring, ring)); + } + Self::Strip { strip } => { + let (e, s_strip) = from_client::(&strip, state); + group.strip(&s_strip); + drop(group); + state.world.spawn_at(e, (s_strip, strip)); + } + Self::Modes { modes } => group.modes(modes), + Self::ModeSwitch { time, serial, mode } => group.mode_switch(time, serial, mode), + Self::Done => group.done(), + _ => log::warn!( + "unhandled {} event: {self:?}", + std::any::type_name::() + ), + } + } +} + +impl Event for zwp_tablet_pad_ring_v2::Event { + fn handle(self, target: Entity, state: &mut ServerState) { + let ring = state.world.get::<&TabletPadRingServer>(target).unwrap(); simple_event_shunt! { - self.server, event: zwp_tablet_pad_ring_v2::Event => [ + ring, self => [ Source { |source| convert_wenum(source) }, Angle { degrees }, Stop, @@ -1361,13 +1376,11 @@ impl HandleEvent for TabletPadRing { } } -pub type TabletPadStrip = GenericObject; -impl HandleEvent for TabletPadStrip { - type Event = zwp_tablet_pad_strip_v2::Event; - - fn handle_event(&mut self, event: Self::Event, _: &mut ServerState) { +impl Event for zwp_tablet_pad_strip_v2::Event { + fn handle(self, target: Entity, state: &mut ServerState) { + let strip = state.world.get::<&TabletPadStripServer>(target).unwrap(); simple_event_shunt! { - self.server, event: zwp_tablet_pad_strip_v2::Event => [ + strip, self => [ Source { |source| convert_wenum(source) }, Position { position }, Stop, diff --git a/src/server/mod.rs b/src/server/mod.rs index c57f19c..34e1e61 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -1,16 +1,16 @@ +mod clientside; mod dispatch; mod event; - #[cfg(test)] mod tests; use self::event::*; -use crate::clientside::*; use crate::xstate::{Decorations, WindowDims, WmHints, WmName, WmNormalHints}; use crate::{X11Selection, XConnection}; +use clientside::MyWorld; +use hecs::{Entity, World}; use log::{debug, warn}; use rustix::event::{poll, PollFd, PollFlags}; -use slotmap::{new_key_type, HopSlotMap, SparseSecondaryMap}; use smithay_client_toolkit::activation::ActivationState; use smithay_client_toolkit::data_device_manager::{ data_device::DataDevice, data_offer::SelectionOffer, data_source::CopyPasteSource, @@ -21,8 +21,10 @@ use std::io::Read; use std::os::fd::{AsFd, BorrowedFd}; use std::os::unix::net::UnixStream; use std::rc::{Rc, Weak}; -use wayland_client::{globals::Global, protocol as client, Proxy}; -use wayland_protocols::wp::fractional_scale::v1::client::wp_fractional_scale_v1::WpFractionalScaleV1; +use wayland_client::{ + globals::{registry_queue_init, Global}, + protocol as client, Connection, EventQueue, Proxy, QueueHandle, +}; use wayland_protocols::xdg::decoration::zv1::client::zxdg_decoration_manager_v1::ZxdgDecorationManagerV1; use wayland_protocols::xdg::decoration::zv1::client::zxdg_toplevel_decoration_v1::{ self, ZxdgToplevelDecorationV1, @@ -31,8 +33,18 @@ use wayland_protocols::{ wp::{ fractional_scale::v1::client::wp_fractional_scale_manager_v1::WpFractionalScaleManagerV1, linux_dmabuf::zv1::{client as c_dmabuf, server as s_dmabuf}, - pointer_constraints::zv1::server::zwp_pointer_constraints_v1::ZwpPointerConstraintsV1, - relative_pointer::zv1::server::zwp_relative_pointer_manager_v1::ZwpRelativePointerManagerV1, + pointer_constraints::zv1::{ + client::{zwp_confined_pointer_v1, zwp_locked_pointer_v1}, + server::zwp_pointer_constraints_v1::ZwpPointerConstraintsV1, + }, + relative_pointer::zv1::{ + client::zwp_relative_pointer_v1, + server::zwp_relative_pointer_manager_v1::ZwpRelativePointerManagerV1, + }, + tablet::zv2::client::{ + zwp_tablet_pad_group_v2, zwp_tablet_pad_ring_v2, zwp_tablet_pad_strip_v2, + zwp_tablet_pad_v2, zwp_tablet_seat_v2, zwp_tablet_tool_v2, zwp_tablet_v2, + }, tablet::zv2::server::zwp_tablet_manager_v2::ZwpTabletManagerV2, viewporter::client::{wp_viewport::WpViewport, wp_viewporter::WpViewporter}, }, @@ -46,9 +58,7 @@ use wayland_protocols::{ }, xdg_output::zv1::server::zxdg_output_manager_v1::ZxdgOutputManagerV1, }, - xwayland::shell::v1::server::{ - xwayland_shell_v1::XwaylandShellV1, xwayland_surface_v1::XwaylandSurfaceV1, - }, + xwayland::shell::v1::server::xwayland_shell_v1::XwaylandShellV1, }; use wayland_server::protocol::wl_seat::WlSeat; use wayland_server::{ @@ -104,27 +114,15 @@ struct WindowOutputOffset { #[derive(Debug)] struct WindowData { - window: x::Window, - surface_serial: Option<[u32; 2]>, - surface_key: Option, mapped: bool, attrs: WindowAttributes, output_offset: WindowOutputOffset, - output_key: Option, activation_token: Option, } impl WindowData { - fn new( - window: x::Window, - override_redirect: bool, - dims: WindowDims, - activation_token: Option, - ) -> Self { + fn new(override_redirect: bool, dims: WindowDims, activation_token: Option) -> Self { Self { - window, - surface_key: None, - surface_serial: None, mapped: false, attrs: WindowAttributes { is_popup: override_redirect, @@ -132,22 +130,17 @@ impl WindowData { ..Default::default() }, output_offset: WindowOutputOffset::default(), - output_key: None, activation_token, } } fn update_output_offset( &mut self, - output_key: ObjectKey, + window: x::Window, offset: WindowOutputOffset, connection: &mut C, ) { log::trace!("offset: {offset:?}"); - if self.output_key != Some(output_key) { - self.output_key = Some(output_key); - } - if offset == self.output_offset { return; } @@ -158,7 +151,7 @@ impl WindowData { self.output_offset = offset; connection.set_window_dims( - self.window, + window, PendingSurfaceState { x: dims.x as i32, y: dims.y as i32, @@ -167,7 +160,7 @@ impl WindowData { }, ); - debug!("set {:?} offset to {:?}", self.window, self.output_offset); + debug!("set {:?} offset to {:?}", window, self.output_offset); } } @@ -177,72 +170,52 @@ struct SurfaceAttach { y: i32, } -pub struct SurfaceData { - client: client::wl_surface::WlSurface, - server: WlSurface, - key: ObjectKey, - serial: Option<[u32; 2]>, - frame_callback: Option, - attach: Option, - role: Option, - xwl: Option, - window: Option, - output_key: Option, - scale_factor: f64, - viewport: WpViewport, - fractional: Option, +#[derive(PartialEq, Eq)] +struct SurfaceSerial([u32; 2]); + +#[derive(Copy, Clone)] +struct SurfaceScaleFactor(f64); + +#[derive(Debug)] +enum SurfaceRole { + Toplevel(Option), + Popup(Option), } -impl SurfaceData { +impl SurfaceRole { fn xdg(&self) -> Option<&XdgSurfaceData> { - match self - .role - .as_ref() - .expect("Tried to get XdgSurface for surface without role") - { + match self { SurfaceRole::Toplevel(ref t) => t.as_ref().map(|t| &t.xdg), SurfaceRole::Popup(ref p) => p.as_ref().map(|p| &p.xdg), } } fn xdg_mut(&mut self) -> Option<&mut XdgSurfaceData> { - match self - .role - .as_mut() - .expect("Tried to get XdgSurface for surface without role") - { + match self { SurfaceRole::Toplevel(ref mut t) => t.as_mut().map(|t| &mut t.xdg), SurfaceRole::Popup(ref mut p) => p.as_mut().map(|p| &mut p.xdg), } } - fn destroy_role(&mut self) { - if let Some(role) = self.role.take() { - match role { - SurfaceRole::Toplevel(Some(mut t)) => { - if let Some(decoration) = t.decoration.take() { - decoration.destroy(); - } - t.toplevel.destroy(); - t.xdg.surface.destroy(); + fn destroy(&mut self) { + match self { + SurfaceRole::Toplevel(Some(ref mut t)) => { + if let Some(decoration) = t.decoration.take() { + decoration.destroy(); } - SurfaceRole::Popup(Some(p)) => { - p.positioner.destroy(); - p.popup.destroy(); - p.xdg.surface.destroy(); - } - _ => {} + t.toplevel.destroy(); + t.xdg.surface.destroy(); } + SurfaceRole::Popup(Some(p)) => { + p.positioner.destroy(); + p.popup.destroy(); + p.xdg.surface.destroy(); + } + _ => {} } } } -#[derive(Debug)] -enum SurfaceRole { - Toplevel(Option), - Popup(Option), -} - #[derive(Debug)] struct XdgSurfaceData { surface: XdgSurface, @@ -265,9 +238,8 @@ struct PopupData { xdg: XdgSurfaceData, } -pub(crate) trait HandleEvent { - type Event; - fn handle_event(&mut self, event: Self::Event, state: &mut ServerState); +trait Event { + fn handle(self, target: Entity, state: &mut ServerState); } macro_rules! enum_try_from { @@ -319,39 +291,26 @@ macro_rules! enum_try_from { } } -/// Implement HandleEvent for our enum -macro_rules! handle_event_enum { +macro_rules! impl_event { ( $(#[$meta:meta])* - $pub:vis enum $name:ident { + $pub:vis enum $enum:ident { $( $variant:ident($ty:ty) ),+ - } => $name_event:ident + } ) => { enum_try_from! { $(#[$meta])* - $pub enum $name { + $pub enum $enum { $( $variant($ty) ),+ } } - enum_try_from! { - #[derive(Debug)] - $pub enum $name_event { - $( $variant(<$ty as HandleEvent>::Event) ),+ - } - } - - impl HandleEvent for $name { - type Event = $name_event; - - fn handle_event(&mut self, event: Self::Event, state: &mut ServerState) { + impl Event for $enum { + fn handle(self, target: Entity, state: &mut ServerState) { match self { $( Self::$variant(v) => { - let Self::Event::$variant(event) = event else { - unreachable!(); - }; - v.handle_event(event, state) + v.handle(target, state) } ),+ } @@ -360,86 +319,28 @@ macro_rules! handle_event_enum { } } -handle_event_enum! { - -/// Objects that generate client side events that we will have to process. -pub(crate) enum Object { - Surface(SurfaceData), - Buffer(Buffer), - Seat(Seat), - Pointer(Pointer), - Keyboard(Keyboard), - Output(Output), - RelativePointer(RelativePointer), - DmabufFeedback(DmabufFeedback), - Drm(Drm), - Touch(Touch), - ConfinedPointer(ConfinedPointer), - LockedPointer(LockedPointer), - TabletSeat(TabletSeat), - Tablet(Tablet), - TabletTool(TabletTool), - TabletPad(TabletPad), - TabletPadGroup(TabletPadGroup), - TabletPadRing(TabletPadRing), - TabletPadStrip(TabletPadStrip) -} => ObjectEvent - +impl_event! { +enum ObjectEvent { + Surface(event::SurfaceEvents), + Buffer(client::wl_buffer::Event), + Seat(client::wl_seat::Event), + Pointer(client::wl_pointer::Event), + Keyboard(client::wl_keyboard::Event), + Touch(client::wl_touch::Event), + Output(event::OutputEvent), + Drm(wl_drm::client::wl_drm::Event), + DmabufFeedback(c_dmabuf::zwp_linux_dmabuf_feedback_v1::Event), + RelativePointer(zwp_relative_pointer_v1::Event), + LockedPointer(zwp_locked_pointer_v1::Event), + ConfinedPointer(zwp_confined_pointer_v1::Event), + TabletSeat(zwp_tablet_seat_v2::Event), + Tablet(zwp_tablet_v2::Event), + TabletPad(zwp_tablet_pad_v2::Event), + TabletTool(zwp_tablet_tool_v2::Event), + TabletPadGroup(zwp_tablet_pad_group_v2::Event), + TabletPadRing(zwp_tablet_pad_ring_v2::Event), + TabletPadStrip(zwp_tablet_pad_strip_v2::Event) } - -#[derive(Default)] -pub(crate) struct WrappedObject(Option); - -impl From for WrappedObject -where - T: Into, -{ - fn from(value: T) -> Self { - Self(Some(value.into())) - } -} - -impl AsRef for WrappedObject -where - for<'a> &'a T: TryFrom<&'a Object, Error = String>, -{ - fn as_ref(&self) -> &T { - <&T>::try_from(self.0.as_ref().unwrap()).unwrap() - } -} - -impl AsMut for WrappedObject -where - for<'a> &'a mut T: TryFrom<&'a mut Object, Error = String>, -{ - fn as_mut(&mut self) -> &mut T { - <&mut T>::try_from(self.0.as_mut().unwrap()).unwrap() - } -} - -type ObjectMap = HopSlotMap; -trait ObjectMapExt { - fn insert_from_other_objects(&mut self, keys: [ObjectKey; N], insert_fn: F) - where - F: FnOnce([&Object; N], ObjectKey) -> Object; -} - -impl ObjectMapExt for ObjectMap { - /// Insert an object into our map that needs some other values from our map as well - fn insert_from_other_objects(&mut self, keys: [ObjectKey; N], insert_fn: F) - where - F: FnOnce([&Object; N], ObjectKey) -> Object, - { - let objects = keys.each_ref().map(|key| self[*key].0.take().unwrap()); - let key = self.insert(WrappedObject(None)); - let obj = insert_fn(objects.each_ref(), key); - let ret = self[key].0.replace(obj); - debug_assert!(ret.is_none()); - for (object, key) in objects.into_iter().zip(keys.into_iter()) { - let ret = self[key].0.replace(object); - debug_assert!(ret.is_none()); - } - } } fn handle_globals<'a, C: XConnection>( @@ -448,18 +349,18 @@ fn handle_globals<'a, C: XConnection>( ) { for global in globals { macro_rules! server_global { - ($($global:ty),+) => { - match global.interface { - $( - ref x if x == <$global>::interface().name => { - let version = u32::min(global.version, <$global>::interface().version); - dh.create_global::, $global, Global>(version, global.clone()); - } - )+ - _ => {} - } + ($($global:ty),+) => { + match global.interface { + $( + ref x if x == <$global>::interface().name => { + let version = u32::min(global.version, <$global>::interface().version); + dh.create_global::, $global, Global>(version, global.clone()); + } + )+ + _ => {} } } + } server_global![ WlCompositor, @@ -476,10 +377,6 @@ fn handle_globals<'a, C: XConnection>( } } -new_key_type! { - pub struct ObjectKey; -} - struct FocusData { window: x::Window, output_name: Option, @@ -487,7 +384,7 @@ struct FocusData { #[derive(Copy, Clone, Default)] struct GlobalOutputOffsetDimension { - owner: Option, + owner: Option, value: i32, } @@ -499,14 +396,12 @@ struct GlobalOutputOffset { pub struct ServerState { dh: DisplayHandle, - clientside: ClientState, - objects: ObjectMap, - associated_windows: SparseSecondaryMap, - output_keys: SparseSecondaryMap, - windows: HashMap, + windows: HashMap, pids: HashSet, - qh: ClientQueueHandle, + world: MyWorld, + queue: EventQueue, + qh: QueueHandle, client: Option, to_focus: Option, unfocus: bool, @@ -517,6 +412,7 @@ pub struct ServerState { xdg_wm_base: XdgWmBase, viewporter: WpViewporter, fractional_scale: Option, + decoration_manager: Option, clipboard_data: Option>, last_kb_serial: Option<(client::wl_seat::WlSeat, u32)>, activation_state: Option, @@ -524,16 +420,20 @@ pub struct ServerState { global_offset_updated: bool, output_scales_updated: bool, new_scale: Option, - decoration_manager: Option, } impl ServerState { pub fn new(dh: DisplayHandle, server_connection: Option) -> Self { - let clientside = ClientState::new(server_connection); - let qh = clientside.qh.clone(); + let connection = if let Some(stream) = server_connection { + Connection::from_socket(stream).unwrap() + } else { + Connection::connect_to_env().unwrap() + }; - let xdg_wm_base = clientside - .global_list + let (global_list, queue) = registry_queue_init::(&connection).unwrap(); + let qh = queue.handle(); + + let xdg_wm_base = global_list .bind::(&qh, 2..=6, ()) .expect("Could not bind xdg_wm_base"); @@ -541,16 +441,15 @@ impl ServerState { warn!("xdg_wm_base version 2 detected. Popup repositioning will not work, and some popups may not work correctly."); } - let viewporter = clientside - .global_list + let viewporter = global_list .bind::(&qh, 1..=1, ()) .expect("Could not bind wp_viewporter"); - let fractional_scale = clientside.global_list.bind::(&qh, 1..=1, ()) + let fractional_scale = global_list.bind::(&qh, 1..=1, ()) .inspect_err(|e| warn!("Couldn't bind fractional scale manager: {e}. Fractional scaling will not work.")) .ok(); - let manager = DataDeviceManagerState::bind(&clientside.global_list, &qh) + let manager = DataDeviceManagerState::bind(&global_list, &qh) .inspect_err(|e| { warn!("Could not bind data device manager ({e:?}). Clipboard will not work.") }) @@ -561,14 +460,13 @@ impl ServerState { source: None::>, }); - let activation_state = ActivationState::bind(&clientside.global_list, &qh) + let activation_state = ActivationState::bind(&global_list, &qh) .inspect_err(|e| { warn!("Could not bind xdg activation ({e:?}). Windows might not receive focus depending on compositor focus stealing policy.") }) .ok(); - let decoration_manager = clientside - .global_list + let decoration_manager = global_list .bind::(&qh, 1..=1, ()) .inspect_err(|e| { warn!("Could not bind xdg decoration ({e:?}). Windows might not have decorations.") @@ -576,16 +474,15 @@ impl ServerState { .ok(); dh.create_global::(1, ()); - clientside - .global_list + global_list .contents() .with_list(|globals| handle_globals::(&dh, globals)); Self { windows: HashMap::new(), pids: HashSet::new(), - clientside, client: None, + queue, qh, dh, to_focus: None, @@ -593,9 +490,6 @@ impl ServerState { last_focused_toplevel: None, last_hovered: None, connection: None, - objects: Default::default(), - output_keys: Default::default(), - associated_windows: Default::default(), xdg_wm_base, viewporter, fractional_scale, @@ -616,11 +510,12 @@ impl ServerState { output_scales_updated: false, new_scale: None, decoration_manager, + world: MyWorld::new(global_list), } } pub fn clientside_fd(&self) -> BorrowedFd<'_> { - self.clientside.queue.as_fd() + self.queue.as_fd() } pub fn connect(&mut self, connection: UnixStream) { @@ -636,7 +531,7 @@ impl ServerState { } fn handle_new_globals(&mut self) { - let globals = std::mem::take(&mut self.clientside.globals.new_globals); + let globals = std::mem::take(&mut self.world.new_globals); handle_globals::(&self.dh, globals.iter()); } @@ -656,27 +551,41 @@ impl ServerState { .find_map(|line| line.strip_prefix(b"XDG_ACTIVATION_TOKEN=")) .and_then(|token| String::from_utf8(token.to_vec()).ok()) }); - self.windows.insert( + + let id = self.world.spawn(( window, - WindowData::new(window, override_redirect, dims, activation_token), - ); + WindowData::new(override_redirect, dims, activation_token), + )); + + self.windows.insert(window, id); } pub fn set_popup(&mut self, window: x::Window, is_popup: bool) { - let Some(win) = self.windows.get_mut(&window) else { + let Some(id) = self.windows.get(&window).copied() else { debug!("not setting popup for unknown window {window:?}"); return; }; - win.attrs.is_popup = is_popup; + self.world + .get::<&mut WindowData>(id) + .unwrap() + .attrs + .is_popup = is_popup; } pub fn set_win_title(&mut self, window: x::Window, name: WmName) { - let Some(win) = self.windows.get_mut(&window) else { + let Some(data) = self + .windows + .get(&window) + .copied() + .and_then(|id| self.world.entity(id).ok()) + else { debug!("not setting title for unknown window {window:?}"); return; }; + let mut win = data.get::<&mut WindowData>().unwrap(); + let new_title = match &mut win.attrs.title { Some(w) => { if matches!(w, WmName::NetWmName(_)) && matches!(name, WmName::WmName(_)) { @@ -694,72 +603,72 @@ impl ServerState { let Some(title) = new_title else { return; }; - if let Some(key) = win.surface_key { - if let Some(object) = self.objects.get(key) { - let surface: &SurfaceData = object.as_ref(); - if let Some(SurfaceRole::Toplevel(Some(data))) = &surface.role { - data.toplevel.set_title(title.name().to_string()); - } - } else { - warn!("could not set window title: stale surface"); + + if let Some(role) = data.get::<&SurfaceRole>() { + if let SurfaceRole::Toplevel(Some(data)) = &*role { + data.toplevel.set_title(title.name().to_string()); } } } pub fn set_win_class(&mut self, window: x::Window, class: String) { - let Some(win) = self.windows.get_mut(&window) else { + let Some(data) = self + .windows + .get(&window) + .copied() + .and_then(|id| self.world.entity(id).ok()) + else { debug!("not setting class for unknown window {window:?}"); return; }; + let mut win = data.get::<&mut WindowData>().unwrap(); + let class = win.attrs.class.insert(class); - if let Some(key) = win.surface_key { - if let Some(object) = self.objects.get(key) { - let surface: &SurfaceData = object.as_ref(); - if let Some(SurfaceRole::Toplevel(Some(data))) = &surface.role { - data.toplevel.set_app_id(class.to_string()); - } - } else { - warn!("could not set window class: stale surface"); + if let Some(role) = data.get::<&SurfaceRole>() { + if let SurfaceRole::Toplevel(Some(data)) = &*role { + data.toplevel.set_app_id(class.to_string()); } } } pub fn set_win_hints(&mut self, window: x::Window, hints: WmHints) { - let Some(win) = self.windows.get_mut(&window) else { + let Some(id) = self.windows.get(&window).copied() else { debug!("not setting hints for unknown window {window:?}"); return; }; - win.attrs.group = hints.window_group; + + self.world.get::<&mut WindowData>(id).unwrap().attrs.group = hints.window_group; } pub fn set_size_hints(&mut self, window: x::Window, hints: WmNormalHints) { - let Some(win) = self.windows.get_mut(&window) else { + let Some(data) = self + .windows + .get(&window) + .copied() + .and_then(|id| self.world.entity(id).ok()) + else { debug!("not setting size hints for unknown window {window:?}"); return; }; + let mut win = data.get::<&mut WindowData>().unwrap(); + if win.attrs.size_hints.is_none() || *win.attrs.size_hints.as_ref().unwrap() != hints { debug!("setting {window:?} hints {hints:?}"); - if let Some(key) = win.surface_key { - if let Some(object) = self.objects.get(key) { - let surface: &SurfaceData = object.as_ref(); - if let Some(SurfaceRole::Toplevel(Some(data))) = &surface.role { - if let Some(min_size) = &hints.min_size { - data.toplevel.set_min_size( - (min_size.width as f64 / surface.scale_factor) as i32, - (min_size.height as f64 / surface.scale_factor) as i32, - ); - } - if let Some(max_size) = &hints.max_size { - data.toplevel.set_max_size( - (max_size.width as f64 / surface.scale_factor) as i32, - (max_size.height as f64 / surface.scale_factor) as i32, - ); - } - } - } else { - warn!("could not set size hint on {window:?}: stale surface") + let mut query = data.query::<(&SurfaceRole, &SurfaceScaleFactor)>(); + if let Some((SurfaceRole::Toplevel(Some(data)), scale_factor)) = query.get() { + if let Some(min_size) = &hints.min_size { + data.toplevel.set_min_size( + (min_size.width as f64 / scale_factor.0) as i32, + (min_size.height as f64 / scale_factor.0) as i32, + ); + } + if let Some(max_size) = &hints.max_size { + data.toplevel.set_max_size( + (max_size.width as f64 / scale_factor.0) as i32, + (max_size.height as f64 / scale_factor.0) as i32, + ); } } win.attrs.size_hints = Some(hints); @@ -771,24 +680,26 @@ impl ServerState { return; }; - let Some(win) = self.windows.get_mut(&window) else { + let Some(data) = self + .windows + .get(&window) + .copied() + .and_then(|id| self.world.entity(id).ok()) + else { debug!("not setting decorations for unknown window {window:?}"); return; }; + let mut win = data.get::<&mut WindowData>().unwrap(); + if win.attrs.decorations != Some(decorations) { debug!("setting {window:?} decorations {decorations:?}"); - if let Some(key) = win.surface_key { - if let Some(object) = self.objects.get(key) { - let surface: &SurfaceData = object.as_ref(); - if let Some(SurfaceRole::Toplevel(Some(data))) = &surface.role { - data.decoration - .as_ref() - .unwrap() - .set_mode(decorations.into()); - } - } else { - warn!("could not set decorations on {window:?}: stale surface") + if let Some(role) = data.get::<&SurfaceRole>() { + if let SurfaceRole::Toplevel(Some(data)) = &*role { + data.decoration + .as_ref() + .unwrap() + .set_mode(decorations.into()); } } win.attrs.decorations = Some(decorations); @@ -796,15 +707,22 @@ impl ServerState { } pub fn set_window_serial(&mut self, window: x::Window, serial: [u32; 2]) { - let Some(win) = self.windows.get_mut(&window) else { + let Some(id) = self.windows.get(&window).copied() else { warn!("Tried to set serial for unknown window {window:?}"); return; }; - win.surface_serial = Some(serial); + + self.world.insert(id, (SurfaceSerial(serial),)).unwrap(); } pub fn can_change_position(&self, window: x::Window) -> bool { - let Some(win) = self.windows.get(&window) else { + let Some(win) = self + .windows + .get(&window) + .copied() + .and_then(|id| self.world.entity(id).ok()) + .map(|data| data.get::<&WindowData>().unwrap()) + else { return true; }; @@ -812,10 +730,18 @@ impl ServerState { } pub fn reconfigure_window(&mut self, event: x::ConfigureNotifyEvent) { - let Some(win) = self.windows.get_mut(&event.window()) else { + let Some(data) = self + .windows + .get(&event.window()) + .copied() + .and_then(|id| self.world.entity(id).ok()) + else { debug!("not reconfiguring unknown window {:?}", event.window()); return; }; + + let mut win = data.get::<&mut WindowData>().unwrap(); + let dims = WindowDims { x: event.x(), y: event.y(), @@ -835,31 +761,29 @@ impl ServerState { return; } - let Some(key) = win.surface_key else { + let mut query = data.query::<(&SurfaceRole, &SurfaceScaleFactor)>(); + let Some((role, scale_factor)) = query.get() else { return; }; - let Some(data): Option<&mut SurfaceData> = self.objects.get_mut(key).map(|o| o.as_mut()) - else { - return; - }; - - match &data.role { - Some(SurfaceRole::Popup(Some(popup))) => { + match role { + SurfaceRole::Popup(Some(popup)) => { popup.positioner.set_offset( - ((event.x() as i32 - win.output_offset.x) as f64 / data.scale_factor) as i32, - ((event.y() as i32 - win.output_offset.y) as f64 / data.scale_factor) as i32, + ((event.x() as i32 - win.output_offset.x) as f64 / scale_factor.0) as i32, + ((event.y() as i32 - win.output_offset.y) as f64 / scale_factor.0) as i32, ); popup.positioner.set_size( - 1.max((event.width() as f64 / data.scale_factor) as i32), - 1.max((event.height() as f64 / data.scale_factor) as i32), + 1.max((event.width() as f64 / scale_factor.0) as i32), + 1.max((event.height() as f64 / scale_factor.0) as i32), ); popup.popup.reposition(&popup.positioner, 0); } - Some(SurfaceRole::Toplevel(Some(_))) => { + SurfaceRole::Toplevel(Some(_)) => { win.attrs.dims.width = dims.width; win.attrs.dims.height = dims.height; - data.update_viewport(win.attrs.dims, win.attrs.size_hints); + drop(query); + drop(win); + update_surface_viewport(self.world.query_one(data.entity()).unwrap()); } other => warn!("Non popup ({other:?}) being reconfigured, behavior may be off."), } @@ -868,55 +792,65 @@ impl ServerState { pub fn map_window(&mut self, window: x::Window) { debug!("mapping {window:?}"); - let Some(window) = self.windows.get_mut(&window) else { + let Some(mut win) = self + .windows + .get(&window) + .copied() + .and_then(|id| self.world.entity(id).ok()) + .map(|data| data.get::<&mut WindowData>().unwrap()) + else { debug!("not mapping unknown window {window:?}"); return; }; - window.mapped = true; + + win.mapped = true; } pub fn unmap_window(&mut self, window: x::Window) { - let Some(win) = self.windows.get_mut(&window) else { - return; - }; - if !win.mapped { - return; - } - debug!("unmapping {window:?}"); + let entity = self.windows.get(&window).copied(); - if matches!(self.last_focused_toplevel, Some(x) if x == window) { - self.last_focused_toplevel.take(); - } - if self.last_hovered == Some(window) { - self.last_hovered.take(); - } - win.mapped = false; - - if let Some(key) = win.surface_key.take() { - let Some(object) = self.objects.get_mut(key) else { - warn!("could not unmap {window:?}: stale surface"); + { + let Some(data) = entity.and_then(|id| self.world.entity(id).ok()) else { return; }; - let surface: &mut SurfaceData = object.as_mut(); - surface.destroy_role(); + + let mut win = data.get::<&mut WindowData>().unwrap(); + if !win.mapped { + return; + } + debug!("unmapping {window:?}"); + + if matches!(self.last_focused_toplevel, Some(x) if x == window) { + self.last_focused_toplevel.take(); + } + if self.last_hovered == Some(window) { + self.last_hovered.take(); + } + win.mapped = false; + } + + if let Ok(mut role) = self.world.remove_one::(entity.unwrap()) { + role.destroy(); } } pub fn set_fullscreen(&mut self, window: x::Window, state: super::xstate::SetState) { - let Some(win) = self.windows.get(&window) else { + let Some(data) = self + .windows + .get(&window) + .copied() + .and_then(|id| self.world.entity(id).ok()) + else { warn!("Tried to set unknown window {window:?} fullscreen"); return; }; - let Some(key) = win.surface_key else { - warn!("Tried to set window without surface fullscreen: {window:?}"); + + let Some(role) = data.get::<&SurfaceRole>() else { + warn!("Tried to set window without role fullscreen: {window:?}"); return; }; - let Some(object) = self.objects.get_mut(key) else { - warn!("Could not set fullscreen on {window:?}: stale surface"); - return; - }; - let surface: &mut SurfaceData = object.as_mut(); - let Some(SurfaceRole::Toplevel(Some(ref toplevel))) = surface.role else { + + let SurfaceRole::Toplevel(Some(ref toplevel)) = &*role else { warn!("Tried to set an unmapped toplevel or non toplevel fullscreen: {window:?}"); return; }; @@ -936,7 +870,13 @@ impl ServerState { } pub fn set_transient_for(&mut self, window: x::Window, parent: x::Window) { - let Some(win) = self.windows.get_mut(&window) else { + let Some(mut win) = self + .windows + .get(&window) + .copied() + .and_then(|id| self.world.entity(id).ok()) + .map(|data| data.get::<&mut WindowData>().unwrap()) + else { return; }; @@ -952,34 +892,41 @@ impl ServerState { warn!("No last focused toplevel, cannot focus window {window:?}"); return; }; - let Some(win) = self.windows.get(&last_focused_toplevel) else { + + let Some(data) = self + .windows + .get(&last_focused_toplevel) + .copied() + .and_then(|id| self.world.entity(id).ok()) + else { warn!("Unknown last focused toplevel, cannot focus window {window:?}"); return; }; - let Some(key) = win.surface_key else { + + let Some(surface) = data.get::<&client::wl_surface::WlSurface>() else { warn!("Last focused toplevel has no surface, cannot focus window {window:?}"); return; }; - let Some(object) = self.objects.get_mut(key) else { - warn!("Last focused toplevel has stale reference, cannot focus window {window:?}"); - return; - }; - let surface: &mut SurfaceData = object.as_mut(); activation_state.request_token_with_data( &self.qh, - xdg_activation::ActivationData::new( + clientside::ActivationData::new( window, smithay_client_toolkit::activation::RequestData { - app_id: win.attrs.class.clone(), + app_id: data.get::<&WindowData>().unwrap().attrs.class.clone(), seat_and_serial: self.last_kb_serial.clone(), - surface: Some(surface.client.clone()), + surface: Some((*surface).clone()), }, ), ); } pub fn destroy_window(&mut self, window: x::Window) { - let _ = self.windows.remove(&window); + if let Some(id) = self.windows.remove(&window) { + self.world.remove::<(x::Window, WindowData)>(id).unwrap(); + if self.world.entity(id).unwrap().is_empty() { + self.world.despawn(id).unwrap(); + } + } } pub(crate) fn set_copy_paste_source(&mut self, selection: &Rc) { @@ -1006,16 +953,15 @@ impl ServerState { } pub fn run(&mut self) { - if let Some(r) = self.clientside.queue.prepare_read() { + if let Some(r) = self.queue.prepare_read() { let fd = r.connection_fd(); let pollfd = PollFd::new(&fd, PollFlags::IN); if poll(&mut [pollfd], 0).unwrap() > 0 { let _ = r.read(); } } - self.clientside - .queue - .dispatch_pending(&mut self.clientside.globals) + self.queue + .dispatch_pending(&mut self.world) .expect("Failed dispatching client side Wayland events"); self.handle_clientside_events(); } @@ -1023,16 +969,12 @@ impl ServerState { pub fn handle_clientside_events(&mut self) { self.handle_new_globals(); - for (key, event) in self.clientside.read_events() { - let Some(object) = &mut self.objects.get_mut(key) else { + for (target, event) in self.world.read_events() { + if !self.world.contains(target) { warn!("could not handle clientside event: stale object"); continue; - }; - let mut object = object.0.take().unwrap(); - object.handle_event(event, self); - - let ret = self.objects[key].0.replace(object); // safe indexed access? - debug_assert!(ret.is_none()); + } + event.handle(target, self); } if self.global_offset_updated { @@ -1046,18 +988,13 @@ impl ServerState { "updated global output offset: {}x{}", self.global_output_offset.x.value, self.global_output_offset.y.value ); - for (key, _) in self.output_keys.clone() { - let Some(object) = &mut self.objects.get_mut(key) else { - continue; - }; - let mut output: Output = object - .0 - .take() - .expect("Output object missing?") - .try_into() - .expect("Not an output?"); - output.global_offset_updated(self); - self.objects[key].0.replace(output.into()); + for (e, _) in self.world.query::<&WlOutput>().iter() { + event::update_global_output_offset( + e, + &self.global_output_offset, + &self.world, + self.connection.as_mut().unwrap(), + ); } self.global_offset_updated = false; } @@ -1066,35 +1003,25 @@ impl ServerState { let mut mixed_scale = false; let mut scale; - 'b: { - let mut keys_iter = self.output_keys.iter(); - let (key, _) = keys_iter.next().unwrap(); - let Some::<&Output>(output) = &mut self.objects.get(key).map(AsRef::as_ref) else { - // This should never happen, but you never know... - break 'b; - }; + let mut outputs = self.world.query_mut::<&OutputScaleFactor>().into_iter(); + let (_, output_scale) = outputs.next().unwrap(); - scale = output.scale(); + scale = output_scale.0; - for (key, _) in keys_iter { - let Some::<&Output>(output) = self.objects.get(key).map(AsRef::as_ref) else { - continue; - }; - - if output.scale() != scale { - mixed_scale = true; - scale = scale.min(output.scale()); - } + for (_, output_scale) in outputs { + if output_scale.0 != scale { + mixed_scale = true; + scale = scale.min(output_scale.0); } - - if mixed_scale { - warn!("Mixed output scales detected, choosing to give apps the smallest detected scale ({scale}x)"); - } - - debug!("Using new scale {scale}"); - self.new_scale = Some(scale); } + if mixed_scale { + warn!("Mixed output scales detected, choosing to give apps the smallest detected scale ({scale}x)"); + } + + debug!("Using new scale {scale}"); + self.new_scale = Some(scale); + self.output_scales_updated = false; } @@ -1117,8 +1044,7 @@ impl ServerState { self.handle_clipboard_events(); self.handle_activations(); - self.clientside - .queue + self.queue .flush() .expect("Failed flushing clientside events"); } @@ -1140,10 +1066,8 @@ impl ServerState { } fn handle_clipboard_events(&mut self) { - let globals = &mut self.clientside.globals; - if let Some(clipboard) = self.clipboard_data.as_mut() { - for (mime_type, fd) in std::mem::take(&mut globals.selection_requests) { + for (mime_type, fd) in std::mem::take(&mut self.world.selection_requests) { let CopyPasteData::X11 { data, .. } = clipboard.source.as_ref().unwrap() else { unreachable!("Got selection request without having set the selection?") }; @@ -1152,8 +1076,8 @@ impl ServerState { } } - if clipboard.source.is_none() || globals.cancelled { - if globals.selection.take().is_some() { + if clipboard.source.is_none() || self.world.selection_cancelled { + if self.world.selection.take().is_some() { let device = clipboard.device.as_ref().unwrap(); let offer = device.data().selection_offer().unwrap(); let mime_types: Box<[String]> = offer.with_mime_types(|mimes| mimes.into()); @@ -1163,7 +1087,7 @@ impl ServerState { }; clipboard.source = Some(CopyPasteData::Foreign(foreign)); } - globals.cancelled = false; + self.world.selection_cancelled = false; } } } @@ -1172,17 +1096,15 @@ impl ServerState { let Some(activation_state) = self.activation_state.as_ref() else { return; }; - let globals = &mut self.clientside.globals; - globals.pending_activations.retain(|(window, token)| { - if let Some(surface) = self - .windows - .get(window) - .and_then(|window| window.surface_key) - .and_then(|key| self.objects.get(key)) - .map(AsRef::::as_ref) - { - activation_state.activate::(&surface.client, token.clone()); + self.world.pending_activations.retain(|(window, token)| { + if let Some(surface) = self.windows.get(window).copied().and_then(|id| { + self.world + .world + .get::<&client::wl_surface::WlSurface>(id) + .ok() + }) { + activation_state.activate::(&surface, token.clone()); return false; } true @@ -1190,97 +1112,98 @@ impl ServerState { } fn calc_global_output_offset(&mut self) { - for (key, _) in &self.output_keys { - let Some(object) = &self.objects.get(key) else { - continue; - }; - - let output: &Output = object.as_ref(); - if output.dimensions.x < self.global_output_offset.x.value { + for (entity, dimensions) in self.world.query_mut::<&OutputDimensions>() { + if dimensions.x < self.global_output_offset.x.value { self.global_output_offset.x = GlobalOutputOffsetDimension { - owner: Some(key), - value: output.dimensions.x, + owner: Some(entity), + value: dimensions.x, } } - if output.dimensions.y < self.global_output_offset.y.value { + if dimensions.y < self.global_output_offset.y.value { self.global_output_offset.y = GlobalOutputOffsetDimension { - owner: Some(key), - value: output.dimensions.y, + owner: Some(entity), + value: dimensions.y, } } } } - fn create_role_window(&mut self, window: x::Window, surface_key: ObjectKey) { - let surface: &mut SurfaceData = self.objects.get_mut(surface_key).unwrap().as_mut(); - surface.window = Some(window); - surface.client.attach(None, 0, 0); - surface.client.commit(); - - let xdg_surface = self - .xdg_wm_base - .get_xdg_surface(&surface.client, &self.qh, surface_key); - - // Temporarily remove to placate borrow checker - let window_data = self.windows.remove(&window).unwrap(); - + fn create_role_window(&mut self, window: x::Window, entity: Entity) { + let xdg_surface; let mut popup_for = None; - if window_data.attrs.is_popup { - popup_for = self.last_hovered.or(self.last_focused_toplevel); - } - let mut fullscreen = false; - let (width, height) = (window_data.attrs.dims.width, window_data.attrs.dims.height); - for (key, _) in &self.output_keys { - let output: &Output = self.objects[key].as_ref(); - if output.dimensions.width == width as i32 && output.dimensions.height == height as i32 - { - fullscreen = true; - popup_for = None; + + { + let data = self.world.entity(entity).unwrap(); + let surface = data.get::<&client::wl_surface::WlSurface>().unwrap(); + surface.attach(None, 0, 0); + surface.commit(); + + xdg_surface = self.xdg_wm_base.get_xdg_surface(&surface, &self.qh, entity); + + let window_data = data.get::<&WindowData>().unwrap(); + if window_data.attrs.is_popup { + popup_for = self.last_hovered.or(self.last_focused_toplevel); + } + + let (width, height) = (window_data.attrs.dims.width, window_data.attrs.dims.height); + for (_, dimensions) in self.world.query::<&OutputDimensions>().iter() { + if dimensions.width == width as i32 && dimensions.height == height as i32 { + fullscreen = true; + popup_for = None; + break; + } } } let initial_scale; let role = if let Some(parent) = popup_for { let data; - (initial_scale, data) = - self.create_popup(&window_data, surface_key, xdg_surface, parent); + (initial_scale, data) = self.create_popup(entity, xdg_surface, parent); SurfaceRole::Popup(Some(data)) } else { initial_scale = 1.0; - let data = self.create_toplevel(&window_data, surface_key, xdg_surface, fullscreen); + let data = self.create_toplevel(entity, xdg_surface, fullscreen); SurfaceRole::Toplevel(Some(data)) }; - let surface: &mut SurfaceData = self.objects[surface_key].as_mut(); - surface.scale_factor = initial_scale; + let (surface_role, scale_factor, client) = self + .world + .query_one_mut::<( + Option<&SurfaceRole>, + &mut SurfaceScaleFactor, + &client::wl_surface::WlSurface, + )>(entity) + .unwrap(); + scale_factor.0 = initial_scale; let new_role_type = std::mem::discriminant(&role); - let prev = surface.role.replace(role); - if let Some(role) = prev { - let old_role_type = std::mem::discriminant(&role); + if let Some(role) = surface_role { + let old_role_type = std::mem::discriminant(role); assert_eq!( new_role_type, old_role_type, "Surface for {:?} already had a role: {:?}", - window_data.window, role + window, role ); } - surface.client.commit(); - // Reinsert - self.windows.insert(window, window_data); + client.commit(); + self.world.insert(entity, (role,)).unwrap(); } fn create_toplevel( &mut self, - window: &WindowData, - surface_key: ObjectKey, + entity: Entity, xdg: XdgSurface, fullscreen: bool, ) -> ToplevelData { - debug!("creating toplevel for {:?}", window.window); + let window = self.world.get::<&WindowData>(entity).unwrap(); + debug!( + "creating toplevel for {:?}", + *self.world.get::<&x::Window>(entity).unwrap() + ); - let toplevel = xdg.get_toplevel(&self.qh, surface_key); + let toplevel = xdg.get_toplevel(&self.qh, entity); if let Some(hints) = &window.attrs.size_hints { if let Some(min) = &hints.min_size { toplevel.set_min_size(min.width, min.height); @@ -1290,12 +1213,15 @@ impl ServerState { } } - let group = window.attrs.group.and_then(|win| self.windows.get(&win)); + let group = window.attrs.group.and_then(|win| { + let id = self.windows.get(&win).copied()?; + Some(self.world.get::<&WindowData>(id).unwrap()) + }); if let Some(class) = window .attrs .class .as_ref() - .or(group.and_then(|g| g.attrs.class.as_ref())) + .or(group.as_ref().and_then(|g| g.attrs.class.as_ref())) { toplevel.set_app_id(class.to_string()); } @@ -1303,7 +1229,7 @@ impl ServerState { .attrs .title .as_ref() - .or(group.and_then(|g| g.attrs.title.as_ref())) + .or(group.as_ref().and_then(|g| g.attrs.title.as_ref())) { toplevel.set_title(title.name().to_string()); } @@ -1323,38 +1249,32 @@ impl ServerState { decoration }); - let surface: &SurfaceData = self.objects[surface_key].as_ref(); + let surface = self + .world + .get::<&client::wl_surface::WlSurface>(entity) + .unwrap(); if let (Some(activation_state), Some(token)) = ( self.activation_state.as_ref(), window.activation_token.clone(), ) { - activation_state.activate::(&surface.client, token); + activation_state.activate::(&surface, token); } if let Some(parent) = window.attrs.transient_for { // TODO: handle transient_for window not being mapped/not a toplevel 'b: { - let Some(parent_data) = self.windows.get_mut(&parent) else { + let Some(parent_id) = self.windows.get(&parent).copied() else { warn!( "Window {:?} is marked transient for unknown window {:?}", - window.window, parent + *self.world.get::<&x::Window>(entity).unwrap(), + parent ); break 'b; }; - let Some(key) = parent_data.surface_key else { - warn!("Parent window {parent:?} missing surface key."); - break 'b; - }; - - let Some::<&SurfaceData>(surface) = self.objects.get(key).map(|o| o.as_ref()) - else { - warn!("Parent window {parent:?} surface is stale"); - break 'b; - }; - - let Some(SurfaceRole::Toplevel(Some(parent_toplevel))) = &surface.role else { - warn!("Surface {:?} (for window {parent:?}) was not an active toplevel, not setting as parent", surface.client.id()); + let role = self.world.get::<&SurfaceRole>(parent_id); + let Ok(SurfaceRole::Toplevel(Some(parent_toplevel))) = role.as_deref() else { + warn!("Window {parent:?} was not an active toplevel, not setting as parent"); break 'b; }; @@ -1375,21 +1295,23 @@ impl ServerState { } fn create_popup( - &self, - window: &WindowData, - surface_key: ObjectKey, + &mut self, + entity: Entity, xdg: XdgSurface, parent: x::Window, ) -> (f64, PopupData) { - let parent_window = self.windows.get(&parent).unwrap(); - let parent_surface: &SurfaceData = - self.objects[parent_window.surface_key.unwrap()].as_ref(); + let window = self.world.get::<&WindowData>(entity).unwrap(); + let mut parent_query = self + .world + .query_one::<(&WindowData, &SurfaceScaleFactor, &SurfaceRole)>(self.windows[&parent]) + .unwrap(); + let (parent_window, parent_scale, parent_role) = parent_query.get().unwrap(); let parent_dims = parent_window.attrs.dims; - let initial_scale = parent_surface.scale_factor; + let initial_scale = parent_scale.0; debug!( - "creating popup ({:?}) {:?} {:?} {:?} {surface_key:?} (scale: {initial_scale})", - window.window, + "creating popup ({:?}) {:?} {:?} {:?} {entity:?} (scale: {initial_scale})", + *self.world.get::<&x::Window>(entity).unwrap(), parent, window.attrs.dims, xdg.id() @@ -1412,10 +1334,10 @@ impl ServerState { (parent_window.attrs.dims.height as f64 / initial_scale) as i32, ); let popup = xdg.get_popup( - Some(&parent_surface.xdg().unwrap().surface), + Some(&parent_role.xdg().unwrap().surface), &positioner, &self.qh, - surface_key, + entity, ); ( @@ -1432,24 +1354,6 @@ impl ServerState { ) } - fn get_server_surface_from_client( - &self, - surface: client::wl_surface::WlSurface, - ) -> Option<&WlSurface> { - let key: &ObjectKey = surface.data().unwrap(); - let surface: &SurfaceData = self.objects.get(*key)?.as_ref(); - Some(&surface.server) - } - - fn get_client_surface_from_server( - &self, - surface: WlSurface, - ) -> Option<&client::wl_surface::WlSurface> { - let key: &ObjectKey = surface.data().unwrap(); - let surface: &SurfaceData = self.objects.get(*key)?.as_ref(); - Some(&surface.client) - } - fn close_x_window(&mut self, window: x::Window) { debug!("sending close request to {window:?}"); self.connection.as_mut().unwrap().close_window(window); @@ -1488,7 +1392,7 @@ impl ForeignSelection { state: &ServerState, ) -> Vec { let mut pipe = self.inner.receive(mime_type).unwrap(); - state.clientside.queue.flush().unwrap(); + state.queue.flush().unwrap(); let mut data = Vec::new(); pipe.read_to_end(&mut data).unwrap(); data