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/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/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/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/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/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/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/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/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_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, 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.rs b/src/ifs/wl_surface/xdg_surface.rs index 6b1d7b01..b88c0d9a 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,20 @@ 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::{LinkedList, LinkedNode}, + numcell::NumCell, + option_ext::OptionExt, + rc_eq::rc_eq, }, wire::{xdg_surface::*, WlSurfaceId, XdgPopupId, XdgSurfaceId}, }, std::{ - cell::{Cell, RefMut}, + cell::{Cell, RefCell, RefMut}, fmt::Debug, rc::Rc, }, @@ -65,11 +71,72 @@ pub struct XdgSurface { extents: Cell, pub absolute_desired_extents: Cell, ext: CloneCell>>, - popups: CopyHashMap>, + popup_display_stack: CloneCell>>>, + 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( + self.parent + .popup_display_stack + .get() + .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, @@ -115,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(), @@ -139,7 +207,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 +215,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 +239,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 +249,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(); } } @@ -216,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 { @@ -279,11 +360,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 +429,29 @@ 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) { + if self.popups.is_empty() { + return; + } + let stack = self.popup_display_stack.get(); for popup in self.popups.lock().values() { - popup.restack(); + if let Some(dl) = &*popup.display_link.borrow() { + stack.add_last_existing(dl); + } + popup.popup.xdg.restack_popups(); } + 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 5b4f1ab8..05d58c8a 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(in super::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,17 +108,13 @@ 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: &dyn XdgPopupParent) { 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 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(); @@ -146,17 +145,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 { @@ -174,32 +177,35 @@ 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) { 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())); } @@ -211,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(()) } @@ -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()); @@ -241,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); @@ -253,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! { @@ -284,13 +270,10 @@ 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; } } -simple_add_obj!(XdgPopup); +dedicated_add_obj!(XdgPopup, XdgPopupId, xdg_popups); impl Node for XdgPopup { fn node_id(&self) -> NodeId { @@ -374,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 { @@ -385,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()); } @@ -393,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(); } } 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/wl_surface/zwlr_layer_surface_v1.rs b/src/ifs/wl_surface/zwlr_layer_surface_v1.rs index 52cf702f..08f4547e 100644 --- a/src/ifs/wl_surface/zwlr_layer_surface_v1.rs +++ b/src/ifs/wl_surface/zwlr_layer_surface_v1.rs @@ -4,23 +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}, + tree::{ + FindTreeResult, FindTreeUsecase, FoundNode, Node, NodeId, NodeVisitor, OutputNode, + StackedNode, + }, utils::{ - bitflags::BitflagsExt, cell_ext::CellExt, linkedlist::LinkedNode, numcell::NumCell, + bitflags::BitflagsExt, + copyhashmap::CopyHashMap, + hash_map_ext::HashMapExt, + linkedlist::{LinkedList, LinkedNode}, + numcell::NumCell, option_ext::OptionExt, }, - wire::{zwlr_layer_surface_v1::*, WlSurfaceId, ZwlrLayerSurfaceV1Id}, + wire::{zwlr_layer_surface_v1::*, WlSurfaceId, XdgPopupId, ZwlrLayerSurfaceV1Id}, }, std::{ - cell::{Cell, RefMut}, - mem, + cell::{Cell, RefCell, RefMut}, ops::Deref, rc::Rc, }, @@ -47,30 +56,74 @@ pub struct ZwlrLayerSurfaceV1 { pub output: Rc, pub namespace: String, pub tracker: Tracker, - output_pos: Cell, + output_extents: Cell, pos: Cell, mapped: Cell, layer: Cell, requested_serial: NumCell, - acked_serial: Cell>, 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, + popups: CopyHashMap>, +} + +#[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), +} + +struct Popup { + parent: Rc, + popup: Rc, + stack: Rc>>, + stack_link: RefCell>>>, } #[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, - any: bool, + exclusive_edge: Option, } impl PendingLayerSurfaceData { @@ -88,7 +141,6 @@ impl PendingLayerSurfaceData { opt!(margin); opt!(keyboard_interactivity); opt!(layer); - self.any |= mem::take(&mut next.any); } } @@ -110,19 +162,22 @@ 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), requested_serial: Default::default(), - acked_serial: Cell::new(None), 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(), + popups: Default::default(), } } @@ -167,7 +222,6 @@ impl ZwlrLayerSurfaceV1RequestHandler for ZwlrLayerSurfaceV1 { } let mut pending = self.pending(); pending.size = Some((req.width as _, req.height as _)); - pending.any = true; Ok(()) } @@ -177,7 +231,6 @@ impl ZwlrLayerSurfaceV1RequestHandler for ZwlrLayerSurfaceV1 { } let mut pending = self.pending(); pending.anchor = Some(req.anchor); - pending.any = true; Ok(()) } @@ -187,15 +240,27 @@ impl ZwlrLayerSurfaceV1RequestHandler for ZwlrLayerSurfaceV1 { _slf: &Rc, ) -> Result<(), Self::Error> { let mut pending = self.pending(); - pending.exclusive_zone = Some(req.zone); - pending.any = true; + 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(()) } 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)); - pending.any = true; Ok(()) } @@ -211,20 +276,35 @@ impl ZwlrLayerSurfaceV1RequestHandler for ZwlrLayerSurfaceV1 { } let mut pending = self.pending(); pending.keyboard_interactivity = Some(req.keyboard_interactivity); - pending.any = true; 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(()) } - 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(()) } 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(); @@ -237,18 +317,77 @@ impl ZwlrLayerSurfaceV1RequestHandler for ZwlrLayerSurfaceV1 { } let mut pending = self.pending(); pending.layer = Some(req.layer); - pending.any = true; + 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 { - fn pre_commit(&self, pending: &mut PendingState) -> Result<(), ZwlrLayerSurfaceV1Error> { - let Some(global) = self.output.get() else { - return Ok(()); + 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(); - let mut send_configure = mem::replace(&mut pending.any, false); if let Some(size) = pending.size.take() { self.size.set(size); } @@ -267,84 +406,152 @@ 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)); + 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) { + 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.send_configure(serial, width as _, height as _); + if let Some(ee) = self.exclusive_edge.get() { + if !self.anchor.get().contains(ee) { + return Err(ZwlrLayerSurfaceV1Error::ExclusiveEdgeNotAnchored); + } } + self.configure(); Ok(()) } - pub fn output_position(&self) -> Rect { - self.output_pos.get() + fn configure(&self) { + 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) = 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; + } + 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; + } + 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 _); + } } - pub fn position(&self) -> Rect { - self.pos.get() + pub fn output_extents(&self) -> Rect { + self.output_extents.get() } - pub fn compute_position(&self) { - let Some(global) = self.output.get() else { + fn compute_position(&self) { + let Some(output) = self.output.node() 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; } - let opos = global.pos.get(); + let (mt, mr, mb, ml) = self.margin.get(); + 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) { - 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 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); + 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(); + } } - 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.pos.set(a_rect); - self.surface.set_absolute_position(a_rect.x1(), a_rect.y1()); self.client.state.tree_changed(); } + pub fn output_resized(&self) { + self.configure(); + 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); self.surface.destroy_node(); 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(); + } + } + 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(); + } + } } } @@ -367,17 +574,17 @@ 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(); } + 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(); @@ -444,7 +651,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>) { @@ -452,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; @@ -482,10 +733,22 @@ 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}")] 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, + #[error("Popup already has a parent")] + PopupHasParent, + #[error("Surface still has popups")] + HasPopups, } 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/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 c3e54e12..d7c213df 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::{GbmBo, 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/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 6821c49b..b6901070 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); } }; @@ -124,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, @@ -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(); @@ -539,8 +545,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/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..09184858 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}, @@ -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 { @@ -244,14 +249,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 +315,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/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 8c009de7..93dec03e 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, @@ -32,11 +32,12 @@ use { time::Time, tree::{ walker::NodeVisitor, Direction, FindTreeResult, FindTreeUsecase, FoundNode, Node, - NodeId, WorkspaceNode, + NodeId, StackedNode, WorkspaceNode, }, 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}, }, @@ -46,7 +47,7 @@ use { std::{ cell::{Cell, RefCell}, fmt::{Debug, Formatter}, - ops::{Deref, Sub}, + ops::Deref, rc::Rc, }, }; @@ -60,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, @@ -93,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(); @@ -140,7 +165,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, _ => { @@ -221,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()); } } @@ -280,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(); @@ -429,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); } @@ -477,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) { @@ -545,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); } @@ -553,11 +591,11 @@ 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() { - surface.compute_position(); + surface.output_resized(); } } self.global.send_mode(); @@ -567,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, @@ -581,7 +659,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) @@ -633,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); } }; } @@ -641,7 +719,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() { @@ -656,6 +734,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; } @@ -794,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() { @@ -801,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; @@ -842,21 +904,33 @@ 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(); - if let Some(ws) = self.workspace.get() { - 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); + let mut search_layers = true; + 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); + } + } + } } } + if search_layers { + self.find_layer_surface_at(x, y, &[BOTTOM, BACKGROUND], tree, usecase); + } FindTreeResult::AcceptsInput } } 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/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>) { 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); } } 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 {