From 3f973ce6d589d243c2f47c070681f46aac126034 Mon Sep 17 00:00:00 2001 From: Ivan Molodetskikh Date: Tue, 15 Aug 2023 14:27:25 +0400 Subject: [PATCH 1/3] smallvil: Store DisplayHandle in State Will be useful for the data device focus. --- smallvil/src/state.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/smallvil/src/state.rs b/smallvil/src/state.rs index b1fd312ef1a0..ee7e81fcd23a 100644 --- a/smallvil/src/state.rs +++ b/smallvil/src/state.rs @@ -8,7 +8,7 @@ use smithay::{ wayland_server::{ backend::{ClientData, ClientId, DisconnectReason}, protocol::wl_surface::WlSurface, - Display, + Display, DisplayHandle, }, }, utils::{Logical, Point}, @@ -27,6 +27,7 @@ use crate::CalloopData; pub struct Smallvil { pub start_time: std::time::Instant, pub socket_name: OsString, + pub display_handle: DisplayHandle, pub space: Space, pub loop_signal: LoopSignal, @@ -80,6 +81,7 @@ impl Smallvil { Self { start_time, + display_handle: dh, space, loop_signal, From 54c0969051d1ce5b78375f0c60ab796ddb79d88d Mon Sep 17 00:00:00 2001 From: Ivan Molodetskikh Date: Tue, 15 Aug 2023 14:28:16 +0400 Subject: [PATCH 2/3] smallvil: Set data device focus Makes clipboard work across clients. --- smallvil/src/handlers/mod.rs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/smallvil/src/handlers/mod.rs b/smallvil/src/handlers/mod.rs index bc2cd23fe1e6..07998db7016c 100644 --- a/smallvil/src/handlers/mod.rs +++ b/smallvil/src/handlers/mod.rs @@ -7,9 +7,12 @@ use crate::Smallvil; // Wl Seat // -use smithay::input::{SeatHandler, SeatState}; +use smithay::input::{Seat, SeatHandler, SeatState}; use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface; -use smithay::wayland::data_device::{ClientDndGrabHandler, DataDeviceHandler, ServerDndGrabHandler}; +use smithay::reexports::wayland_server::Resource; +use smithay::wayland::data_device::{ + set_data_device_focus, ClientDndGrabHandler, DataDeviceHandler, ServerDndGrabHandler, +}; use smithay::{delegate_data_device, delegate_output, delegate_seat}; impl SeatHandler for Smallvil { @@ -20,13 +23,13 @@ impl SeatHandler for Smallvil { &mut self.seat_state } - fn cursor_image( - &mut self, - _seat: &smithay::input::Seat, - _image: smithay::input::pointer::CursorImageStatus, - ) { + fn cursor_image(&mut self, _seat: &Seat, _image: smithay::input::pointer::CursorImageStatus) {} + + fn focus_changed(&mut self, seat: &Seat, focused: Option<&WlSurface>) { + let dh = &self.display_handle; + let client = focused.and_then(|s| dh.get_client(s.id()).ok()); + set_data_device_focus(dh, seat, client); } - fn focus_changed(&mut self, _seat: &smithay::input::Seat, _focused: Option<&WlSurface>) {} } delegate_seat!(Smallvil); From e8cca3828ec8ade1b8c57ce6fd0b5c64ff700b6e Mon Sep 17 00:00:00 2001 From: Ivan Molodetskikh Date: Tue, 15 Aug 2023 14:39:09 +0400 Subject: [PATCH 3/3] smallvil: Add basic PopupManager implementation Doesn't handle popup grabs but gets smallvil far enough. --- smallvil/src/handlers/compositor.rs | 2 +- smallvil/src/handlers/xdg_shell.rs | 77 ++++++++++++++++++++--------- smallvil/src/state.rs | 5 +- smallvil/src/winit.rs | 1 + 4 files changed, 61 insertions(+), 24 deletions(-) diff --git a/smallvil/src/handlers/compositor.rs b/smallvil/src/handlers/compositor.rs index fcb0dc0280e5..e0abdfcbbbf1 100644 --- a/smallvil/src/handlers/compositor.rs +++ b/smallvil/src/handlers/compositor.rs @@ -38,7 +38,7 @@ impl CompositorHandler for Smallvil { } }; - xdg_shell::handle_commit(&self.space, surface); + xdg_shell::handle_commit(&mut self.popups, &self.space, surface); resize_grab::handle_commit(&mut self.space, surface); } } diff --git a/smallvil/src/handlers/xdg_shell.rs b/smallvil/src/handlers/xdg_shell.rs index 7351d3bf0de0..1eade507e1a4 100644 --- a/smallvil/src/handlers/xdg_shell.rs +++ b/smallvil/src/handlers/xdg_shell.rs @@ -1,6 +1,6 @@ use smithay::{ delegate_xdg_shell, - desktop::{Space, Window}, + desktop::{PopupKind, PopupManager, Space, Window}, input::{ pointer::{Focus, GrabStartData as PointerGrabStartData}, Seat, @@ -16,8 +16,8 @@ use smithay::{ wayland::{ compositor::with_states, shell::xdg::{ - PopupSurface, PositionerState, ToplevelSurface, XdgShellHandler, XdgShellState, - XdgToplevelSurfaceData, + PopupSurface, PositionerState, ToplevelSurface, XdgPopupSurfaceData, XdgShellHandler, + XdgShellState, XdgToplevelSurfaceData, }, }, }; @@ -37,8 +37,21 @@ impl XdgShellHandler for Smallvil { self.space.map_element(window, (0, 0), false); } - fn new_popup(&mut self, _surface: PopupSurface, _positioner: PositionerState) { - // TODO: Popup handling using PopupManager + fn new_popup(&mut self, surface: PopupSurface, _positioner: PositionerState) { + let _ = self.popups.track_popup(PopupKind::Xdg(surface)); + } + + fn reposition_request(&mut self, surface: PopupSurface, positioner: PositionerState, token: u32) { + surface.with_pending_state(|state| { + // NOTE: This is again a simplification, a proper compositor would + // calculate the geometry of the popup here. For simplicity we just + // use the default implementation here that does not take the + // window position and output constraints into account. + let geometry = positioner.get_geometry(); + state.geometry = geometry; + state.positioner = positioner; + }); + surface.send_repositioned(token); } fn move_request(&mut self, surface: ToplevelSurface, seat: wl_seat::WlSeat, serial: Serial) { @@ -139,25 +152,45 @@ fn check_grab( } /// Should be called on `WlSurface::commit` -pub fn handle_commit(space: &Space, surface: &WlSurface) -> Option<()> { - let window = space +pub fn handle_commit(popups: &mut PopupManager, space: &Space, surface: &WlSurface) { + // Handle toplevel commits. + if let Some(window) = space .elements() .find(|w| w.toplevel().wl_surface() == surface) - .cloned()?; - - let initial_configure_sent = with_states(surface, |states| { - states - .data_map - .get::() - .unwrap() - .lock() - .unwrap() - .initial_configure_sent - }); - - if !initial_configure_sent { - window.toplevel().send_configure(); + .cloned() + { + let initial_configure_sent = with_states(surface, |states| { + states + .data_map + .get::() + .unwrap() + .lock() + .unwrap() + .initial_configure_sent + }); + + if !initial_configure_sent { + window.toplevel().send_configure(); + } } - Some(()) + // Handle popup commits. + popups.commit(surface); + if let Some(popup) = popups.find_popup(surface) { + let PopupKind::Xdg(ref popup) = popup; + let initial_configure_sent = with_states(surface, |states| { + states + .data_map + .get::() + .unwrap() + .lock() + .unwrap() + .initial_configure_sent + }); + if !initial_configure_sent { + // NOTE: This should never fail as the initial configure is always + // allowed. + popup.send_configure().expect("initial configure failed"); + } + } } diff --git a/smallvil/src/state.rs b/smallvil/src/state.rs index ee7e81fcd23a..582f59fda68b 100644 --- a/smallvil/src/state.rs +++ b/smallvil/src/state.rs @@ -1,7 +1,7 @@ use std::{ffi::OsString, os::unix::io::AsRawFd, sync::Arc}; use smithay::{ - desktop::{Space, Window, WindowSurfaceType}, + desktop::{PopupManager, Space, Window, WindowSurfaceType}, input::{Seat, SeatState}, reexports::{ calloop::{generic::Generic, EventLoop, Interest, LoopSignal, Mode, PostAction}, @@ -39,6 +39,7 @@ pub struct Smallvil { pub output_manager_state: OutputManagerState, pub seat_state: SeatState, pub data_device_state: DataDeviceState, + pub popups: PopupManager, pub seat: Seat, } @@ -55,6 +56,7 @@ impl Smallvil { let output_manager_state = OutputManagerState::new_with_xdg_output::(&dh); let mut seat_state = SeatState::new(); let data_device_state = DataDeviceState::new::(&dh); + let popups = PopupManager::default(); // A seat is a group of keyboards, pointer and touch devices. // A seat typically has a pointer and maintains a keyboard focus and a pointer focus. @@ -93,6 +95,7 @@ impl Smallvil { output_manager_state, seat_state, data_device_state, + popups, seat, } } diff --git a/smallvil/src/winit.rs b/smallvil/src/winit.rs index 0a453084e873..5d1164945baf 100644 --- a/smallvil/src/winit.rs +++ b/smallvil/src/winit.rs @@ -120,6 +120,7 @@ pub fn winit_dispatch( }); state.space.refresh(); + state.popups.cleanup(); display.flush_clients()?; Ok(())