From 4c0e6d9b51543487406ec68c49818d5ebf555316 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Wed, 8 May 2024 15:55:02 +0200 Subject: [PATCH 01/14] wl-pointer: don't send motion events if the position did not change --- src/fixed.rs | 2 +- src/ifs/wl_seat/wl_pointer.rs | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/fixed.rs b/src/fixed.rs index 0b32769b..3bd02e9a 100644 --- a/src/fixed.rs +++ b/src/fixed.rs @@ -4,7 +4,7 @@ use std::{ ops::{Add, AddAssign, Sub, SubAssign}, }; -#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Default)] #[repr(transparent)] pub struct Fixed(pub i32); diff --git a/src/ifs/wl_seat/wl_pointer.rs b/src/ifs/wl_seat/wl_pointer.rs index 839828cc..fa1f71f8 100644 --- a/src/ifs/wl_seat/wl_pointer.rs +++ b/src/ifs/wl_seat/wl_pointer.rs @@ -74,6 +74,7 @@ pub struct WlPointer { id: WlPointerId, pub seat: Rc, pub tracker: Tracker, + last_motion: Cell<(Fixed, Fixed)>, } impl WlPointer { @@ -82,10 +83,12 @@ impl WlPointer { id, seat: seat.clone(), tracker: Default::default(), + last_motion: Default::default(), } } pub fn send_enter(&self, serial: u32, surface: WlSurfaceId, x: Fixed, y: Fixed) { + self.last_motion.set((x, y)); self.seat.client.event(Enter { self_id: self.id, serial, @@ -104,6 +107,9 @@ impl WlPointer { } pub fn send_motion(&self, time: u32, x: Fixed, y: Fixed) { + if self.last_motion.replace((x, y)) == (x, y) { + return; + } self.seat.client.event(Motion { self_id: self.id, time, From 0d7a07ec4089950f7678310738b928b3641405b6 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Wed, 8 May 2024 15:13:21 +0200 Subject: [PATCH 02/14] all: add HashMapExt --- src/backends/metal.rs | 9 +++++---- src/cursor_user.rs | 6 +++--- src/ifs/wl_seat/event_handling.rs | 4 ++-- src/ifs/wl_seat/tablet.rs | 17 ++++++++++------- src/ifs/wl_seat/tablet/pad.rs | 4 ++-- src/ifs/wl_seat/tablet/tool.rs | 4 ++-- src/ifs/wl_surface/commit_timeline.rs | 3 ++- src/ifs/wl_surface/xdg_surface/xdg_toplevel.rs | 6 +++--- src/ifs/zwp_linux_buffer_params_v1.rs | 4 ++-- src/it/test_transport.rs | 3 ++- src/leaks.rs | 3 ++- src/pipewire/pw_con.rs | 3 ++- src/portal/ptl_display.rs | 4 ++-- src/portal/ptl_screencast.rs | 3 ++- src/portal/ptl_screencast/screencast_gui.rs | 6 +++--- src/portal/ptr_gui.rs | 6 +++--- src/security_context_acceptor.rs | 4 ++-- src/state.rs | 10 +++++----- src/tasks/connector.rs | 8 ++++---- src/tree/container.rs | 3 ++- src/tree/output.rs | 5 +++-- src/tree/toplevel.rs | 7 ++++--- src/utils.rs | 1 + src/utils/hash_map_ext.rs | 15 +++++++++++++++ src/video/drm/sync_obj.rs | 3 ++- src/video/drm/wait_for_sync_obj.rs | 4 ++-- src/wheel.rs | 6 +++--- src/wl_usr.rs | 3 ++- src/xwayland/xwm.rs | 14 +++++++------- 29 files changed, 99 insertions(+), 69 deletions(-) create mode 100644 src/utils/hash_map_ext.rs diff --git a/src/backends/metal.rs b/src/backends/metal.rs index 8cf68ae9..4b5e8c03 100644 --- a/src/backends/metal.rs +++ b/src/backends/metal.rs @@ -35,6 +35,7 @@ use { clonecell::{CloneCell, UnsafeCellCloneSafe}, copyhashmap::CopyHashMap, errorfmt::ErrorFmt, + hash_map_ext::HashMapExt, numcell::NumCell, oserror::OsError, smallmap::SmallMap, @@ -184,7 +185,7 @@ impl Backend for MetalBackend { dev.cb.take(); } } - for (_, dev) in self.device_holder.drm_devices.lock().drain() { + for dev in self.device_holder.drm_devices.lock().drain_values() { dev.futures.clear(); for crtc in dev.dev.crtcs.values() { crtc.connector.take(); @@ -196,13 +197,13 @@ impl Backend for MetalBackend { lease.crtcs.clear(); lease.planes.clear(); }; - for (_, mut lease) in dev.dev.leases.lock().drain() { + for mut lease in dev.dev.leases.lock().drain_values() { clear_lease(&mut lease); } - for (_, mut lease) in dev.dev.leases_to_break.lock().drain() { + for mut lease in dev.dev.leases_to_break.lock().drain_values() { clear_lease(&mut lease); } - for (_, connector) in dev.connectors.lock().drain() { + for connector in dev.connectors.lock().drain_values() { { let d = &mut *connector.display.borrow_mut(); d.crtcs.clear(); diff --git a/src/cursor_user.rs b/src/cursor_user.rs index f16abf3c..0c66c675 100644 --- a/src/cursor_user.rs +++ b/src/cursor_user.rs @@ -6,8 +6,8 @@ use { state::State, tree::OutputNode, utils::{ - clonecell::CloneCell, copyhashmap::CopyHashMap, errorfmt::ErrorFmt, rc_eq::rc_eq, - transform_ext::TransformExt, + clonecell::CloneCell, copyhashmap::CopyHashMap, errorfmt::ErrorFmt, + hash_map_ext::HashMapExt, rc_eq::rc_eq, transform_ext::TransformExt, }, }, std::{cell::Cell, ops::Deref, rc::Rc}, @@ -99,7 +99,7 @@ impl CursorUserGroup { .set(self.state.dummy_output.get().unwrap()); self.state.remove_cursor_size(self.size.get()); self.state.cursor_user_groups.remove(&self.id); - for (_, user) in self.users.lock().drain() { + for user in self.users.lock().drain_values() { user.detach(); } } diff --git a/src/ifs/wl_seat/event_handling.rs b/src/ifs/wl_seat/event_handling.rs index be5cab5a..8af8f3b6 100644 --- a/src/ifs/wl_seat/event_handling.rs +++ b/src/ifs/wl_seat/event_handling.rs @@ -33,7 +33,7 @@ use { object::Version, state::DeviceHandlerData, tree::{Direction, Node, ToplevelNode}, - utils::{bitflags::BitflagsExt, smallmap::SmallMap}, + utils::{bitflags::BitflagsExt, hash_map_ext::HashMapExt, smallmap::SmallMap}, wire::WlDataOfferId, xkbcommon::{KeyboardState, XkbState, XKB_KEY_DOWN, XKB_KEY_UP}, }, @@ -237,7 +237,7 @@ impl WlSeatGlobal { | InputEvent::TabletPadStrip { time_usec, .. } => { self.last_input_usec.set(time_usec); if self.idle_notifications.is_not_empty() { - for (_, notification) in self.idle_notifications.lock().drain() { + for notification in self.idle_notifications.lock().drain_values() { notification.resume.trigger(); } } diff --git a/src/ifs/wl_seat/tablet.rs b/src/ifs/wl_seat/tablet.rs index bd8517d6..9bf7fc44 100644 --- a/src/ifs/wl_seat/tablet.rs +++ b/src/ifs/wl_seat/tablet.rs @@ -19,7 +19,10 @@ use { object::Version, time::now_usec, tree::{FoundNode, Node}, - utils::{bindings::PerClientBindings, clonecell::CloneCell, copyhashmap::CopyHashMap}, + utils::{ + bindings::PerClientBindings, clonecell::CloneCell, copyhashmap::CopyHashMap, + hash_map_ext::HashMapExt, + }, }, std::{ cell::{Cell, RefCell}, @@ -293,18 +296,18 @@ impl WlSeatGlobal { pub fn tablet_clear(&self) { self.tablet.seats.clear(); - for (_, tablet) in self.tablet.tablets.lock().drain() { + for tablet in self.tablet.tablets.lock().drain_values() { tablet.pads.clear(); tablet.bindings.clear(); tablet.tools.clear(); } - for (_, tool) in self.tablet.tools.lock().drain() { + for tool in self.tablet.tools.lock().drain_values() { tool.cursor.detach(); tool.opt.tool.take(); tool.tool_owner.destroy(&tool); tool.bindings.clear(); } - for (_, pad) in self.tablet.pads.lock().drain() { + for pad in self.tablet.pads.lock().drain_values() { pad.pad_owner.destroy(&pad); pad.tablet.take(); pad.bindings.clear(); @@ -324,14 +327,14 @@ impl WlSeatGlobal { let Some(tablet) = self.tablet.tablets.remove(&id) else { return; }; - for (_, tool) in tablet.tools.lock().drain() { + for tool in tablet.tools.lock().drain_values() { self.tablet_handle_remove_tool(now_usec(), tool.id); } - for (_, pad) in tablet.pads.lock().drain() { + for pad in tablet.pads.lock().drain_values() { pad.pad_owner.destroy(&pad); pad.tablet.take(); } - for (_, binding) in tablet.bindings.lock().drain() { + for binding in tablet.bindings.lock().drain_values() { binding.send_removed(); } } diff --git a/src/ifs/wl_seat/tablet/pad.rs b/src/ifs/wl_seat/tablet/pad.rs index 7eddb329..c39c5109 100644 --- a/src/ifs/wl_seat/tablet/pad.rs +++ b/src/ifs/wl_seat/tablet/pad.rs @@ -14,7 +14,7 @@ use { wl_surface::WlSurface, }, time::{now_usec, usec_to_msec}, - utils::clonecell::CloneCell, + utils::{clonecell::CloneCell, hash_map_ext::HashMapExt}, }, std::{cell::Cell, rc::Rc}, }; @@ -76,7 +76,7 @@ impl WlSeatGlobal { if let Some(tablet) = pad.tablet.take() { tablet.pads.remove(&pad.id); } - for (_, binding) in pad.bindings.lock().drain() { + for binding in pad.bindings.lock().drain_values() { binding.send_removed(); } } diff --git a/src/ifs/wl_seat/tablet/tool.rs b/src/ifs/wl_seat/tablet/tool.rs index 9f11ea5f..61fa6306 100644 --- a/src/ifs/wl_seat/tablet/tool.rs +++ b/src/ifs/wl_seat/tablet/tool.rs @@ -15,7 +15,7 @@ use { }, rect::Rect, time::usec_to_msec, - utils::clonecell::CloneCell, + utils::{clonecell::CloneCell, hash_map_ext::HashMapExt}, }, std::{cell::Cell, rc::Rc}, }; @@ -31,7 +31,7 @@ impl WlSeatGlobal { tool.opt.tool.take(); tool.cursor.detach(); tool.tool_owner.destroy(&tool); - for (_, binding) in tool.bindings.lock().drain() { + for binding in tool.bindings.lock().drain_values() { binding.send_removed(); } tool.tablet.tools.remove(&id); diff --git a/src/ifs/wl_surface/commit_timeline.rs b/src/ifs/wl_surface/commit_timeline.rs index df6deb94..a150b6fa 100644 --- a/src/ifs/wl_surface/commit_timeline.rs +++ b/src/ifs/wl_surface/commit_timeline.rs @@ -4,6 +4,7 @@ use { utils::{ clonecell::CloneCell, copyhashmap::CopyHashMap, + hash_map_ext::HashMapExt, linkedlist::{LinkedList, LinkedNode, NodeRef}, numcell::NumCell, }, @@ -102,7 +103,7 @@ impl CommitTimelines { } pub fn clear(&self) { - for (_, list) in self.gc.lock().drain() { + for list in self.gc.lock().drain_values() { break_loops(&list); } } diff --git a/src/ifs/wl_surface/xdg_surface/xdg_toplevel.rs b/src/ifs/wl_surface/xdg_surface/xdg_toplevel.rs index ae1a377d..5c93bd6e 100644 --- a/src/ifs/wl_surface/xdg_surface/xdg_toplevel.rs +++ b/src/ifs/wl_surface/xdg_surface/xdg_toplevel.rs @@ -29,7 +29,7 @@ use { OutputNode, ToplevelData, ToplevelNode, ToplevelNodeBase, ToplevelNodeId, WorkspaceNode, }, - utils::clonecell::CloneCell, + utils::{clonecell::CloneCell, hash_map_ext::HashMapExt}, wire::{xdg_toplevel::*, XdgToplevelId}, }, ahash::{AHashMap, AHashSet}, @@ -218,7 +218,7 @@ impl XdgToplevelRequestHandler for XdgToplevel { Some(p) => Some(p.children.borrow_mut()), _ => None, }; - for (_, child) in children.drain() { + for child in children.drain_values() { child.parent.set(parent.clone()); if let Some(parent_children) = &mut parent_children { parent_children.insert(child.id, child); @@ -418,7 +418,7 @@ impl XdgToplevel { { let new_parent = self.parent.get(); let mut children = self.children.borrow_mut(); - for (_, child) in children.drain() { + for child in children.drain_values() { child.parent.set(new_parent.clone()); } } diff --git a/src/ifs/zwp_linux_buffer_params_v1.rs b/src/ifs/zwp_linux_buffer_params_v1.rs index db1914a6..025cbc1b 100644 --- a/src/ifs/zwp_linux_buffer_params_v1.rs +++ b/src/ifs/zwp_linux_buffer_params_v1.rs @@ -5,7 +5,7 @@ use { ifs::{wl_buffer::WlBuffer, zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1}, leaks::Tracker, object::Object, - utils::errorfmt::ErrorFmt, + utils::{errorfmt::ErrorFmt, hash_map_ext::HashMapExt}, video::dmabuf::{DmaBuf, DmaBufPlane, PlaneVec, MAX_PLANES}, wire::{zwp_linux_buffer_params_v1::*, WlBufferId, ZwpLinuxBufferParamsV1Id}, }, @@ -90,7 +90,7 @@ impl ZwpLinuxBufferParamsV1 { modifier, planes: PlaneVec::new(), }; - let mut planes: Vec<_> = self.planes.borrow_mut().drain().map(|v| v.1).collect(); + let mut planes: Vec<_> = self.planes.borrow_mut().drain_values().collect(); planes.sort_by_key(|a| a.plane_idx); for (i, p) in planes.into_iter().enumerate() { if p.plane_idx as usize != i { diff --git a/src/it/test_transport.rs b/src/it/test_transport.rs index d0bb3063..da8484f0 100644 --- a/src/it/test_transport.rs +++ b/src/it/test_transport.rs @@ -15,6 +15,7 @@ use { bitfield::Bitfield, buffd::{BufFdIn, BufFdOut, MsgFormatter, MsgParser, OutBuffer, OutBufferSwapchain}, copyhashmap::CopyHashMap, + hash_map_ext::HashMapExt, stack::Stack, vec_ext::VecExt, }, @@ -105,7 +106,7 @@ impl TestTransport { pub fn kill(&self) { self.outgoing.take(); self.incoming.take(); - for (_, object) in self.objects.lock().drain() { + for object in self.objects.lock().drain_values() { object.on_remove(self); } } diff --git a/src/leaks.rs b/src/leaks.rs index 343e023c..6a6f0c57 100644 --- a/src/leaks.rs +++ b/src/leaks.rs @@ -43,6 +43,7 @@ mod leaks { crate::{ client::ClientId, utils::{ + hash_map_ext::HashMapExt, ptr_ext::{MutPtrExt, PtrExt}, windows::WindowsExt, }, @@ -157,7 +158,7 @@ mod leaks { if map.is_empty() { log::info!("No leaks"); } - for (_, mut objs) in map.drain() { + for mut objs in map.drain_values() { if objs.len() == 0 { continue; } diff --git a/src/pipewire/pw_con.rs b/src/pipewire/pw_con.rs index 6fe93ed5..51cec2b8 100644 --- a/src/pipewire/pw_con.rs +++ b/src/pipewire/pw_con.rs @@ -23,6 +23,7 @@ use { clonecell::CloneCell, copyhashmap::CopyHashMap, errorfmt::ErrorFmt, + hash_map_ext::HashMapExt, numcell::NumCell, oserror::OsError, xrd::xrd, @@ -122,7 +123,7 @@ impl PwCon { } pub fn kill(&self) { - for (_, obj) in self.objects.lock().drain() { + for obj in self.objects.lock().drain_values() { obj.break_loops(); } self.io.shutdown(); diff --git a/src/portal/ptl_display.rs b/src/portal/ptl_display.rs index 84539061..9d078e89 100644 --- a/src/portal/ptl_display.rs +++ b/src/portal/ptl_display.rs @@ -8,7 +8,7 @@ use { }, utils::{ bitflags::BitflagsExt, clonecell::CloneCell, copyhashmap::CopyHashMap, - errorfmt::ErrorFmt, oserror::OsError, + errorfmt::ErrorFmt, hash_map_ext::HashMapExt, oserror::OsError, }, video::drm::Drm, wire::{ @@ -191,7 +191,7 @@ impl UsrJayRenderCtxOwner for PortalDisplay { impl UsrConOwner for PortalDisplay { fn killed(&self) { log::info!("Removing display {}", self.id); - for (_, sc) in self.screencasts.lock().drain() { + for sc in self.screencasts.lock().drain_values() { sc.kill(); } self.windows.clear(); diff --git a/src/portal/ptl_screencast.rs b/src/portal/ptl_screencast.rs index 00c42514..fafc706b 100644 --- a/src/portal/ptl_screencast.rs +++ b/src/portal/ptl_screencast.rs @@ -21,6 +21,7 @@ use { utils::{ clonecell::{CloneCell, UnsafeCellCloneSafe}, copyhashmap::CopyHashMap, + hash_map_ext::HashMapExt, }, video::dmabuf::{DmaBuf, PlaneVec}, wire::jay_screencast::Ready, @@ -267,7 +268,7 @@ impl ScreencastSession { ScreencastPhase::Terminated => {} ScreencastPhase::Selecting(s) => { s.core.reply.err("Session has been terminated"); - for (_, gui) in s.guis.lock().drain() { + for gui in s.guis.lock().drain_values() { gui.kill(false); } } diff --git a/src/portal/ptl_screencast/screencast_gui.rs b/src/portal/ptl_screencast/screencast_gui.rs index 016c9b66..9ae787a6 100644 --- a/src/portal/ptl_screencast/screencast_gui.rs +++ b/src/portal/ptl_screencast/screencast_gui.rs @@ -13,7 +13,7 @@ use { }, }, theme::Color, - utils::copyhashmap::CopyHashMap, + utils::{copyhashmap::CopyHashMap, hash_map_ext::HashMapExt}, wl_usr::usr_ifs::{ usr_jay_select_toplevel::UsrJaySelectToplevelOwner, usr_jay_select_workspace::UsrJaySelectWorkspaceOwner, usr_jay_toplevel::UsrJayToplevel, @@ -53,7 +53,7 @@ enum ButtonRole { impl SelectionGui { pub fn kill(&self, upwards: bool) { - for (_, surface) in self.surfaces.lock().drain() { + for surface in self.surfaces.lock().drain_values() { surface.overlay.data.kill(false); } if let ScreencastPhase::Selecting(s) = self.screencast_session.phase.get() { @@ -159,7 +159,7 @@ impl ButtonOwner for StaticButton { ScreencastPhase::Selecting(selecting) => selecting, _ => return, }; - for (_, gui) in selecting.guis.lock().drain() { + for gui in selecting.guis.lock().drain_values() { gui.kill(false); } let dpy = &self.surface.output.dpy; diff --git a/src/portal/ptr_gui.rs b/src/portal/ptr_gui.rs index 3020d219..0bd6e509 100644 --- a/src/portal/ptr_gui.rs +++ b/src/portal/ptr_gui.rs @@ -13,7 +13,7 @@ use { theme::Color, utils::{ asyncevent::AsyncEvent, clonecell::CloneCell, copyhashmap::CopyHashMap, - errorfmt::ErrorFmt, rc_eq::rc_eq, + errorfmt::ErrorFmt, hash_map_ext::HashMapExt, rc_eq::rc_eq, }, video::gbm::GBM_BO_USE_RENDERING, wire::{ @@ -671,7 +671,7 @@ impl WindowData { owner.kill(upwards); } self.render_task.take(); - for (_, pb) in self.pending_bufs.lock().drain() { + for pb in self.pending_bufs.lock().drain_values() { pb.params.con.remove_obj(pb.params.deref()); } for buf in self.bufs.borrow_mut().drain(..) { @@ -689,7 +689,7 @@ impl WindowData { pub fn allocate_buffers(self: &Rc) { { - for (_, buf) in self.pending_bufs.lock().drain() { + for buf in self.pending_bufs.lock().drain_values() { buf.params.con.remove_obj(buf.params.deref()); } } diff --git a/src/security_context_acceptor.rs b/src/security_context_acceptor.rs index 69f102af..7d4ae7d0 100644 --- a/src/security_context_acceptor.rs +++ b/src/security_context_acceptor.rs @@ -3,7 +3,7 @@ use { async_engine::SpawnedFuture, client::ClientCaps, state::State, - utils::{copyhashmap::CopyHashMap, errorfmt::ErrorFmt}, + utils::{copyhashmap::CopyHashMap, errorfmt::ErrorFmt, hash_map_ext::HashMapExt}, }, std::{ cell::Cell, @@ -36,7 +36,7 @@ struct Acceptor { impl SecurityContextAcceptors { pub fn clear(&self) { - for (_, acceptor) in self.acceptors.lock().drain() { + for acceptor in self.acceptors.lock().drain_values() { acceptor.kill(); } } diff --git a/src/state.rs b/src/state.rs index 73abb7df..8246aeb1 100644 --- a/src/state.rs +++ b/src/state.rs @@ -68,8 +68,8 @@ use { utils::{ activation_token::ActivationToken, asyncevent::AsyncEvent, bindings::Bindings, clonecell::CloneCell, copyhashmap::CopyHashMap, errorfmt::ErrorFmt, fdcloser::FdCloser, - linkedlist::LinkedList, numcell::NumCell, queue::AsyncQueue, refcounted::RefCounted, - run_toplevel::RunToplevel, + hash_map_ext::HashMapExt, linkedlist::LinkedList, numcell::NumCell, queue::AsyncQueue, + refcounted::RefCounted, run_toplevel::RunToplevel, }, video::{ dmabuf::DmaBufIds, @@ -743,11 +743,11 @@ impl State { self.xwayland.queue.clear(); self.idle.inhibitors.clear(); self.idle.change.clear(); - for (_, drm_dev) in self.drm_devs.lock().drain() { + for drm_dev in self.drm_devs.lock().drain_values() { drm_dev.handler.take(); drm_dev.connectors.clear(); } - for (_, connector) in self.connectors.lock().drain() { + for connector in self.connectors.lock().drain_values() { connector.handler.take(); connector.async_event.clear(); } @@ -769,7 +769,7 @@ impl State { self.toplevel_lists.clear(); self.security_context_acceptors.clear(); self.slow_clients.clear(); - for (_, h) in self.input_device_handlers.borrow_mut().drain() { + for h in self.input_device_handlers.borrow_mut().drain_values() { h.async_event.clear(); } self.backend_events.clear(); diff --git a/src/tasks/connector.rs b/src/tasks/connector.rs index 2824010c..901b439a 100644 --- a/src/tasks/connector.rs +++ b/src/tasks/connector.rs @@ -5,7 +5,7 @@ use { ifs::wl_output::{OutputId, PersistentOutputState, WlOutputGlobal}, state::{ConnectorData, OutputData, State}, tree::{move_ws_to_output, OutputNode, OutputRenderData, WsMoveConfig}, - utils::{asyncevent::AsyncEvent, clonecell::CloneCell}, + utils::{asyncevent::AsyncEvent, clonecell::CloneCell, hash_map_ext::HashMapExt}, }, std::{ cell::{Cell, RefCell}, @@ -244,14 +244,14 @@ impl ConnectorHandler { config.connector_disconnected(self.id); } global.clear(); - for (_, jo) in on.jay_outputs.lock().drain() { + for jo in on.jay_outputs.lock().drain_values() { jo.send_destroyed(); } let screencasts: Vec<_> = on.screencasts.lock().values().cloned().collect(); for sc in screencasts { sc.do_destroy(); } - for (_, sc) in on.screencopies.lock().drain() { + for sc in on.screencopies.lock().drain_values() { sc.send_failed(); } global.destroyed.set(true); @@ -310,7 +310,7 @@ impl ConnectorHandler { } }; let withdraw = || { - for (_, con) in output_data.lease_connectors.lock().drain() { + for con in output_data.lease_connectors.lock().drain_values() { con.send_withdrawn(); if !con.device.destroyed.get() { con.device.send_done(); diff --git a/src/tree/container.rs b/src/tree/container.rs index 4ad79051..052ecdb0 100644 --- a/src/tree/container.rs +++ b/src/tree/container.rs @@ -23,6 +23,7 @@ use { clonecell::CloneCell, double_click_state::DoubleClickState, errorfmt::ErrorFmt, + hash_map_ext::HashMapExt, linkedlist::{LinkedList, LinkedNode, NodeRef}, numcell::NumCell, rc_eq::rc_eq, @@ -1613,7 +1614,7 @@ impl ToplevelNodeBase for ContainerNode { fn tl_destroy_impl(&self) { mem::take(self.cursors.borrow_mut().deref_mut()); let mut cn = self.child_nodes.borrow_mut(); - for (_, n) in cn.drain() { + for n in cn.drain_values() { n.node.tl_destroy(); } } diff --git a/src/tree/output.rs b/src/tree/output.rs index 8c009de7..2582d64a 100644 --- a/src/tree/output.rs +++ b/src/tree/output.rs @@ -36,7 +36,8 @@ use { }, utils::{ clonecell::CloneCell, copyhashmap::CopyHashMap, errorfmt::ErrorFmt, - linkedlist::LinkedList, scroller::Scroller, transform_ext::TransformExt, + hash_map_ext::HashMapExt, linkedlist::LinkedList, scroller::Scroller, + transform_ext::TransformExt, }, wire::{JayOutputId, JayScreencastId, ZwlrScreencopyFrameV1Id}, }, @@ -140,7 +141,7 @@ impl OutputNode { return; } let now = Time::now().unwrap(); - for (_, capture) in self.screencopies.lock().drain() { + for capture in self.screencopies.lock().drain_values() { let wl_buffer = match capture.buffer.take() { Some(b) => b, _ => { diff --git a/src/tree/toplevel.rs b/src/tree/toplevel.rs index 7aefabe3..ee6ecdfc 100644 --- a/src/tree/toplevel.rs +++ b/src/tree/toplevel.rs @@ -15,6 +15,7 @@ use { utils::{ clonecell::CloneCell, copyhashmap::CopyHashMap, + hash_map_ext::HashMapExt, numcell::NumCell, smallmap::SmallMap, threshold_counter::ThresholdCounter, @@ -289,16 +290,16 @@ impl ToplevelData { } pub fn destroy_node(&self, node: &dyn Node) { - for (_, jay_tl) in self.jay_toplevels.lock().drain() { + for jay_tl in self.jay_toplevels.lock().drain_values() { jay_tl.destroy(); } - for (_, screencast) in self.jay_screencasts.lock().drain() { + for screencast in self.jay_screencasts.lock().drain_values() { screencast.do_destroy(); } self.identifier.set(toplevel_identifier()); { let mut handles = self.handles.lock(); - for (_, handle) in handles.drain() { + for handle in handles.drain_values() { handle.send_closed(); } } diff --git a/src/utils.rs b/src/utils.rs index 8531378c..850931c7 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -16,6 +16,7 @@ pub mod double_click_state; pub mod errorfmt; pub mod fdcloser; pub mod gfx_api_ext; +pub mod hash_map_ext; pub mod hex; pub mod line_logger; pub mod linkedlist; diff --git a/src/utils/hash_map_ext.rs b/src/utils/hash_map_ext.rs new file mode 100644 index 00000000..53ce1030 --- /dev/null +++ b/src/utils/hash_map_ext.rs @@ -0,0 +1,15 @@ +use std::collections::HashMap; + +pub trait HashMapExt { + type V; + + fn drain_values(&mut self) -> impl Iterator; +} + +impl HashMapExt for HashMap { + type V = V; + + fn drain_values(&mut self) -> impl Iterator { + self.drain().map(|(_, v)| v) + } +} diff --git a/src/video/drm/sync_obj.rs b/src/video/drm/sync_obj.rs index 3eb8e355..d1d74f6a 100644 --- a/src/video/drm/sync_obj.rs +++ b/src/video/drm/sync_obj.rs @@ -5,6 +5,7 @@ use { clonecell::{CloneCell, UnsafeCellCloneSafe}, copyhashmap::CopyHashMap, errorfmt::ErrorFmt, + hash_map_ext::HashMapExt, linkedlist::{LinkedList, LinkedNode}, oserror::OsError, }, @@ -230,7 +231,7 @@ impl Drop for SyncObjCtx { fn drop(&mut self) { self.inner.links.clear(); let mut map = self.inner.handles.lock(); - for (_, handle) in map.drain() { + for handle in map.drain_values() { destroy(&self.inner.drm, handle); } } diff --git a/src/video/drm/wait_for_sync_obj.rs b/src/video/drm/wait_for_sync_obj.rs index a01788f2..f22a1095 100644 --- a/src/video/drm/wait_for_sync_obj.rs +++ b/src/video/drm/wait_for_sync_obj.rs @@ -4,7 +4,7 @@ use { io_uring::IoUring, utils::{ asyncevent::AsyncEvent, buf::Buf, clonecell::CloneCell, copyhashmap::CopyHashMap, - numcell::NumCell, oserror::OsError, stack::Stack, + hash_map_ext::HashMapExt, numcell::NumCell, oserror::OsError, stack::Stack, }, video::drm::{ sync_obj::{SyncObj, SyncObjCtx, SyncObjPoint}, @@ -89,7 +89,7 @@ impl WaitForSyncObj { pub fn set_ctx(&self, ctx: Option>) { self.inner.ctx.set(ctx); - let busy_waiters: Vec<_> = self.inner.busy.lock().drain().map(|(_, w)| w).collect(); + let busy_waiters: Vec<_> = self.inner.busy.lock().drain_values().collect(); for waiter in busy_waiters { let res = self.submit_job( waiter.job.id, diff --git a/src/wheel.rs b/src/wheel.rs index 2ffe6786..5c7dedb5 100644 --- a/src/wheel.rs +++ b/src/wheel.rs @@ -4,8 +4,8 @@ use { io_uring::{IoUring, IoUringError}, time::{Time, TimeError}, utils::{ - buf::TypedBuf, copyhashmap::CopyHashMap, errorfmt::ErrorFmt, numcell::NumCell, - oserror::OsError, stack::Stack, + buf::TypedBuf, copyhashmap::CopyHashMap, errorfmt::ErrorFmt, hash_map_ext::HashMapExt, + numcell::NumCell, oserror::OsError, stack::Stack, }, }, std::{ @@ -204,7 +204,7 @@ impl WheelData { self.destroyed.set(true); self.dispatcher.set(None); self.cached_futures.take(); - for (_, dispatcher) in self.dispatchers.lock().drain() { + for dispatcher in self.dispatchers.lock().drain_values() { dispatcher.complete(Err(WheelError::Destroyed)); } } diff --git a/src/wl_usr.rs b/src/wl_usr.rs index 1b2fc47a..4feffa64 100644 --- a/src/wl_usr.rs +++ b/src/wl_usr.rs @@ -17,6 +17,7 @@ use { clonecell::CloneCell, copyhashmap::CopyHashMap, errorfmt::ErrorFmt, + hash_map_ext::HashMapExt, oserror::OsError, vec_ext::VecExt, }, @@ -159,7 +160,7 @@ impl UsrCon { pub fn kill(&self) { self.dead.set(true); - for (_, obj) in self.objects.lock().drain() { + for obj in self.objects.lock().drain_values() { if let Some(obj) = obj { obj.break_loops(); } diff --git a/src/xwayland/xwm.rs b/src/xwayland/xwm.rs index 82fdef48..f05fcb54 100644 --- a/src/xwayland/xwm.rs +++ b/src/xwayland/xwm.rs @@ -27,8 +27,8 @@ use { tree::{Node, ToplevelNode}, utils::{ bitflags::BitflagsExt, buf::Buf, cell_ext::CellExt, clonecell::CloneCell, - copyhashmap::CopyHashMap, errorfmt::ErrorFmt, linkedlist::LinkedList, numcell::NumCell, - oserror::OsError, rc_eq::rc_eq, + copyhashmap::CopyHashMap, errorfmt::ErrorFmt, hash_map_ext::HashMapExt, + linkedlist::LinkedList, numcell::NumCell, oserror::OsError, rc_eq::rc_eq, }, wire::WlSurfaceId, wire_xcon::{ @@ -171,7 +171,7 @@ struct SelectionData { impl SelectionData { fn destroy(&self) { - for (_, offer) in self.offers.lock().drain() { + for offer in self.offers.lock().drain_values() { destroy_data_offer::(&offer.offer); } self.active_offer.take(); @@ -179,7 +179,7 @@ impl SelectionData { } fn destroy_sources(&self) { - for (_, source) in self.sources.lock().drain() { + for source in self.sources.lock().drain_values() { destroy_data_source::(&source); } } @@ -207,7 +207,7 @@ impl Drop for XwmShared { fn drop(&mut self) { self.data.destroy(); self.primary_selection.destroy(); - for (_, device) in self.devices.lock().drain() { + for device in self.devices.lock().drain_values() { destroy_data_device::(&device); destroy_data_device::(&device); device.seat.unset_x_data_device(device.id); @@ -260,7 +260,7 @@ enum Initiator { impl Drop for Wm { fn drop(&mut self) { - for (_, window) in self.windows.drain() { + for window in self.windows.drain_values() { if let Some(window) = window.window.take() { window.break_loops(); } @@ -1985,7 +1985,7 @@ impl Wm { } { let mut children = data.children.lock(); - for (_, child) in children.drain() { + for child in children.drain_values() { child.parent.set(None); } } From 381b71e09cefdbd8bf33e4dbc5aa6503bd28edc9 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Mon, 6 May 2024 23:45:50 +0200 Subject: [PATCH 03/14] xdg-popup: handle popup position failures gracefully --- src/ifs/wl_surface/xdg_surface/xdg_popup.rs | 35 ++++++++++----------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/src/ifs/wl_surface/xdg_surface/xdg_popup.rs b/src/ifs/wl_surface/xdg_surface/xdg_popup.rs index 5b4f1ab8..f7999bb7 100644 --- a/src/ifs/wl_surface/xdg_surface/xdg_popup.rs +++ b/src/ifs/wl_surface/xdg_surface/xdg_popup.rs @@ -105,12 +105,8 @@ impl XdgPopup { .event(PopupDone { self_id: self.id }) } - fn update_position(&self, parent: &XdgSurface) -> Result<(), XdgPopupError> { - // let parent = parent.extents.get(); + fn update_position(&self, parent: &XdgSurface) { let positioner = self.pos.borrow_mut(); - // if !parent.contains_rect(&positioner.ar) { - // return Err(XdgPopupError::AnchorRectOutside); - // } let parent_abs = parent.absolute_desired_extents.get(); let mut rel_pos = positioner.get_position(false, false); let mut abs_pos = rel_pos.move_(parent_abs.x1(), parent_abs.y1()); @@ -174,26 +170,29 @@ impl XdgPopup { dy2 = -overflow.bottom.max(0); } if dx1 > 0 || dx2 < 0 || dy1 > 0 || dy2 < 0 { - abs_pos = Rect::new( + let maybe_abs_pos = Rect::new( abs_pos.x1() + dx1, abs_pos.y1() + dy1, abs_pos.x2() + dx2, abs_pos.y2() + dy2, - ) - .unwrap(); - rel_pos = Rect::new_sized( - abs_pos.x1() - parent_abs.x1(), - abs_pos.y1() - parent_abs.y1(), - abs_pos.width(), - abs_pos.height(), - ) - .unwrap(); + ); + // If the popup is completely outside the output, this will fail. Just + // use its position as is. + if let Some(maybe_abs_pos) = maybe_abs_pos { + abs_pos = maybe_abs_pos; + rel_pos = Rect::new_sized( + abs_pos.x1() - parent_abs.x1(), + abs_pos.y1() - parent_abs.y1(), + abs_pos.width(), + abs_pos.height(), + ) + .unwrap(); + } } } } self.relative_position.set(rel_pos); self.xdg.set_absolute_desired_extents(&abs_pos); - Ok(()) } pub fn update_absolute_position(&self) { @@ -230,7 +229,7 @@ impl XdgPopupRequestHandler for XdgPopup { fn reposition(&self, req: Reposition, _slf: &Rc) -> Result<(), Self::Error> { *self.pos.borrow_mut() = self.xdg.surface.client.lookup(req.positioner)?.value(); if let Some(parent) = self.parent.get() { - self.update_position(&parent)?; + self.update_position(&parent); let rel = self.relative_position.get(); self.send_repositioned(req.token); self.send_configure(rel.x1(), rel.y1(), rel.width(), rel.height()); @@ -385,7 +384,7 @@ impl StackedNode for XdgPopup { impl XdgSurfaceExt for XdgPopup { fn initial_configure(self: Rc) -> Result<(), XdgSurfaceError> { if let Some(parent) = self.parent.get() { - self.update_position(&parent)?; + self.update_position(&parent); let rel = self.relative_position.get(); self.send_configure(rel.x1(), rel.y1(), rel.width(), rel.height()); } From 65062710710fe6b7e4edc9665d989e7c570f3e87 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Mon, 6 May 2024 23:53:00 +0200 Subject: [PATCH 04/14] xdg-popup: slide as little as possible when re-positioning popups --- src/ifs/wl_surface/xdg_surface/xdg_popup.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/ifs/wl_surface/xdg_surface/xdg_popup.rs b/src/ifs/wl_surface/xdg_surface/xdg_popup.rs index f7999bb7..a1880cc7 100644 --- a/src/ifs/wl_surface/xdg_surface/xdg_popup.rs +++ b/src/ifs/wl_surface/xdg_surface/xdg_popup.rs @@ -142,17 +142,21 @@ impl XdgPopup { } let (mut dx, mut dy) = (0, 0); if positioner.ca.contains(CA_SLIDE_X) && overflow.x_overflow() { - dx = if overflow.left > 0 || overflow.left + overflow.right > 0 { + dx = if overflow.left + overflow.right > 0 { parent_abs.x1() - abs_pos.x1() + } else if overflow.left > 0 { + overflow.left } else { - parent_abs.x2() - abs_pos.x2() + -overflow.right }; } if positioner.ca.contains(CA_SLIDE_Y) && overflow.y_overflow() { - dy = if overflow.top > 0 || overflow.top + overflow.bottom > 0 { + dy = if overflow.top + overflow.bottom > 0 { parent_abs.y1() - abs_pos.y1() + } else if overflow.top > 0 { + overflow.top } else { - parent_abs.y2() - abs_pos.y2() + -overflow.bottom }; } if dx != 0 || dy != 0 { From c06f532a5801008ce414619f3c6a2fa4ffcfbd1b Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Mon, 6 May 2024 18:22:05 +0200 Subject: [PATCH 05/14] layer-shell: fix visibility tracking of bottom and background layers --- src/tree/output.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tree/output.rs b/src/tree/output.rs index 2582d64a..6717cc7c 100644 --- a/src/tree/output.rs +++ b/src/tree/output.rs @@ -642,7 +642,7 @@ impl OutputNode { if let Some(ws) = self.workspace.get() { have_fullscreen = ws.fullscreen.is_some(); } - let lower_visible = visible && have_fullscreen; + let lower_visible = visible && !have_fullscreen; set_layer_visible!(self.layers[0], lower_visible); set_layer_visible!(self.layers[1], lower_visible); if let Some(ws) = self.workspace.get() { From 7cb9bd667f44341c6b9f785ae141c768e97ca106 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Mon, 6 May 2024 19:52:42 +0200 Subject: [PATCH 06/14] layer-shell: fix input routing to bottom and background layers --- src/tree/output.rs | 22 +++++++++++++++------- src/tree/workspace.rs | 4 ++-- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/tree/output.rs b/src/tree/output.rs index 6717cc7c..21df694c 100644 --- a/src/tree/output.rs +++ b/src/tree/output.rs @@ -843,21 +843,29 @@ impl Node for OutputNode { }); fs.tl_as_node().node_find_tree_at(x, y, tree, usecase) } else { - if y >= bar_height { - y -= bar_height; - let len = tree.len(); + let mut search_layers = true; + if y < bar_height { + search_layers = false; + } else { if let Some(ws) = self.workspace.get() { + let y = y - bar_height; + let len = tree.len(); tree.push(FoundNode { node: ws.clone(), x, y, }); - ws.node_find_tree_at(x, y, tree, usecase); - } - if tree.len() == len { - self.find_layer_surface_at(x, y, &[BOTTOM, BACKGROUND], tree, usecase); + match ws.node_find_tree_at(x, y, tree, usecase) { + FindTreeResult::AcceptsInput => search_layers = false, + FindTreeResult::Other => { + tree.truncate(len); + } + } } } + if search_layers { + self.find_layer_surface_at(x, y, &[BOTTOM, BACKGROUND], tree, usecase); + } FindTreeResult::AcceptsInput } } diff --git a/src/tree/workspace.rs b/src/tree/workspace.rs index f4f91d69..ed2835a5 100644 --- a/src/tree/workspace.rs +++ b/src/tree/workspace.rs @@ -264,9 +264,9 @@ impl Node for WorkspaceNode { x, y, }); - n.node_find_tree_at(x, y, tree, usecase); + return n.node_find_tree_at(x, y, tree, usecase); } - FindTreeResult::AcceptsInput + FindTreeResult::Other } fn node_render(&self, renderer: &mut Renderer, x: i32, y: i32, _bounds: Option<&Rect>) { From cc37d14c9e5350664df8cb80b70c10e594fed955 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Wed, 8 May 2024 12:53:54 +0200 Subject: [PATCH 07/14] layer-shell: compute configure width/height after every commit --- src/ifs/wl_surface/zwlr_layer_surface_v1.rs | 60 ++++++++++----------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/src/ifs/wl_surface/zwlr_layer_surface_v1.rs b/src/ifs/wl_surface/zwlr_layer_surface_v1.rs index 52cf702f..bdda86b1 100644 --- a/src/ifs/wl_surface/zwlr_layer_surface_v1.rs +++ b/src/ifs/wl_surface/zwlr_layer_surface_v1.rs @@ -13,8 +13,7 @@ use { renderer::Renderer, tree::{FindTreeResult, FindTreeUsecase, FoundNode, Node, NodeId, NodeVisitor}, utils::{ - bitflags::BitflagsExt, cell_ext::CellExt, linkedlist::LinkedNode, numcell::NumCell, - option_ext::OptionExt, + bitflags::BitflagsExt, linkedlist::LinkedNode, numcell::NumCell, option_ext::OptionExt, }, wire::{zwlr_layer_surface_v1::*, WlSurfaceId, ZwlrLayerSurfaceV1Id}, }, @@ -60,6 +59,7 @@ pub struct ZwlrLayerSurfaceV1 { keyboard_interactivity: Cell, link: Cell>>>, seat_state: NodeSeatState, + last_configure: Cell<(i32, i32)>, } #[derive(Default)] @@ -123,6 +123,7 @@ impl ZwlrLayerSurfaceV1 { keyboard_interactivity: Cell::new(0), link: Cell::new(None), seat_state: Default::default(), + last_configure: Default::default(), } } @@ -244,11 +245,7 @@ impl ZwlrLayerSurfaceV1RequestHandler for ZwlrLayerSurfaceV1 { impl ZwlrLayerSurfaceV1 { fn pre_commit(&self, pending: &mut PendingState) -> Result<(), ZwlrLayerSurfaceV1Error> { - let Some(global) = self.output.get() else { - return Ok(()); - }; let pending = pending.layer_surface.get_or_insert_default_ext(); - let mut send_configure = mem::replace(&mut pending.any, false); if let Some(size) = pending.size.take() { self.size.set(size); } @@ -267,34 +264,36 @@ impl ZwlrLayerSurfaceV1 { if let Some(layer) = pending.layer.take() { self.layer.set(layer); } - { - let (mut width, mut height) = self.size.get(); - let anchor = self.anchor.get(); - if width == 0 { - if !anchor.contains(LEFT | RIGHT) { - return Err(ZwlrLayerSurfaceV1Error::WidthZero); - } - send_configure = true; - width = global.position().width(); - } - if height == 0 { - if !anchor.contains(TOP | BOTTOM) { - return Err(ZwlrLayerSurfaceV1Error::HeightZero); - } - send_configure = true; - height = global.position().height(); - } - self.size.set((width, height)); + let anchor = self.anchor.get(); + let (width, height) = self.size.get(); + if width == 0 && !anchor.contains(LEFT | RIGHT) { + return Err(ZwlrLayerSurfaceV1Error::WidthZero); } - if self.acked_serial.is_none() { - send_configure = true; + if height == 0 && !anchor.contains(TOP | BOTTOM) { + return Err(ZwlrLayerSurfaceV1Error::HeightZero); } - if send_configure { - let (width, height) = self.size.get(); - let serial = self.requested_serial.fetch_add(1) + 1; + self.configure(); + Ok(()) + } + + fn configure(&self) { + let Some(global) = self.output.get() else { + return; + }; + let (mut width, mut height) = self.size.get(); + let (available_width, available_height) = global.position().size(); + if width == 0 { + width = available_width; + } + width = width.min(available_width).max(1); + if height == 0 { + height = available_height; + } + height = height.min(available_height).max(1); + let serial = self.requested_serial.fetch_add(1) + 1; + if self.last_configure.replace((width, height)) != (width, height) { self.send_configure(serial, width as _, height as _); } - Ok(()) } pub fn output_position(&self) -> Rect { @@ -345,6 +344,7 @@ impl ZwlrLayerSurfaceV1 { self.surface.destroy_node(); self.seat_state.destroy_node(self); self.client.state.tree_changed(); + self.last_configure.take(); } } From 9ac07155ab98cc11511919d267692ec9d71c73eb Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Wed, 8 May 2024 12:58:17 +0200 Subject: [PATCH 08/14] layer-shell: remove dead code --- src/ifs/wl_surface/zwlr_layer_surface_v1.rs | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/src/ifs/wl_surface/zwlr_layer_surface_v1.rs b/src/ifs/wl_surface/zwlr_layer_surface_v1.rs index bdda86b1..635770e0 100644 --- a/src/ifs/wl_surface/zwlr_layer_surface_v1.rs +++ b/src/ifs/wl_surface/zwlr_layer_surface_v1.rs @@ -19,7 +19,6 @@ use { }, std::{ cell::{Cell, RefMut}, - mem, ops::Deref, rc::Rc, }, @@ -51,7 +50,6 @@ pub struct ZwlrLayerSurfaceV1 { mapped: Cell, layer: Cell, requested_serial: NumCell, - acked_serial: Cell>, size: Cell<(i32, i32)>, anchor: Cell, exclusive_zone: Cell, @@ -70,7 +68,6 @@ pub struct PendingLayerSurfaceData { margin: Option<(i32, i32, i32, i32)>, keyboard_interactivity: Option, layer: Option, - any: bool, } impl PendingLayerSurfaceData { @@ -88,7 +85,6 @@ impl PendingLayerSurfaceData { opt!(margin); opt!(keyboard_interactivity); opt!(layer); - self.any |= mem::take(&mut next.any); } } @@ -115,7 +111,6 @@ impl ZwlrLayerSurfaceV1 { mapped: Cell::new(false), layer: Cell::new(layer), requested_serial: Default::default(), - acked_serial: Cell::new(None), size: Cell::new((0, 0)), anchor: Cell::new(0), exclusive_zone: Cell::new(0), @@ -168,7 +163,6 @@ impl ZwlrLayerSurfaceV1RequestHandler for ZwlrLayerSurfaceV1 { } let mut pending = self.pending(); pending.size = Some((req.width as _, req.height as _)); - pending.any = true; Ok(()) } @@ -178,7 +172,6 @@ impl ZwlrLayerSurfaceV1RequestHandler for ZwlrLayerSurfaceV1 { } let mut pending = self.pending(); pending.anchor = Some(req.anchor); - pending.any = true; Ok(()) } @@ -189,14 +182,12 @@ impl ZwlrLayerSurfaceV1RequestHandler for ZwlrLayerSurfaceV1 { ) -> Result<(), Self::Error> { let mut pending = self.pending(); pending.exclusive_zone = Some(req.zone); - pending.any = true; Ok(()) } fn set_margin(&self, req: SetMargin, _slf: &Rc) -> Result<(), Self::Error> { let mut pending = self.pending(); pending.margin = Some((req.top, req.right, req.bottom, req.left)); - pending.any = true; Ok(()) } @@ -212,7 +203,6 @@ impl ZwlrLayerSurfaceV1RequestHandler for ZwlrLayerSurfaceV1 { } let mut pending = self.pending(); pending.keyboard_interactivity = Some(req.keyboard_interactivity); - pending.any = true; Ok(()) } @@ -220,8 +210,7 @@ impl ZwlrLayerSurfaceV1RequestHandler for ZwlrLayerSurfaceV1 { Ok(()) } - fn ack_configure(&self, req: AckConfigure, _slf: &Rc) -> Result<(), Self::Error> { - self.acked_serial.set(Some(req.serial)); + fn ack_configure(&self, _req: AckConfigure, _slf: &Rc) -> Result<(), Self::Error> { Ok(()) } @@ -238,7 +227,6 @@ impl ZwlrLayerSurfaceV1RequestHandler for ZwlrLayerSurfaceV1 { } let mut pending = self.pending(); pending.layer = Some(req.layer); - pending.any = true; Ok(()) } } From 06ac1189d090307fbe1bc8a6846f69feda0294c7 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Wed, 8 May 2024 13:00:23 +0200 Subject: [PATCH 09/14] layer-shell: send configure event when output size changes --- src/ifs/wl_surface/zwlr_layer_surface_v1.rs | 7 ++++++- src/tree/output.rs | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/ifs/wl_surface/zwlr_layer_surface_v1.rs b/src/ifs/wl_surface/zwlr_layer_surface_v1.rs index 635770e0..e2e274f3 100644 --- a/src/ifs/wl_surface/zwlr_layer_surface_v1.rs +++ b/src/ifs/wl_surface/zwlr_layer_surface_v1.rs @@ -292,7 +292,7 @@ impl ZwlrLayerSurfaceV1 { self.pos.get() } - pub fn compute_position(&self) { + fn compute_position(&self) { let Some(global) = self.output.get() else { return; }; @@ -326,6 +326,11 @@ impl ZwlrLayerSurfaceV1 { self.client.state.tree_changed(); } + pub fn output_resized(&self) { + self.configure(); + self.compute_position(); + } + pub fn destroy_node(&self) { self.link.set(None); self.mapped.set(false); diff --git a/src/tree/output.rs b/src/tree/output.rs index 21df694c..0aabc9f4 100644 --- a/src/tree/output.rs +++ b/src/tree/output.rs @@ -558,7 +558,7 @@ impl OutputNode { } for layer in &self.layers { for surface in layer.iter() { - surface.compute_position(); + surface.output_resized(); } } self.global.send_mode(); From a5d6b0f2659f32e011659ef7d8de933a88974271 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Wed, 8 May 2024 13:04:19 +0200 Subject: [PATCH 10/14] layer-shell: consider sub-surface extents during positioning --- src/ifs/wl_surface/zwlr_layer_surface_v1.rs | 28 ++++++++++----------- src/renderer.rs | 13 +++------- src/tree/output.rs | 2 +- 3 files changed, 18 insertions(+), 25 deletions(-) diff --git a/src/ifs/wl_surface/zwlr_layer_surface_v1.rs b/src/ifs/wl_surface/zwlr_layer_surface_v1.rs index e2e274f3..6f974fff 100644 --- a/src/ifs/wl_surface/zwlr_layer_surface_v1.rs +++ b/src/ifs/wl_surface/zwlr_layer_surface_v1.rs @@ -45,7 +45,7 @@ pub struct ZwlrLayerSurfaceV1 { pub output: Rc, pub namespace: String, pub tracker: Tracker, - output_pos: Cell, + output_extents: Cell, pos: Cell, mapped: Cell, layer: Cell, @@ -106,7 +106,7 @@ impl ZwlrLayerSurfaceV1 { output: output.clone(), namespace: namespace.to_string(), tracker: Default::default(), - output_pos: Default::default(), + output_extents: Default::default(), pos: Default::default(), mapped: Cell::new(false), layer: Cell::new(layer), @@ -284,19 +284,16 @@ impl ZwlrLayerSurfaceV1 { } } - pub fn output_position(&self) -> Rect { - self.output_pos.get() - } - - pub fn position(&self) -> Rect { - self.pos.get() + pub fn output_extents(&self) -> Rect { + self.output_extents.get() } fn compute_position(&self) { let Some(global) = self.output.get() else { return; }; - let (width, height) = self.size.get(); + let extents = self.surface.extents.get(); + let (width, height) = extents.size(); let mut anchor = self.anchor.get(); if anchor == 0 { anchor = LEFT | RIGHT | TOP | BOTTOM; @@ -320,9 +317,11 @@ impl ZwlrLayerSurfaceV1 { } let o_rect = Rect::new_sized(x1, y1, width, height).unwrap(); let a_rect = o_rect.move_(opos.x1(), opos.y1()); - self.output_pos.set(o_rect); + self.output_extents.set(o_rect); self.pos.set(a_rect); - self.surface.set_absolute_position(a_rect.x1(), a_rect.y1()); + let abs_x = a_rect.x1() - extents.x1(); + let abs_y = a_rect.y1() - extents.y1(); + self.surface.set_absolute_position(abs_x, abs_y); self.client.state.tree_changed(); } @@ -360,9 +359,7 @@ impl SurfaceExt for ZwlrLayerSurfaceV1 { if !buffer_is_some { self.destroy_node(); } else { - let pos = self.pos.get(); - let (width, height) = self.size.get(); - if width != pos.width() || height != pos.height() { + if self.surface.extents.get().size() != self.pos.get().size() { self.compute_position(); } } @@ -437,7 +434,8 @@ impl Node for ZwlrLayerSurfaceV1 { tree: &mut Vec, _usecase: FindTreeUsecase, ) -> FindTreeResult { - self.surface.find_tree_at_(x, y, tree) + let (dx, dy) = self.surface.extents.get().position(); + self.surface.find_tree_at_(x + dx, y + dy, tree) } fn node_render(&self, renderer: &mut Renderer, x: i32, y: i32, _bounds: Option<&Rect>) { diff --git a/src/renderer.rs b/src/renderer.rs index 6821c49b..85244e24 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -103,12 +103,8 @@ impl Renderer<'_> { macro_rules! render_layer { ($layer:expr) => { for ls in $layer.iter() { - let pos = ls.position(); - self.render_layer_surface( - ls.deref(), - x + pos.x1() - opos.x1(), - y + pos.y1() - opos.y1(), - ); + let pos = ls.output_extents(); + self.render_layer_surface(ls.deref(), x + pos.x1(), y + pos.y1()); self.base.ops.push(GfxApiOpt::Sync); } }; @@ -539,8 +535,7 @@ impl Renderer<'_> { } pub fn render_layer_surface(&mut self, surface: &ZwlrLayerSurfaceV1, x: i32, y: i32) { - let body = surface.position().at_point(x, y); - let body = self.base.scale_rect(body); - self.render_surface(&surface.surface, x, y, Some(&body)); + let (dx, dy) = surface.surface.extents.get().position(); + self.render_surface(&surface.surface, x - dx, y - dy, None); } } diff --git a/src/tree/output.rs b/src/tree/output.rs index 0aabc9f4..459046ea 100644 --- a/src/tree/output.rs +++ b/src/tree/output.rs @@ -582,7 +582,7 @@ impl OutputNode { let len = tree.len(); for layer in layers.iter().copied() { for surface in self.layers[layer as usize].rev_iter() { - let pos = surface.output_position(); + let pos = surface.output_extents(); if pos.contains(x, y) { let (x, y) = pos.translate(x, y); if surface.node_find_tree_at(x, y, tree, usecase) From 8dc31110b9653396f855c0085db883662169d276 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Tue, 7 May 2024 16:18:10 +0200 Subject: [PATCH 11/14] layer-shell: implement margin --- src/ifs/wl_surface/zwlr_layer_surface_v1.rs | 45 ++++++++++++++++----- 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/src/ifs/wl_surface/zwlr_layer_surface_v1.rs b/src/ifs/wl_surface/zwlr_layer_surface_v1.rs index 6f974fff..856ce5d0 100644 --- a/src/ifs/wl_surface/zwlr_layer_surface_v1.rs +++ b/src/ifs/wl_surface/zwlr_layer_surface_v1.rs @@ -187,6 +187,11 @@ impl ZwlrLayerSurfaceV1RequestHandler for ZwlrLayerSurfaceV1 { fn set_margin(&self, req: SetMargin, _slf: &Rc) -> Result<(), Self::Error> { let mut pending = self.pending(); + for s in [req.top, req.right, req.bottom, req.left] { + if (s as i64).abs() > u16::MAX as i64 { + return Err(ZwlrLayerSurfaceV1Error::ExcessiveMargin); + } + } pending.margin = Some((req.top, req.right, req.bottom, req.left)); Ok(()) } @@ -269,7 +274,21 @@ impl ZwlrLayerSurfaceV1 { return; }; let (mut width, mut height) = self.size.get(); - let (available_width, available_height) = global.position().size(); + let (mt, mr, mb, ml) = self.margin.get(); + let (mut available_width, mut available_height) = global.position().size(); + let anchor = self.anchor.get(); + if anchor.contains(LEFT) { + available_width -= ml; + } + if anchor.contains(RIGHT) { + available_width -= mr; + } + if anchor.contains(TOP) { + available_height -= mt; + } + if anchor.contains(BOTTOM) { + available_height -= mb; + } if width == 0 { width = available_width; } @@ -298,22 +317,24 @@ impl ZwlrLayerSurfaceV1 { if anchor == 0 { anchor = LEFT | RIGHT | TOP | BOTTOM; } + let (mt, mr, mb, ml) = self.margin.get(); let opos = global.pos.get(); + let (owidth, oheight) = opos.size(); let mut x1 = 0; let mut y1 = 0; - if anchor.contains(LEFT) { - if anchor.contains(RIGHT) { - x1 += (opos.width() - width) / 2; - } + if anchor.contains(LEFT | RIGHT) { + x1 = (owidth - width - ml - mr) / 2; + } else if anchor.contains(LEFT) { + x1 = ml; } else if anchor.contains(RIGHT) { - x1 += opos.width() - width; + x1 = owidth - width - mr; } - if anchor.contains(TOP) { - if anchor.contains(BOTTOM) { - y1 += (opos.height() - height) / 2; - } + if anchor.contains(TOP | BOTTOM) { + y1 = (oheight - height - mt - mb) / 2; + } else if anchor.contains(TOP) { + y1 = mt; } else if anchor.contains(BOTTOM) { - y1 += opos.height() - height; + y1 = oheight - height - mb; } let o_rect = Rect::new_sized(x1, y1, width, height).unwrap(); let a_rect = o_rect.move_(opos.x1(), opos.y1()); @@ -473,6 +494,8 @@ pub enum ZwlrLayerSurfaceV1Error { UnknownLayer(u32), #[error("Surface size must not be larger than 65535x65535")] ExcessiveSize, + #[error("Margin must not be larger than 65535")] + ExcessiveMargin, #[error("Unknown anchor {0}")] UnknownAnchor(u32), #[error("Unknown keyboard interactivity {0}")] From 8a91c070be4416b9c6b2649f8f6242b3ffe5943b Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Tue, 7 May 2024 20:19:41 +0200 Subject: [PATCH 12/14] layer-shell: implement exclusive zones --- docs/features.md | 89 +++++------ src/compositor.rs | 4 + src/ifs/wl_surface/zwlr_layer_surface_v1.rs | 169 ++++++++++++++++++-- src/ifs/zwlr_layer_shell_v1.rs | 2 +- src/rect.rs | 5 + src/renderer.rs | 6 +- src/tasks/connector.rs | 5 + src/tree/output.rs | 106 ++++++++---- wire/zwlr_layer_surface_v1.txt | 4 + 9 files changed, 300 insertions(+), 90 deletions(-) diff --git a/docs/features.md b/docs/features.md index 3d1feb0e..ff1b7d7f 100644 --- a/docs/features.md +++ b/docs/features.md @@ -126,54 +126,53 @@ Jay's supports leasing VR headsets to applications. Jay supports the following wayland protocols: -| Global | Version | Privileged | -|-----------------------------------------|:-----------------|---------------| -| ext_foreign_toplevel_list_v1 | 1 | Yes | -| ext_idle_notifier_v1 | 1 | Yes | -| ext_session_lock_manager_v1 | 1 | Yes | -| ext_transient_seat_manager_v1 | 1[^ts_rejected] | Yes | -| org_kde_kwin_server_decoration_manager | 1 | | -| wl_compositor | 6[^no_touch] | | -| wl_data_device_manager | 3 | | -| wl_drm | 2 | | -| wl_output | 4 | | -| wl_seat | 9 | | -| wl_shm | 2 | | -| wl_subcompositor | 1 | | -| wp_alpha_modifier_v1 | 1 | | -| wp_content_type_manager_v1 | 1 | | -| wp_cursor_shape_manager_v1 | 1 | | -| wp_drm_lease_device_v1 | 1 | | -| wp_fractional_scale_manager_v1 | 1 | | -| wp_linux_drm_syncobj_manager_v1 | 1 | | -| wp_presentation | 1 | | -| wp_security_context_manager_v1 | 1 | | -| wp_single_pixel_buffer_manager_v1 | 1 | | -| wp_tearing_control_manager_v1 | 1[^no_tearing] | | -| wp_viewporter | 1 | | -| xdg_activation_v1 | 1 | | -| xdg_toplevel_drag_manager_v1 | 1 | | -| xdg_wm_base | 6 | | -| xdg_wm_dialog_v1 | 1 | | -| zwlr_data_control_manager_v1 | 2 | Yes | -| zwlr_layer_shell_v1 | 4[^no_exclusive] | No[^lsaccess] | -| zwlr_screencopy_manager_v1 | 3 | Yes | -| zwp_idle_inhibit_manager_v1 | 1 | | -| zwp_input_method_manager_v2 | 1 | Yes | -| zwp_linux_dmabuf_v1 | 5 | | -| zwp_pointer_constraints_v1 | 1 | | -| zwp_pointer_gestures_v1 | 3 | | -| zwp_primary_selection_device_manager_v1 | 1 | | -| zwp_relative_pointer_manager_v1 | 1 | | -| zwp_tablet_manager_v2 | 1 | | -| zwp_text_input_manager_v3 | 1 | | -| zwp_virtual_keyboard_manager_v1 | 1 | Yes | -| zxdg_decoration_manager_v1 | 1 | | -| zxdg_output_manager_v1 | 3 | | +| Global | Version | Privileged | +|-----------------------------------------|:----------------|---------------| +| ext_foreign_toplevel_list_v1 | 1 | Yes | +| ext_idle_notifier_v1 | 1 | Yes | +| ext_session_lock_manager_v1 | 1 | Yes | +| ext_transient_seat_manager_v1 | 1[^ts_rejected] | Yes | +| org_kde_kwin_server_decoration_manager | 1 | | +| wl_compositor | 6[^no_touch] | | +| wl_data_device_manager | 3 | | +| wl_drm | 2 | | +| wl_output | 4 | | +| wl_seat | 9 | | +| wl_shm | 2 | | +| wl_subcompositor | 1 | | +| wp_alpha_modifier_v1 | 1 | | +| wp_content_type_manager_v1 | 1 | | +| wp_cursor_shape_manager_v1 | 1 | | +| wp_drm_lease_device_v1 | 1 | | +| wp_fractional_scale_manager_v1 | 1 | | +| wp_linux_drm_syncobj_manager_v1 | 1 | | +| wp_presentation | 1 | | +| wp_security_context_manager_v1 | 1 | | +| wp_single_pixel_buffer_manager_v1 | 1 | | +| wp_tearing_control_manager_v1 | 1[^no_tearing] | | +| wp_viewporter | 1 | | +| xdg_activation_v1 | 1 | | +| xdg_toplevel_drag_manager_v1 | 1 | | +| xdg_wm_base | 6 | | +| xdg_wm_dialog_v1 | 1 | | +| zwlr_data_control_manager_v1 | 2 | Yes | +| zwlr_layer_shell_v1 | 5 | No[^lsaccess] | +| zwlr_screencopy_manager_v1 | 3 | Yes | +| zwp_idle_inhibit_manager_v1 | 1 | | +| zwp_input_method_manager_v2 | 1 | Yes | +| zwp_linux_dmabuf_v1 | 5 | | +| zwp_pointer_constraints_v1 | 1 | | +| zwp_pointer_gestures_v1 | 3 | | +| zwp_primary_selection_device_manager_v1 | 1 | | +| zwp_relative_pointer_manager_v1 | 1 | | +| zwp_tablet_manager_v2 | 1 | | +| zwp_text_input_manager_v3 | 1 | | +| zwp_virtual_keyboard_manager_v1 | 1 | Yes | +| zxdg_decoration_manager_v1 | 1 | | +| zxdg_output_manager_v1 | 3 | | [^no_touch]: Touch input is not supported. [^no_tearing]: Tearing screen updates are not supported. -[^no_exclusive]: Exclusive zones are not supported. [^lsaccess]: Sandboxes can restrict access to this protocol. [^ts_rejected]: Seat creation is always rejected. diff --git a/src/compositor.rs b/src/compositor.rs index 065629a6..f8c427aa 100644 --- a/src/compositor.rs +++ b/src/compositor.rs @@ -449,6 +449,10 @@ fn create_dummy_output(state: &Rc) { workspace: Default::default(), seat_state: Default::default(), layers: Default::default(), + exclusive_zones: Default::default(), + workspace_rect: Default::default(), + non_exclusive_rect_rel: Default::default(), + non_exclusive_rect: Default::default(), render_data: Default::default(), state: state.clone(), is_dummy: true, diff --git a/src/ifs/wl_surface/zwlr_layer_surface_v1.rs b/src/ifs/wl_surface/zwlr_layer_surface_v1.rs index 856ce5d0..cd531698 100644 --- a/src/ifs/wl_surface/zwlr_layer_surface_v1.rs +++ b/src/ifs/wl_surface/zwlr_layer_surface_v1.rs @@ -52,22 +52,59 @@ pub struct ZwlrLayerSurfaceV1 { requested_serial: NumCell, size: Cell<(i32, i32)>, anchor: Cell, - exclusive_zone: Cell, + exclusive_zone: Cell, margin: Cell<(i32, i32, i32, i32)>, keyboard_interactivity: Cell, link: Cell>>>, seat_state: NodeSeatState, last_configure: Cell<(i32, i32)>, + exclusive_edge: Cell>, + exclusive_size: Cell, +} + +#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] +pub struct ExclusiveSize { + pub top: i32, + pub right: i32, + pub bottom: i32, + pub left: i32, +} + +impl ExclusiveSize { + pub fn is_empty(&self) -> bool { + *self == ExclusiveSize::default() + } + + pub fn is_not_empty(&self) -> bool { + !self.is_empty() + } + + pub fn max(&self, other: &Self) -> Self { + Self { + top: self.top.max(other.top), + right: self.right.max(other.right), + bottom: self.bottom.max(other.bottom), + left: self.left.max(other.left), + } + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum ExclusiveZone { + MoveSelf, + FixedSelf, + Acquire(i32), } #[derive(Default)] pub struct PendingLayerSurfaceData { size: Option<(i32, i32)>, anchor: Option, - exclusive_zone: Option, + exclusive_zone: Option, margin: Option<(i32, i32, i32, i32)>, keyboard_interactivity: Option, layer: Option, + exclusive_edge: Option, } impl PendingLayerSurfaceData { @@ -113,12 +150,14 @@ impl ZwlrLayerSurfaceV1 { requested_serial: Default::default(), size: Cell::new((0, 0)), anchor: Cell::new(0), - exclusive_zone: Cell::new(0), + exclusive_zone: Cell::new(ExclusiveZone::MoveSelf), margin: Cell::new((0, 0, 0, 0)), keyboard_interactivity: Cell::new(0), link: Cell::new(None), seat_state: Default::default(), last_configure: Default::default(), + exclusive_edge: Default::default(), + exclusive_size: Default::default(), } } @@ -181,7 +220,16 @@ impl ZwlrLayerSurfaceV1RequestHandler for ZwlrLayerSurfaceV1 { _slf: &Rc, ) -> Result<(), Self::Error> { let mut pending = self.pending(); - pending.exclusive_zone = Some(req.zone); + let zone = if req.zone < 0 { + ExclusiveZone::FixedSelf + } else if req.zone == 0 { + ExclusiveZone::MoveSelf + } else if req.zone > u16::MAX as i32 { + return Err(ZwlrLayerSurfaceV1Error::ExcessiveExclusive); + } else { + ExclusiveZone::Acquire(req.zone) + }; + pending.exclusive_zone = Some(zone); Ok(()) } @@ -234,9 +282,73 @@ impl ZwlrLayerSurfaceV1RequestHandler for ZwlrLayerSurfaceV1 { pending.layer = Some(req.layer); Ok(()) } + + fn set_exclusive_edge( + &self, + req: SetExclusiveEdge, + _slf: &Rc, + ) -> Result<(), Self::Error> { + if req.edge & !(LEFT | RIGHT | TOP | BOTTOM) != 0 { + return Err(ZwlrLayerSurfaceV1Error::UnknownAnchor(req.edge)); + } + if req.edge.count_ones() > 1 { + return Err(ZwlrLayerSurfaceV1Error::TooManyExclusiveEdges); + } + let mut pending = self.pending(); + if req.edge == 0 { + pending.exclusive_edge = None; + } else { + pending.exclusive_edge = Some(req.edge); + } + Ok(()) + } } impl ZwlrLayerSurfaceV1 { + pub fn exclusive_size(&self) -> ExclusiveSize { + self.exclusive_size.get() + } + + fn update_exclusive_size(&self) { + let exclusive_edge = { + if let Some(ee) = self.exclusive_edge.get() { + Some(ee) + } else { + let anchor = self.anchor.get(); + let edges = anchor.count_ones(); + if edges == 1 { + Some(anchor) + } else if edges == 3 { + match (!anchor) & (TOP | BOTTOM | LEFT | RIGHT) { + TOP => Some(BOTTOM), + BOTTOM => Some(TOP), + LEFT => Some(RIGHT), + RIGHT => Some(LEFT), + _ => None, + } + } else { + None + } + } + }; + let mut exclusive_size = ExclusiveSize::default(); + if let (ExclusiveZone::Acquire(s), Some(edge)) = (self.exclusive_zone.get(), exclusive_edge) + { + match edge { + TOP => exclusive_size.top = s, + RIGHT => exclusive_size.right = s, + BOTTOM => exclusive_size.bottom = s, + LEFT => exclusive_size.left = s, + _ => {} + } + } + if self.exclusive_size.replace(exclusive_size) != exclusive_size { + if let Some(output) = self.output.node.get() { + output.update_exclusive_zones(); + } + } + } + fn pre_commit(&self, pending: &mut PendingState) -> Result<(), ZwlrLayerSurfaceV1Error> { let pending = pending.layer_surface.get_or_insert_default_ext(); if let Some(size) = pending.size.take() { @@ -257,6 +369,9 @@ impl ZwlrLayerSurfaceV1 { if let Some(layer) = pending.layer.take() { self.layer.set(layer); } + if let Some(edge) = pending.exclusive_edge.take() { + self.exclusive_edge.set(Some(edge)); + } let anchor = self.anchor.get(); let (width, height) = self.size.get(); if width == 0 && !anchor.contains(LEFT | RIGHT) { @@ -265,17 +380,25 @@ impl ZwlrLayerSurfaceV1 { if height == 0 && !anchor.contains(TOP | BOTTOM) { return Err(ZwlrLayerSurfaceV1Error::HeightZero); } + if let Some(ee) = self.exclusive_edge.get() { + if !self.anchor.get().contains(ee) { + return Err(ZwlrLayerSurfaceV1Error::ExclusiveEdgeNotAnchored); + } + } self.configure(); Ok(()) } fn configure(&self) { - let Some(global) = self.output.get() else { + let Some(node) = self.output.node() else { return; }; let (mut width, mut height) = self.size.get(); let (mt, mr, mb, ml) = self.margin.get(); - let (mut available_width, mut available_height) = global.position().size(); + let (mut available_width, mut available_height) = match self.exclusive_zone.get() { + ExclusiveZone::MoveSelf => node.non_exclusive_rect.get().size(), + _ => node.global.pos.get().size(), + }; let anchor = self.anchor.get(); if anchor.contains(LEFT) { available_width -= ml; @@ -308,7 +431,7 @@ impl ZwlrLayerSurfaceV1 { } fn compute_position(&self) { - let Some(global) = self.output.get() else { + let Some(output) = self.output.node() else { return; }; let extents = self.surface.extents.get(); @@ -318,8 +441,12 @@ impl ZwlrLayerSurfaceV1 { anchor = LEFT | RIGHT | TOP | BOTTOM; } let (mt, mr, mb, ml) = self.margin.get(); - let opos = global.pos.get(); - let (owidth, oheight) = opos.size(); + let opos = output.global.pos.get(); + let rect = match self.exclusive_zone.get() { + ExclusiveZone::MoveSelf => output.non_exclusive_rect.get(), + _ => opos, + }; + let (owidth, oheight) = rect.size(); let mut x1 = 0; let mut y1 = 0; if anchor.contains(LEFT | RIGHT) { @@ -336,8 +463,8 @@ impl ZwlrLayerSurfaceV1 { } else if anchor.contains(BOTTOM) { y1 = oheight - height - mb; } - let o_rect = Rect::new_sized(x1, y1, width, height).unwrap(); - let a_rect = o_rect.move_(opos.x1(), opos.y1()); + let a_rect = Rect::new_sized(x1 + rect.x1(), y1 + rect.y1(), width, height).unwrap(); + let o_rect = a_rect.move_(-opos.x1(), -opos.y1()); self.output_extents.set(o_rect); self.pos.set(a_rect); let abs_x = a_rect.x1() - extents.x1(); @@ -351,6 +478,13 @@ impl ZwlrLayerSurfaceV1 { self.compute_position(); } + pub fn exclusive_zones_changed(&self) { + if self.exclusive_zone.get() != ExclusiveZone::MoveSelf { + return; + } + self.output_resized(); + } + pub fn destroy_node(&self) { self.link.set(None); self.mapped.set(false); @@ -358,6 +492,11 @@ impl ZwlrLayerSurfaceV1 { self.seat_state.destroy_node(self); self.client.state.tree_changed(); self.last_configure.take(); + if self.exclusive_size.take().is_not_empty() { + if let Some(node) = self.output.node() { + node.update_exclusive_zones(); + } + } } } @@ -383,12 +522,14 @@ impl SurfaceExt for ZwlrLayerSurfaceV1 { if self.surface.extents.get().size() != self.pos.get().size() { self.compute_position(); } + self.update_exclusive_size(); } } else if buffer_is_some { let layer = &output.layers[self.layer.get() as usize]; self.link.set(Some(layer.add_last(self.clone()))); self.mapped.set(true); self.compute_position(); + self.update_exclusive_size(); } if self.mapped.get() != was_mapped { output.update_visible(); @@ -500,6 +641,12 @@ pub enum ZwlrLayerSurfaceV1Error { UnknownAnchor(u32), #[error("Unknown keyboard interactivity {0}")] UnknownKi(u32), + #[error("Surface is not anchored at exclusive edge")] + ExclusiveEdgeNotAnchored, + #[error("Request must contain exactly one edge")] + TooManyExclusiveEdges, + #[error("Exclusive zone not be larger than 65535")] + ExcessiveExclusive, } efrom!(ZwlrLayerSurfaceV1Error, WlSurfaceError); efrom!(ZwlrLayerSurfaceV1Error, ClientError); diff --git a/src/ifs/zwlr_layer_shell_v1.rs b/src/ifs/zwlr_layer_shell_v1.rs index ec1b13b1..348af5ae 100644 --- a/src/ifs/zwlr_layer_shell_v1.rs +++ b/src/ifs/zwlr_layer_shell_v1.rs @@ -107,7 +107,7 @@ impl Global for ZwlrLayerShellV1Global { } fn version(&self) -> u32 { - 4 + 5 } fn required_caps(&self) -> ClientCaps { diff --git a/src/rect.rs b/src/rect.rs index c310632b..1426df12 100644 --- a/src/rect.rs +++ b/src/rect.rs @@ -86,6 +86,11 @@ impl Rect { Self::new(x1, y1, x1 + width, y1 + height) } + #[track_caller] + pub fn new_sized_unchecked(x1: i32, y1: i32, width: i32, height: i32) -> Self { + Self::new_sized(x1, y1, width, height).unwrap() + } + pub fn union(&self, other: Self) -> Self { Self { raw: RectRaw { diff --git a/src/renderer.rs b/src/renderer.rs index 85244e24..80bf94cc 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -120,10 +120,14 @@ impl Renderer<'_> { } else { render_layer!(output.layers[0]); render_layer!(output.layers[1]); + let non_exclusive_rect = output.non_exclusive_rect_rel.get(); + let (x, y) = non_exclusive_rect.translate_inv(x, y); { let c = theme.colors.bar_background.get(); self.base.fill_boxes2( - slice::from_ref(&Rect::new_sized(0, 0, opos.width(), th).unwrap()), + slice::from_ref( + &Rect::new_sized(0, 0, non_exclusive_rect.width(), th).unwrap(), + ), &c, x, y, diff --git a/src/tasks/connector.rs b/src/tasks/connector.rs index 901b439a..09184858 100644 --- a/src/tasks/connector.rs +++ b/src/tasks/connector.rs @@ -147,6 +147,10 @@ impl ConnectorHandler { seat_state: Default::default(), global: global.clone(), layers: Default::default(), + exclusive_zones: Default::default(), + workspace_rect: Default::default(), + non_exclusive_rect: Default::default(), + non_exclusive_rect_rel: Default::default(), render_data: RefCell::new(OutputRenderData { active_workspace: None, underline: Default::default(), @@ -169,6 +173,7 @@ impl ConnectorHandler { hardware_cursor_needs_render: Cell::new(false), screencopies: Default::default(), }); + on.update_rects(); self.state .add_output_scale(on.global.persistent.scale.get()); let output_data = Rc::new(OutputData { diff --git a/src/tree/output.rs b/src/tree/output.rs index 459046ea..cc6b76fc 100644 --- a/src/tree/output.rs +++ b/src/tree/output.rs @@ -18,8 +18,8 @@ use { }, wl_surface::{ ext_session_lock_surface_v1::ExtSessionLockSurfaceV1, - zwlr_layer_surface_v1::ZwlrLayerSurfaceV1, SurfaceSendPreferredScaleVisitor, - SurfaceSendPreferredTransformVisitor, + zwlr_layer_surface_v1::{ExclusiveSize, ZwlrLayerSurfaceV1}, + SurfaceSendPreferredScaleVisitor, SurfaceSendPreferredTransformVisitor, }, zwlr_layer_shell_v1::{BACKGROUND, BOTTOM, OVERLAY, TOP}, zwlr_screencopy_frame_v1::ZwlrScreencopyFrameV1, @@ -47,7 +47,7 @@ use { std::{ cell::{Cell, RefCell}, fmt::{Debug, Formatter}, - ops::{Deref, Sub}, + ops::Deref, rc::Rc, }, }; @@ -61,6 +61,10 @@ pub struct OutputNode { pub workspace: CloneCell>>, pub seat_state: NodeSeatState, pub layers: [LinkedList>; 4], + pub exclusive_zones: Cell, + pub workspace_rect: Cell, + pub non_exclusive_rect: Cell, + pub non_exclusive_rect_rel: Cell, pub render_data: RefCell, pub state: Rc, pub is_dummy: bool, @@ -94,6 +98,26 @@ pub async fn output_render_data(state: Rc) { } impl OutputNode { + pub fn update_exclusive_zones(self: &Rc) { + let mut exclusive = ExclusiveSize::default(); + for layer in &self.layers { + for surface in layer.iter() { + exclusive = exclusive.max(&surface.exclusive_size()); + } + } + if self.exclusive_zones.replace(exclusive) != exclusive { + self.update_rects(); + for layer in &self.layers { + for surface in layer.iter() { + surface.exclusive_zones_changed(); + } + } + if let Some(c) = self.workspace.get() { + c.change_extents(&self.workspace_rect.get()); + } + } + } + pub fn add_screencast(&self, sc: &Rc) { self.screencasts.set((sc.client.id, sc.id), sc.clone()); self.screencast_changed(); @@ -222,9 +246,9 @@ impl OutputNode { } pub fn on_spaces_changed(self: &Rc) { - self.schedule_update_render_data(); + self.update_rects(); if let Some(c) = self.workspace.get() { - c.change_extents(&self.workspace_rect()); + c.change_extents(&self.workspace_rect.get()); } } @@ -281,7 +305,7 @@ impl OutputNode { texture_height = (th as f64 * scale).round() as _; } let active_id = self.workspace.get().map(|w| w.id); - let output_width = self.global.pos.get().width(); + let output_width = self.non_exclusive_rect.get().width(); rd.underline = Rect::new_sized(0, th, output_width, 1).unwrap(); for ws in self.workspaces.iter() { let old_tex = ws.title_texture.take(); @@ -430,7 +454,7 @@ impl OutputNode { if let Some(fs) = ws.fullscreen.get() { fs.tl_change_extents(&self.global.pos.get()); } - ws.change_extents(&self.workspace_rect()); + ws.change_extents(&self.workspace_rect.get()); for seat in seats { ws.clone().node_do_focus(&seat, Direction::Unspecified); } @@ -478,16 +502,29 @@ impl OutputNode { ws } - fn workspace_rect(&self) -> Rect { + pub fn update_rects(self: &Rc) { let rect = self.global.pos.get(); let th = self.state.theme.sizes.title_height.get(); - Rect::new_sized( - rect.x1(), - rect.y1() + th + 1, - rect.width(), - rect.height().sub(th + 1).max(0), - ) - .unwrap() + let exclusive = self.exclusive_zones.get(); + let y1 = rect.y1() + exclusive.top; + let x2 = rect.x2() - exclusive.right; + let y2 = rect.y2() - exclusive.bottom; + let x1 = rect.x1() + exclusive.left; + let width = (x2 - x1).max(0); + let height = (y2 - y1).max(0); + self.non_exclusive_rect + .set(Rect::new_sized_unchecked(x1, y1, width, height)); + self.non_exclusive_rect_rel.set(Rect::new_sized_unchecked( + exclusive.left, + exclusive.top, + width, + height, + )); + let y1 = y1 + th + 1; + let height = (y2 - y1).max(0); + self.workspace_rect + .set(Rect::new_sized_unchecked(x1, y1, width, height)); + self.schedule_update_render_data(); } pub fn set_position(self: &Rc, x: i32, y: i32) { @@ -546,7 +583,7 @@ impl OutputNode { self.global.persistent.pos.set((rect.x1(), rect.y1())); self.global.pos.set(*rect); self.state.root.update_extents(); - self.schedule_update_render_data(); + self.update_rects(); if let Some(ls) = self.lock_surface.get() { ls.change_extents(*rect); } @@ -554,7 +591,7 @@ impl OutputNode { if let Some(fs) = c.fullscreen.get() { fs.tl_change_extents(rect); } - c.change_extents(&self.workspace_rect()); + c.change_extents(&self.workspace_rect.get()); } for layer in &self.layers { for surface in layer.iter() { @@ -657,6 +694,7 @@ impl OutputNode { Some(p) => p, _ => return, }; + let (x, y) = self.non_exclusive_rect_rel.get().translate(x, y); if y >= self.state.theme.sizes.title_height.get() { return; } @@ -844,21 +882,25 @@ impl Node for OutputNode { fs.tl_as_node().node_find_tree_at(x, y, tree, usecase) } else { let mut search_layers = true; - if y < bar_height { - search_layers = false; - } else { - if let Some(ws) = self.workspace.get() { - let y = y - bar_height; - let len = tree.len(); - tree.push(FoundNode { - node: ws.clone(), - x, - y, - }); - match ws.node_find_tree_at(x, y, tree, usecase) { - FindTreeResult::AcceptsInput => search_layers = false, - FindTreeResult::Other => { - tree.truncate(len); + let non_exclusive_rect = self.non_exclusive_rect_rel.get(); + if non_exclusive_rect.contains(x, y) { + let (x, y) = non_exclusive_rect.translate(x, y); + if y < bar_height { + search_layers = false; + } else { + if let Some(ws) = self.workspace.get() { + let y = y - bar_height; + let len = tree.len(); + tree.push(FoundNode { + node: ws.clone(), + x, + y, + }); + match ws.node_find_tree_at(x, y, tree, usecase) { + FindTreeResult::AcceptsInput => search_layers = false, + FindTreeResult::Other => { + tree.truncate(len); + } } } } diff --git a/wire/zwlr_layer_surface_v1.txt b/wire/zwlr_layer_surface_v1.txt index 1d4680de..2d7b382b 100644 --- a/wire/zwlr_layer_surface_v1.txt +++ b/wire/zwlr_layer_surface_v1.txt @@ -38,6 +38,10 @@ request set_layer (since = 2) { layer: u32, } +request set_exclusive_edge (since = 5) { + edge: u32, +} + # events event configure { From c6ef63a85f447c64a206f3c5d100eea80fbb5e46 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Mon, 6 May 2024 19:17:44 +0200 Subject: [PATCH 13/14] xdg-popup: add parent abstraction --- src/ifs/wl_surface/xdg_surface.rs | 100 +++++++++++++++--- src/ifs/wl_surface/xdg_surface/xdg_popup.rs | 109 ++++++-------------- 2 files changed, 115 insertions(+), 94 deletions(-) diff --git a/src/ifs/wl_surface/xdg_surface.rs b/src/ifs/wl_surface/xdg_surface.rs index 6b1d7b01..5117b6a9 100644 --- a/src/ifs/wl_surface/xdg_surface.rs +++ b/src/ifs/wl_surface/xdg_surface.rs @@ -7,7 +7,7 @@ use { ifs::{ wl_surface::{ xdg_surface::{ - xdg_popup::{XdgPopup, XdgPopupError}, + xdg_popup::{XdgPopup, XdgPopupError, XdgPopupParent}, xdg_toplevel::{XdgToplevel, WM_CAPABILITIES_SINCE}, }, PendingState, SurfaceExt, SurfaceRole, WlSurface, WlSurfaceError, @@ -17,14 +17,15 @@ use { leaks::Tracker, object::Object, rect::Rect, - tree::{FindTreeResult, FoundNode, OutputNode, WorkspaceNode}, + tree::{FindTreeResult, FoundNode, OutputNode, StackedNode, WorkspaceNode}, utils::{ - clonecell::CloneCell, copyhashmap::CopyHashMap, numcell::NumCell, option_ext::OptionExt, + clonecell::CloneCell, copyhashmap::CopyHashMap, hash_map_ext::HashMapExt, + linkedlist::LinkedNode, numcell::NumCell, option_ext::OptionExt, }, wire::{xdg_surface::*, WlSurfaceId, XdgPopupId, XdgSurfaceId}, }, std::{ - cell::{Cell, RefMut}, + cell::{Cell, RefCell, RefMut}, fmt::Debug, rc::Rc, }, @@ -65,11 +66,66 @@ pub struct XdgSurface { extents: Cell, pub absolute_desired_extents: Cell, ext: CloneCell>>, - popups: CopyHashMap>, + popups: CopyHashMap>, pub workspace: CloneCell>>, pub tracker: Tracker, } +struct Popup { + parent: Rc, + popup: Rc, + display_link: RefCell>>>, + workspace_link: RefCell>>>, +} + +impl XdgPopupParent for Popup { + fn position(&self) -> Rect { + self.parent.absolute_desired_extents.get() + } + + fn remove_popup(&self) { + self.parent.popups.remove(&self.popup.id); + } + + fn output(&self) -> Rc { + self.parent.surface.output.get() + } + + fn has_workspace_link(&self) -> bool { + self.workspace_link.borrow().is_some() + } + + fn post_commit(&self) { + let mut wl = self.workspace_link.borrow_mut(); + let mut dl = self.display_link.borrow_mut(); + let ws = match self.parent.workspace.get() { + Some(ws) => ws, + _ => { + log::info!("no ws"); + return; + } + }; + let surface = &self.popup.xdg.surface; + let state = &surface.client.state; + if surface.buffer.is_some() { + if wl.is_none() { + self.popup.xdg.set_workspace(&ws); + *wl = Some(ws.stacked.add_last(self.popup.clone())); + *dl = Some(state.root.stacked.add_last(self.popup.clone())); + state.tree_changed(); + self.popup.set_visible(self.parent.surface.visible.get()); + } + } else { + if wl.take().is_some() { + drop(wl); + drop(dl); + self.popup.set_visible(false); + self.popup.destroy_node(); + } + } + } +} + #[derive(Default, Debug)] pub struct PendingXdgSurfaceData { geometry: Option, @@ -139,7 +195,7 @@ impl XdgSurface { self.surface.set_output(&ws.output.get()); let pu = self.popups.lock(); for pu in pu.values() { - pu.xdg.set_workspace(ws); + pu.popup.xdg.set_workspace(ws); } } @@ -147,7 +203,7 @@ impl XdgSurface { self.surface.set_output(output); let pu = self.popups.lock(); for pu in pu.values() { - pu.xdg.set_output(output); + pu.popup.xdg.set_output(output); } } @@ -171,9 +227,8 @@ impl XdgSurface { fn destroy_node(&self) { self.workspace.set(None); self.surface.destroy_node(); - let popups = self.popups.lock(); - for popup in popups.values() { - popup.destroy_node(); + for popup in self.popups.lock().drain_values() { + popup.popup.destroy_node(); } } @@ -182,7 +237,8 @@ impl XdgSurface { self.surface.detach_node(false); let popups = self.popups.lock(); for popup in popups.values() { - popup.detach_node(); + let _v = popup.workspace_link.borrow_mut().take(); + popup.popup.detach_node(); } } @@ -279,11 +335,18 @@ impl XdgSurfaceRequestHandler for XdgSurface { ); return Err(XdgSurfaceError::AlreadyConstructed); } - let popup = Rc::new(XdgPopup::new(req.id, slf, parent.as_ref(), &positioner)?); + let popup = Rc::new(XdgPopup::new(req.id, slf, &positioner)?); track!(self.surface.client, popup); self.surface.client.add_client_obj(&popup)?; if let Some(parent) = &parent { - parent.popups.set(req.id, popup.clone()); + let user = Rc::new(Popup { + parent: parent.clone(), + popup: popup.clone(), + display_link: Default::default(), + workspace_link: Default::default(), + }); + popup.parent.set(Some(user.clone())); + parent.popups.set(req.id, user); } self.ext.set(Some(popup)); Ok(()) @@ -341,21 +404,26 @@ impl XdgSurface { fn update_popup_positions(&self) { let popups = self.popups.lock(); for popup in popups.values() { - popup.update_absolute_position(); + popup.popup.update_absolute_position(); } } fn set_visible(&self, visible: bool) { self.surface.set_visible(visible); for popup in self.popups.lock().values() { - popup.set_visible(visible); + popup.popup.set_visible(visible); } } fn restack_popups(&self) { + let state = &self.surface.client.state; for popup in self.popups.lock().values() { - popup.restack(); + if let Some(dl) = &*popup.display_link.borrow() { + state.root.stacked.add_last_existing(dl); + } + popup.popup.xdg.restack_popups(); } + state.tree_changed(); } } diff --git a/src/ifs/wl_surface/xdg_surface/xdg_popup.rs b/src/ifs/wl_surface/xdg_surface/xdg_popup.rs index a1880cc7..7e716bf1 100644 --- a/src/ifs/wl_surface/xdg_surface/xdg_popup.rs +++ b/src/ifs/wl_surface/xdg_surface/xdg_popup.rs @@ -16,10 +16,10 @@ use { rect::Rect, renderer::Renderer, tree::{ - FindTreeResult, FindTreeUsecase, FoundNode, Node, NodeId, NodeVisitor, StackedNode, - WorkspaceNode, + FindTreeResult, FindTreeUsecase, FoundNode, Node, NodeId, NodeVisitor, OutputNode, + StackedNode, }, - utils::{clonecell::CloneCell, linkedlist::LinkedNode}, + utils::clonecell::CloneCell, wire::{xdg_popup::*, XdgPopupId}, }, std::{ @@ -35,14 +35,20 @@ const INVALID_GRAB: u32 = 1; tree_id!(PopupId); +pub trait XdgPopupParent { + fn position(&self) -> Rect; + fn remove_popup(&self); + fn output(&self) -> Rc; + fn has_workspace_link(&self) -> bool; + fn post_commit(&self); +} + pub struct XdgPopup { - id: XdgPopupId, + pub id: XdgPopupId, node_id: PopupId, pub xdg: Rc, - pub(super) parent: CloneCell>>, + pub(super) parent: CloneCell>>, relative_position: Cell, - display_link: RefCell>>>, - workspace_link: RefCell>>>, pos: RefCell, pub tracker: Tracker, seat_state: NodeSeatState, @@ -59,7 +65,6 @@ impl XdgPopup { pub fn new( id: XdgPopupId, xdg: &Rc, - parent: Option<&Rc>, pos: &Rc, ) -> Result { let pos = pos.value(); @@ -70,10 +75,8 @@ impl XdgPopup { id, node_id: xdg.surface.client.state.node_ids.next(), xdg: xdg.clone(), - parent: CloneCell::new(parent.cloned()), + parent: Default::default(), relative_position: Cell::new(Default::default()), - display_link: RefCell::new(None), - workspace_link: RefCell::new(None), pos: RefCell::new(pos), tracker: Default::default(), seat_state: Default::default(), @@ -105,13 +108,13 @@ impl XdgPopup { .event(PopupDone { self_id: self.id }) } - fn update_position(&self, parent: &XdgSurface) { + fn update_position(&self, parent: &dyn XdgPopupParent) { let positioner = self.pos.borrow_mut(); - let parent_abs = parent.absolute_desired_extents.get(); + let parent_abs = parent.position(); let mut rel_pos = positioner.get_position(false, false); let mut abs_pos = rel_pos.move_(parent_abs.x1(), parent_abs.y1()); - if let Some(ws) = parent.workspace.get() { - let output_pos = ws.output.get().global.pos.get(); + { + let output_pos = parent.output().global.pos.get(); let mut overflow = output_pos.get_overflow(&abs_pos); if !overflow.is_contained() { let mut flip_x = positioner.ca.contains(CA_FLIP_X) && overflow.x_overflow(); @@ -202,7 +205,7 @@ impl XdgPopup { pub fn update_absolute_position(&self) { if let Some(parent) = self.parent.get() { let rel = self.relative_position.get(); - let parent = parent.absolute_desired_extents.get(); + let parent = parent.position(); self.xdg .set_absolute_desired_extents(&rel.move_(parent.x1(), parent.y1())); } @@ -214,15 +217,8 @@ impl XdgPopupRequestHandler for XdgPopup { fn destroy(&self, _req: Destroy, _slf: &Rc) -> Result<(), Self::Error> { self.destroy_node(); - { - if let Some(parent) = self.parent.take() { - parent.popups.remove(&self.id); - } - } self.xdg.ext.set(None); self.xdg.surface.client.remove_obj(self)?; - *self.display_link.borrow_mut() = None; - *self.workspace_link.borrow_mut() = None; Ok(()) } @@ -233,7 +229,7 @@ impl XdgPopupRequestHandler for XdgPopup { fn reposition(&self, req: Reposition, _slf: &Rc) -> Result<(), Self::Error> { *self.pos.borrow_mut() = self.xdg.surface.client.lookup(req.positioner)?.value(); if let Some(parent) = self.parent.get() { - self.update_position(&parent); + self.update_position(&*parent); let rel = self.relative_position.get(); self.send_repositioned(req.token); self.send_configure(rel.x1(), rel.y1(), rel.width(), rel.height()); @@ -244,10 +240,6 @@ impl XdgPopupRequestHandler for XdgPopup { } impl XdgPopup { - fn get_parent_workspace(&self) -> Option> { - self.parent.get()?.workspace.get() - } - pub fn set_visible(&self, visible: bool) { // log::info!("set visible = {}", visible); self.set_visible_prepared.set(false); @@ -256,27 +248,18 @@ impl XdgPopup { } pub fn destroy_node(&self) { - let _v = self.display_link.borrow_mut().take(); - let _v = self.workspace_link.borrow_mut().take(); self.xdg.destroy_node(); self.seat_state.destroy_node(self); + if let Some(parent) = self.parent.take() { + parent.remove_popup(); + } + self.send_popup_done(); } pub fn detach_node(&self) { - let _v = self.workspace_link.borrow_mut().take(); self.xdg.detach_node(); self.seat_state.destroy_node(self); } - - pub fn restack(&self) { - let state = &self.xdg.surface.client.state; - let dl = self.display_link.borrow(); - if let Some(dl) = &*dl { - state.root.stacked.add_last_existing(dl); - } - self.xdg.restack_popups(); - state.tree_changed(); - } } object_base! { @@ -287,9 +270,6 @@ object_base! { impl Object for XdgPopup { fn break_loops(&self) { self.destroy_node(); - self.parent.set(None); - *self.display_link.borrow_mut() = None; - *self.workspace_link.borrow_mut() = None; } } @@ -377,7 +357,10 @@ impl StackedNode for XdgPopup { } fn stacked_has_workspace_link(&self) -> bool { - self.workspace_link.borrow().is_some() + match self.parent.get() { + Some(p) => p.has_workspace_link(), + _ => false, + } } fn stacked_absolute_position_constrains_input(&self) -> bool { @@ -388,7 +371,7 @@ impl StackedNode for XdgPopup { impl XdgSurfaceExt for XdgPopup { fn initial_configure(self: Rc) -> Result<(), XdgSurfaceError> { if let Some(parent) = self.parent.get() { - self.update_position(&parent); + self.update_position(&*parent); let rel = self.relative_position.get(); self.send_configure(rel.x1(), rel.y1(), rel.width(), rel.height()); } @@ -396,38 +379,8 @@ impl XdgSurfaceExt for XdgPopup { } fn post_commit(self: Rc) { - let mut wl = self.workspace_link.borrow_mut(); - let mut dl = self.display_link.borrow_mut(); - let ws = match self.get_parent_workspace() { - Some(ws) => ws, - _ => { - log::info!("no ws"); - return; - } - }; - let surface = &self.xdg.surface; - let state = &surface.client.state; - if surface.buffer.is_some() { - if wl.is_none() { - self.xdg.set_workspace(&ws); - *wl = Some(ws.stacked.add_last(self.clone())); - *dl = Some(state.root.stacked.add_last(self.clone())); - state.tree_changed(); - self.set_visible( - self.parent - .get() - .map(|p| p.surface.visible.get()) - .unwrap_or(false), - ); - } - } else { - if wl.take().is_some() { - drop(wl); - drop(dl); - self.set_visible(false); - self.destroy_node(); - self.send_popup_done(); - } + if let Some(parent) = self.parent.get() { + parent.post_commit(); } } From 52afd94f6e8f4fb95cdbfd6f97ef107fa0bcf8ff Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Mon, 6 May 2024 20:41:10 +0200 Subject: [PATCH 14/14] layer-shell: implement popups --- deploy-notes.md | 2 + release-notes.md | 2 + src/client/objects.rs | 9 +- src/ifs/wl_surface/xdg_surface.rs | 40 ++++++- src/ifs/wl_surface/xdg_surface/xdg_popup.rs | 4 +- src/ifs/wl_surface/zwlr_layer_surface_v1.rs | 118 ++++++++++++++++++-- src/renderer.rs | 22 ++-- src/tree/display.rs | 4 +- src/tree/output.rs | 81 +++++++++----- 9 files changed, 225 insertions(+), 57 deletions(-) diff --git a/deploy-notes.md b/deploy-notes.md index 5d11b72b..b2c0fa6b 100644 --- a/deploy-notes.md +++ b/deploy-notes.md @@ -1,5 +1,7 @@ # Unreleased +- Needs jay-compositor release. + # 1.2.0 - Needs jay-config release. diff --git a/release-notes.md b/release-notes.md index 54972082..ea2793e5 100644 --- a/release-notes.md +++ b/release-notes.md @@ -1,5 +1,7 @@ # Unreleased +- Add remaining layer-shell features. + # 1.2.0 (2024-05-05) - Add support for wp-security-manager-v1. diff --git a/src/client/objects.rs b/src/client/objects.rs index 4616e014..599fafdf 100644 --- a/src/client/objects.rs +++ b/src/client/objects.rs @@ -17,7 +17,7 @@ use { wl_registry::WlRegistry, wl_seat::{tablet::zwp_tablet_tool_v2::ZwpTabletToolV2, wl_pointer::WlPointer, WlSeat}, wl_surface::{ - xdg_surface::{xdg_toplevel::XdgToplevel, XdgSurface}, + xdg_surface::{xdg_popup::XdgPopup, xdg_toplevel::XdgToplevel, XdgSurface}, WlSurface, }, wp_drm_lease_connector_v1::WpDrmLeaseConnectorV1, @@ -33,8 +33,8 @@ use { wire::{ JayOutputId, JayScreencastId, JayToplevelId, JayWorkspaceId, WlBufferId, WlDataSourceId, WlOutputId, WlPointerId, WlRegionId, WlRegistryId, WlSeatId, - WlSurfaceId, WpDrmLeaseConnectorV1Id, WpLinuxDrmSyncobjTimelineV1Id, XdgPositionerId, - XdgSurfaceId, XdgToplevelId, XdgWmBaseId, ZwlrDataControlSourceV1Id, + WlSurfaceId, WpDrmLeaseConnectorV1Id, WpLinuxDrmSyncobjTimelineV1Id, XdgPopupId, + XdgPositionerId, XdgSurfaceId, XdgToplevelId, XdgWmBaseId, ZwlrDataControlSourceV1Id, ZwpPrimarySelectionSourceV1Id, ZwpTabletToolV2Id, }, }, @@ -66,6 +66,7 @@ pub struct Objects { pub jay_toplevels: CopyHashMap>, pub drm_lease_outputs: CopyHashMap>, pub tablet_tools: CopyHashMap>, + pub xdg_popups: CopyHashMap>, ids: RefCell>, } @@ -98,6 +99,7 @@ impl Objects { jay_toplevels: Default::default(), drm_lease_outputs: Default::default(), tablet_tools: Default::default(), + xdg_popups: Default::default(), ids: RefCell::new(vec![]), } } @@ -134,6 +136,7 @@ impl Objects { self.jay_toplevels.clear(); self.drm_lease_outputs.clear(); self.tablet_tools.clear(); + self.xdg_popups.clear(); } pub fn id(&self, client_data: &Client) -> Result diff --git a/src/ifs/wl_surface/xdg_surface.rs b/src/ifs/wl_surface/xdg_surface.rs index 5117b6a9..b88c0d9a 100644 --- a/src/ifs/wl_surface/xdg_surface.rs +++ b/src/ifs/wl_surface/xdg_surface.rs @@ -19,8 +19,13 @@ use { rect::Rect, tree::{FindTreeResult, FoundNode, OutputNode, StackedNode, WorkspaceNode}, utils::{ - clonecell::CloneCell, copyhashmap::CopyHashMap, hash_map_ext::HashMapExt, - linkedlist::LinkedNode, numcell::NumCell, option_ext::OptionExt, + clonecell::CloneCell, + copyhashmap::CopyHashMap, + hash_map_ext::HashMapExt, + linkedlist::{LinkedList, LinkedNode}, + numcell::NumCell, + option_ext::OptionExt, + rc_eq::rc_eq, }, wire::{xdg_surface::*, WlSurfaceId, XdgPopupId, XdgSurfaceId}, }, @@ -66,6 +71,7 @@ pub struct XdgSurface { extents: Cell, pub absolute_desired_extents: Cell, ext: CloneCell>>, + popup_display_stack: CloneCell>>>, popups: CopyHashMap>, pub workspace: CloneCell>>, pub tracker: Tracker, @@ -111,7 +117,12 @@ impl XdgPopupParent for Popup { if wl.is_none() { self.popup.xdg.set_workspace(&ws); *wl = Some(ws.stacked.add_last(self.popup.clone())); - *dl = Some(state.root.stacked.add_last(self.popup.clone())); + *dl = Some( + self.parent + .popup_display_stack + .get() + .add_last(self.popup.clone()), + ); state.tree_changed(); self.popup.set_visible(self.parent.surface.visible.get()); } @@ -171,6 +182,7 @@ impl XdgSurface { extents: Cell::new(Default::default()), absolute_desired_extents: Cell::new(Default::default()), ext: Default::default(), + popup_display_stack: CloneCell::new(surface.client.state.root.stacked.clone()), popups: Default::default(), workspace: Default::default(), tracker: Default::default(), @@ -272,6 +284,19 @@ impl XdgSurface { p.xdg_surface.get_or_insert_default_ext() }) } + + pub fn set_popup_stack(&self, stack: &Rc>>) { + let prev = self.popup_display_stack.set(stack.clone()); + if rc_eq(&prev, stack) { + return; + } + for popup in self.popups.lock().values() { + if let Some(dl) = &*popup.display_link.borrow() { + stack.add_last_existing(dl); + } + popup.popup.xdg.set_popup_stack(stack); + } + } } impl XdgSurfaceRequestHandler for XdgSurface { @@ -416,14 +441,17 @@ impl XdgSurface { } fn restack_popups(&self) { - let state = &self.surface.client.state; + if self.popups.is_empty() { + return; + } + let stack = self.popup_display_stack.get(); for popup in self.popups.lock().values() { if let Some(dl) = &*popup.display_link.borrow() { - state.root.stacked.add_last_existing(dl); + stack.add_last_existing(dl); } popup.popup.xdg.restack_popups(); } - state.tree_changed(); + self.surface.client.state.tree_changed(); } } diff --git a/src/ifs/wl_surface/xdg_surface/xdg_popup.rs b/src/ifs/wl_surface/xdg_surface/xdg_popup.rs index 7e716bf1..05d58c8a 100644 --- a/src/ifs/wl_surface/xdg_surface/xdg_popup.rs +++ b/src/ifs/wl_surface/xdg_surface/xdg_popup.rs @@ -47,7 +47,7 @@ pub struct XdgPopup { pub id: XdgPopupId, node_id: PopupId, pub xdg: Rc, - pub(super) parent: CloneCell>>, + pub(in super::super) parent: CloneCell>>, relative_position: Cell, pos: RefCell, pub tracker: Tracker, @@ -273,7 +273,7 @@ impl Object for XdgPopup { } } -simple_add_obj!(XdgPopup); +dedicated_add_obj!(XdgPopup, XdgPopupId, xdg_popups); impl Node for XdgPopup { fn node_id(&self) -> NodeId { diff --git a/src/ifs/wl_surface/zwlr_layer_surface_v1.rs b/src/ifs/wl_surface/zwlr_layer_surface_v1.rs index cd531698..08f4547e 100644 --- a/src/ifs/wl_surface/zwlr_layer_surface_v1.rs +++ b/src/ifs/wl_surface/zwlr_layer_surface_v1.rs @@ -4,21 +4,32 @@ use { ifs::{ wl_output::OutputGlobalOpt, wl_seat::NodeSeatState, - wl_surface::{PendingState, SurfaceExt, SurfaceRole, WlSurface, WlSurfaceError}, + wl_surface::{ + xdg_surface::xdg_popup::{XdgPopup, XdgPopupParent}, + PendingState, SurfaceExt, SurfaceRole, WlSurface, WlSurfaceError, + }, zwlr_layer_shell_v1::{ZwlrLayerShellV1, OVERLAY}, }, leaks::Tracker, object::Object, rect::Rect, renderer::Renderer, - tree::{FindTreeResult, FindTreeUsecase, FoundNode, Node, NodeId, NodeVisitor}, - utils::{ - bitflags::BitflagsExt, linkedlist::LinkedNode, numcell::NumCell, option_ext::OptionExt, + tree::{ + FindTreeResult, FindTreeUsecase, FoundNode, Node, NodeId, NodeVisitor, OutputNode, + StackedNode, }, - wire::{zwlr_layer_surface_v1::*, WlSurfaceId, ZwlrLayerSurfaceV1Id}, + utils::{ + bitflags::BitflagsExt, + copyhashmap::CopyHashMap, + hash_map_ext::HashMapExt, + linkedlist::{LinkedList, LinkedNode}, + numcell::NumCell, + option_ext::OptionExt, + }, + wire::{zwlr_layer_surface_v1::*, WlSurfaceId, XdgPopupId, ZwlrLayerSurfaceV1Id}, }, std::{ - cell::{Cell, RefMut}, + cell::{Cell, RefCell, RefMut}, ops::Deref, rc::Rc, }, @@ -60,6 +71,7 @@ pub struct ZwlrLayerSurfaceV1 { last_configure: Cell<(i32, i32)>, exclusive_edge: Cell>, exclusive_size: Cell, + popups: CopyHashMap>, } #[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] @@ -96,6 +108,13 @@ pub enum ExclusiveZone { Acquire(i32), } +struct Popup { + parent: Rc, + popup: Rc, + stack: Rc>>, + stack_link: RefCell>>>, +} + #[derive(Default)] pub struct PendingLayerSurfaceData { size: Option<(i32, i32)>, @@ -158,6 +177,7 @@ impl ZwlrLayerSurfaceV1 { last_configure: Default::default(), exclusive_edge: Default::default(), exclusive_size: Default::default(), + popups: Default::default(), } } @@ -259,7 +279,21 @@ impl ZwlrLayerSurfaceV1RequestHandler for ZwlrLayerSurfaceV1 { Ok(()) } - fn get_popup(&self, _req: GetPopup, _slf: &Rc) -> Result<(), Self::Error> { + fn get_popup(&self, req: GetPopup, slf: &Rc) -> Result<(), Self::Error> { + let popup = self.client.lookup(req.popup)?; + if popup.parent.is_some() { + return Err(ZwlrLayerSurfaceV1Error::PopupHasParent); + } + let stack = self.client.state.root.stacked_above_layers.clone(); + popup.xdg.set_popup_stack(&stack); + let user = Rc::new(Popup { + parent: slf.clone(), + popup: popup.clone(), + stack, + stack_link: Default::default(), + }); + popup.parent.set(Some(user.clone())); + self.popups.set(popup.id, user); Ok(()) } @@ -268,6 +302,9 @@ impl ZwlrLayerSurfaceV1RequestHandler for ZwlrLayerSurfaceV1 { } fn destroy(&self, _req: Destroy, _slf: &Rc) -> Result<(), Self::Error> { + if self.popups.is_not_empty() { + return Err(ZwlrLayerSurfaceV1Error::HasPopups); + } self.destroy_node(); self.client.remove_obj(self)?; self.surface.unset_ext(); @@ -466,10 +503,15 @@ impl ZwlrLayerSurfaceV1 { let a_rect = Rect::new_sized(x1 + rect.x1(), y1 + rect.y1(), width, height).unwrap(); let o_rect = a_rect.move_(-opos.x1(), -opos.y1()); self.output_extents.set(o_rect); - self.pos.set(a_rect); + let a_rect_old = self.pos.replace(a_rect); let abs_x = a_rect.x1() - extents.x1(); let abs_y = a_rect.y1() - extents.y1(); self.surface.set_absolute_position(abs_x, abs_y); + if a_rect_old != a_rect { + for popup in self.popups.lock().values() { + popup.popup.update_absolute_position(); + } + } self.client.state.tree_changed(); } @@ -497,6 +539,19 @@ impl ZwlrLayerSurfaceV1 { node.update_exclusive_zones(); } } + for popup in self.popups.lock().drain_values() { + popup.popup.destroy_node(); + } + } + + pub fn set_visible(&self, visible: bool) { + self.surface.set_visible(visible); + if !visible { + for popup in self.popups.lock().drain_values() { + popup.popup.set_visible(false); + popup.popup.destroy_node(); + } + } } } @@ -605,6 +660,49 @@ impl Node for ZwlrLayerSurfaceV1 { } } +impl XdgPopupParent for Popup { + fn position(&self) -> Rect { + self.parent.pos.get() + } + + fn remove_popup(&self) { + self.parent.popups.remove(&self.popup.id); + } + + fn output(&self) -> Rc { + self.parent.surface.output.get() + } + + fn has_workspace_link(&self) -> bool { + false + } + + fn post_commit(&self) { + let mut dl = self.stack_link.borrow_mut(); + let output = self.output(); + let surface = &self.popup.xdg.surface; + let state = &surface.client.state; + if surface.buffer.is_some() { + if dl.is_none() { + if self.parent.surface.visible.get() { + self.popup.xdg.set_output(&output); + *dl = Some(self.stack.add_last(self.popup.clone())); + state.tree_changed(); + self.popup.set_visible(self.parent.surface.visible.get()); + } else { + self.popup.destroy_node(); + } + } + } else { + if dl.take().is_some() { + drop(dl); + self.popup.set_visible(false); + self.popup.destroy_node(); + } + } + } +} + object_base! { self = ZwlrLayerSurfaceV1; version = self.shell.version; @@ -647,6 +745,10 @@ pub enum ZwlrLayerSurfaceV1Error { TooManyExclusiveEdges, #[error("Exclusive zone not be larger than 65535")] ExcessiveExclusive, + #[error("Popup already has a parent")] + PopupHasParent, + #[error("Surface still has popups")] + HasPopups, } efrom!(ZwlrLayerSurfaceV1Error, WlSurfaceError); efrom!(ZwlrLayerSurfaceV1Error, ClientError); diff --git a/src/renderer.rs b/src/renderer.rs index 80bf94cc..b6901070 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -189,18 +189,24 @@ impl Renderer<'_> { self.render_workspace(&ws, x, y + th + 1); } } - for stacked in self.state.root.stacked.iter() { - if stacked.node_visible() { - self.base.ops.push(GfxApiOpt::Sync); - let pos = stacked.node_absolute_position(); - if pos.intersects(&opos) { - let (x, y) = opos.translate(pos.x1(), pos.y1()); - stacked.node_render(self, x, y, None); + macro_rules! render_stacked { + ($stack:expr) => { + for stacked in $stack.iter() { + if stacked.node_visible() { + self.base.ops.push(GfxApiOpt::Sync); + let pos = stacked.node_absolute_position(); + if pos.intersects(&opos) { + let (x, y) = opos.translate(pos.x1(), pos.y1()); + stacked.node_render(self, x, y, None); + } + } } - } + }; } + render_stacked!(self.state.root.stacked); render_layer!(output.layers[2]); render_layer!(output.layers[3]); + render_stacked!(self.state.root.stacked_above_layers); if let Some(ws) = output.workspace.get() { if ws.render_highlight.get() > 0 { let color = self.state.theme.colors.highlight.get(); diff --git a/src/tree/display.rs b/src/tree/display.rs index 4cd2039f..87c3cf61 100644 --- a/src/tree/display.rs +++ b/src/tree/display.rs @@ -20,7 +20,8 @@ pub struct DisplayNode { pub id: NodeId, pub extents: Cell, pub outputs: CopyHashMap>, - pub stacked: LinkedList>, + pub stacked: Rc>>, + pub stacked_above_layers: Rc>>, pub seat_state: NodeSeatState, } @@ -31,6 +32,7 @@ impl DisplayNode { extents: Default::default(), outputs: Default::default(), stacked: Default::default(), + stacked_above_layers: Default::default(), seat_state: Default::default(), } } diff --git a/src/tree/output.rs b/src/tree/output.rs index cc6b76fc..93dec03e 100644 --- a/src/tree/output.rs +++ b/src/tree/output.rs @@ -32,7 +32,7 @@ use { time::Time, tree::{ walker::NodeVisitor, Direction, FindTreeResult, FindTreeUsecase, FoundNode, Node, - NodeId, WorkspaceNode, + NodeId, StackedNode, WorkspaceNode, }, utils::{ clonecell::CloneCell, copyhashmap::CopyHashMap, errorfmt::ErrorFmt, @@ -605,6 +605,46 @@ impl OutputNode { self.state.tree_changed(); } + fn find_stacked_at( + &self, + stack: &LinkedList>, + x: i32, + y: i32, + tree: &mut Vec, + usecase: FindTreeUsecase, + ) -> FindTreeResult { + if stack.is_empty() { + return FindTreeResult::Other; + } + let (x_abs, y_abs) = self.global.pos.get().translate_inv(x, y); + for stacked in stack.rev_iter() { + let ext = stacked.node_absolute_position(); + if !stacked.node_visible() { + continue; + } + if stacked.stacked_absolute_position_constrains_input() && !ext.contains(x_abs, y_abs) { + // TODO: make constrain always true + continue; + } + let (x, y) = ext.translate(x_abs, y_abs); + let idx = tree.len(); + tree.push(FoundNode { + node: stacked.deref().clone().stacked_into_node(), + x, + y, + }); + match stacked.node_find_tree_at(x, y, tree, usecase) { + FindTreeResult::AcceptsInput => { + return FindTreeResult::AcceptsInput; + } + FindTreeResult::Other => { + tree.truncate(idx); + } + } + } + FindTreeResult::Other + } + pub fn find_layer_surface_at( &self, x: i32, @@ -671,7 +711,7 @@ impl OutputNode { macro_rules! set_layer_visible { ($layer:expr, $visible:expr) => { for ls in $layer.iter() { - ls.surface.set_visible($visible); + ls.set_visible($visible); } }; } @@ -833,6 +873,13 @@ impl Node for OutputNode { } } } + { + let res = + self.find_stacked_at(&self.state.root.stacked_above_layers, x, y, tree, usecase); + if res.accepts_input() { + return res; + } + } { let res = self.find_layer_surface_at(x, y, &[OVERLAY, TOP], tree, usecase); if res.accepts_input() { @@ -840,33 +887,9 @@ impl Node for OutputNode { } } { - let (x_abs, y_abs) = self.global.pos.get().translate_inv(x, y); - for stacked in self.state.root.stacked.rev_iter() { - let ext = stacked.node_absolute_position(); - if !stacked.node_visible() { - continue; - } - if stacked.stacked_absolute_position_constrains_input() - && !ext.contains(x_abs, y_abs) - { - // TODO: make constrain always true - continue; - } - let (x, y) = ext.translate(x_abs, y_abs); - let idx = tree.len(); - tree.push(FoundNode { - node: stacked.deref().clone().stacked_into_node(), - x, - y, - }); - match stacked.node_find_tree_at(x, y, tree, usecase) { - FindTreeResult::AcceptsInput => { - return FindTreeResult::AcceptsInput; - } - FindTreeResult::Other => { - tree.truncate(idx); - } - } + let res = self.find_stacked_at(&self.state.root.stacked, x, y, tree, usecase); + if res.accepts_input() { + return res; } } let mut fullscreen = None;