diff --git a/Cargo.lock b/Cargo.lock index 5ea3fc4..cf948d6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2137,7 +2137,6 @@ dependencies = [ "directories", "fxtypemap", "glam 0.23.0", - "global_counter", "input-event-codes", "lazy_static", "libc", diff --git a/Cargo.toml b/Cargo.toml index 5a9816c..9433e42 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -60,7 +60,6 @@ serde = { version = "1.0.160", features = ["derive"] } serde_repr = "0.1.16" tracing = "0.1.37" tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } -global_counter = "0.2.2" rand = "0.8.5" atty = "0.2.14" xkbcommon = { version = "0.7.0", default-features = false, optional = true } diff --git a/src/wayland/mod.rs b/src/wayland/mod.rs index 361d511..d653405 100644 --- a/src/wayland/mod.rs +++ b/src/wayland/mod.rs @@ -22,7 +22,6 @@ use self::xwayland_rootless::XWaylandState; use self::{state::WaylandState, surface::CORE_SURFACES}; use crate::{core::task, wayland::state::ClientState}; use color_eyre::eyre::{ensure, Result}; -use global_counter::primitive::exact::CounterU32; use once_cell::sync::OnceCell; use parking_lot::Mutex; use sk::StereoKitDraw; @@ -52,7 +51,6 @@ use tracing::{debug_span, info, instrument}; pub static X_DISPLAY: OnceCell = OnceCell::new(); pub static WAYLAND_DISPLAY: OnceCell = OnceCell::new(); -pub static SERIAL_COUNTER: CounterU32 = CounterU32::new(0); struct EGLRawHandles { display: *const c_void, @@ -175,6 +173,7 @@ impl Wayland { acc = listen_async.accept() => { // New client connected let (stream, _) = acc?; let client_state = Arc::new(ClientState { + pid: stream.peer_cred().ok().and_then(|c| c.pid()), id: OnceCell::new(), compositor_state: Default::default(), display: Arc::downgrade(&display), diff --git a/src/wayland/seat.rs b/src/wayland/seat.rs index 7b02b05..293f08c 100644 --- a/src/wayland/seat.rs +++ b/src/wayland/seat.rs @@ -106,15 +106,49 @@ impl SeatWrapper { touches: Mutex::new(FxHashMap::default()), } } + pub fn unfocus(&self, surface: &WlSurface, state: &mut WaylandState) { + let pointer = self.seat.get_pointer().unwrap(); + if pointer.current_focus() == Some(surface.clone()) { + pointer.motion( + state, + None, + &MotionEvent { + location: (0.0, 0.0).into(), + serial: SERIAL_COUNTER.next_serial(), + time: 0, + }, + ) + } + let keyboard = self.seat.get_keyboard().unwrap(); + if keyboard.current_focus() == Some(surface.clone()) { + keyboard.set_focus(state, None, SERIAL_COUNTER.next_serial()); + } + let touch = self.seat.get_touch().unwrap(); + for (id, touch_surface) in self.touches.lock().iter() { + if touch_surface.id() == surface.id() { + self.touch_up(*id); + touch.up( + state, + &UpEvent { + slot: Some(*id).into(), + serial: SERIAL_COUNTER.next_serial(), + time: 0, + }, + ) + } + } + } + pub fn pointer_motion(&self, surface: WlSurface, position: Vector2) { let Some(state) = self.wayland_state.upgrade() else { return; }; + let mut state = state.lock(); let Some(pointer) = self.seat.get_pointer() else { return; }; pointer.motion( - &mut state.lock(), + &mut state, Some((surface, (0, 0).into())), &MotionEvent { location: (position.x as f64, position.y as f64).into(), @@ -122,16 +156,18 @@ impl SeatWrapper { time: 0, }, ); + pointer.frame(&mut state); } pub fn pointer_button(&self, button: u32, pressed: bool) { let Some(state) = self.wayland_state.upgrade() else { return; }; + let mut state = state.lock(); let Some(pointer) = self.seat.get_pointer() else { return; }; pointer.button( - &mut state.lock(), + &mut state, &ButtonEvent { button, state: if pressed { @@ -143,6 +179,7 @@ impl SeatWrapper { time: 0, }, ); + pointer.frame(&mut state); } pub fn pointer_scroll( &self, @@ -171,7 +208,8 @@ impl SeatWrapper { v120: scroll_steps.map(|d| ((d.x * 120.0) as i32, (d.y * 120.0) as i32)), stop: (false, false), }, - ) + ); + pointer.frame(&mut state); } pub fn keyboard_keys(&self, surface: WlSurface, keymap_id: &str, keys: Vec) { diff --git a/src/wayland/seat_old.rs b/src/wayland/seat_old.rs deleted file mode 100644 index 6f255c9..0000000 --- a/src/wayland/seat_old.rs +++ /dev/null @@ -1,606 +0,0 @@ -use super::{ - state::{ClientState, WaylandState}, - surface::CoreSurface, - SERIAL_COUNTER, -}; -use crate::{ - core::task, - nodes::items::panel::{Backend, Geometry, PanelItem}, -}; -use color_eyre::eyre::{bail, eyre, Result}; -use mint::Vector2; -use nanoid::nanoid; -use once_cell::sync::OnceCell; -use parking_lot::Mutex; -use rand::{seq::IteratorRandom, thread_rng}; -use rustc_hash::{FxHashMap, FxHashSet}; -use smithay::{ - input::keyboard::{KeymapFile, ModifiersState}, - reexports::wayland_server::{ - backend::{ClientId, GlobalId, ObjectId}, - protocol::{ - wl_keyboard::{self, KeyState, WlKeyboard}, - wl_pointer::{self, Axis, ButtonState, WlPointer}, - wl_seat::{self, Capability, WlSeat, EVT_NAME_SINCE}, - wl_surface::WlSurface, - wl_touch::{self, WlTouch}, - }, - Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource, Weak as WlWeak, - }, - wayland::compositor, -}; -use std::{ - collections::VecDeque, - sync::Arc, - time::{Duration, Instant}, -}; -use tokio::sync::watch; -use tracing::{debug, warn}; -use xkbcommon::xkb::{self, ffi::XKB_KEYMAP_FORMAT_TEXT_V1, Keycode, Keymap}; - -pub fn handle_cursor( - panel_item: &Arc>, - mut cursor: watch::Receiver>, -) { - let panel_item_weak = Arc::downgrade(panel_item); - let _ = task::new(|| "cursor handler", async move { - while cursor.changed().await.is_ok() { - let Some(panel_item) = panel_item_weak.upgrade() else {continue}; - let cursor_info = cursor.borrow(); - panel_item.set_cursor(cursor_info.as_ref().and_then(CursorInfo::cursor_data)); - } - }); -} - -pub struct KeyboardInfo { - keymap_string: String, - keymap: KeymapFile, - state: xkb::State, - mods: ModifiersState, - keys: FxHashSet, -} -impl KeyboardInfo { - pub fn new(keymap_string: String, keymap: &Keymap) -> Self { - KeyboardInfo { - keymap_string, - state: xkb::State::new(keymap), - keymap: KeymapFile::new(keymap), - mods: ModifiersState::default(), - keys: FxHashSet::default(), - } - } - pub fn process(&mut self, key: u32, pressed: bool, keyboard: &WlKeyboard) -> Result { - let xkb_key_state = if pressed { - xkb::KeyDirection::Down - } else { - xkb::KeyDirection::Up - }; - let state_components = self.state.update_key(Keycode::new(key + 8), xkb_key_state); - if state_components != 0 { - self.mods.update_with(&self.state); - keyboard.modifiers( - 0, - self.mods.serialized.depressed, - self.mods.serialized.latched, - self.mods.serialized.locked, - 0, - ); - } - // if pressed { - // println!("Key {key} is being pressed with {state_components} modifiers"); - // } else { - // println!("Key {key} is being released with {state_components} modifiers"); - // } - - let wl_key_state = if pressed { - KeyState::Pressed - } else { - KeyState::Released - }; - keyboard.key(SERIAL_COUNTER.inc(), 0, key, wl_key_state); - match wl_key_state { - KeyState::Pressed => { - self.keys.insert(key); - } - KeyState::Released => { - self.keys.remove(&key); - } - _ => unimplemented!(), - } - Ok(self.keys.len()) - } -} -unsafe impl Send for KeyboardInfo {} - -#[derive(Debug, Clone, Copy)] -pub enum PointerEvent { - Motion(Vector2), - Button { - button: u32, - state: u32, - }, - Scroll { - axis_continuous: Option>, - axis_discrete: Option>, - }, -} -#[derive(Debug, Clone)] -pub enum KeyboardEvent { - Keymap, - Key { key: u32, state: bool }, -} - -const POINTER_EVENT_TIMEOUT: Duration = Duration::from_millis(50); -struct SurfaceInfo { - wl_surface: WlWeak, - cursor_sender: watch::Sender>, - pointer_queue: VecDeque, - pointer_latest_event: Instant, - keyboard_queue: VecDeque, - keyboard_info: Option, -} -impl SurfaceInfo { - fn new(wl_surface: &WlSurface, cursor_sender: watch::Sender>) -> Self { - SurfaceInfo { - wl_surface: wl_surface.downgrade(), - cursor_sender, - pointer_queue: VecDeque::new(), - pointer_latest_event: Instant::now(), - keyboard_queue: VecDeque::new(), - keyboard_info: None, - } - } - fn flush(&self) { - if let Some(client) = self.wl_surface.upgrade().ok().and_then(|s| s.client()) { - if let Some(client_data) = client.get_data::() { - client_data.flush(); - } - } - } - fn handle_pointer_events(&mut self, pointer: &WlPointer, mut locked: bool) -> bool { - let Ok(focus) = self.wl_surface.upgrade() else { return false; }; - let Some(core_surface) = CoreSurface::from_wl_surface(&focus) else { return false; }; - let Some(focus_size) = core_surface.size() else { return false; }; - - if !self.pointer_queue.is_empty() { - self.pointer_latest_event = Instant::now(); - } - while let Some(event) = self.pointer_queue.pop_front() { - match (locked, event) { - (false, PointerEvent::Motion(pos)) => { - pointer.enter( - SERIAL_COUNTER.inc(), - &focus, - (pos.x as f64).clamp(0.0, focus_size.x as f64), - (pos.y as f64).clamp(0.0, focus_size.y as f64), - ); - locked = true; - } - (true, PointerEvent::Motion(pos)) => { - pointer.motion( - 0, - (pos.x as f64).clamp(0.0, focus_size.x as f64), - (pos.y as f64).clamp(0.0, focus_size.y as f64), - ); - if pointer.version() >= wl_pointer::EVT_FRAME_SINCE { - pointer.frame(); - } - } - (true, PointerEvent::Button { button, state }) => { - pointer.button( - 0, - 0, - button, - match state { - 0 => ButtonState::Released, - 1 => ButtonState::Pressed, - _ => continue, - }, - ); - if pointer.version() >= wl_pointer::EVT_FRAME_SINCE { - pointer.frame(); - } - } - ( - true, - PointerEvent::Scroll { - axis_continuous, - axis_discrete, - }, - ) => { - if let Some(axis_continuous) = axis_continuous { - pointer.axis(0, Axis::HorizontalScroll, axis_continuous.x as f64); - pointer.axis(0, Axis::VerticalScroll, -axis_continuous.y as f64); - } - if pointer.version() >= wl_pointer::EVT_AXIS_DISCRETE_SINCE { - if let Some(axis_discrete) = axis_discrete { - pointer.axis_discrete(Axis::HorizontalScroll, axis_discrete.x as i32); - pointer.axis_discrete(Axis::VerticalScroll, -axis_discrete.y as i32); - } - } - if pointer.version() >= wl_pointer::EVT_AXIS_STOP_SINCE - && axis_discrete.is_none() - && axis_continuous.is_none() - { - pointer.axis_stop(0, Axis::HorizontalScroll); - pointer.axis_stop(0, Axis::VerticalScroll); - } - if pointer.version() >= wl_pointer::EVT_FRAME_SINCE { - pointer.frame(); - } - } - (locked, event) => { - warn!(locked, ?event, "Invalid pointer event!"); - } - } - } - if self.pointer_latest_event.elapsed() > POINTER_EVENT_TIMEOUT { - pointer.leave(SERIAL_COUNTER.inc(), &focus); - locked = false; - } - self.flush(); - - locked - } - fn handle_keyboard_events(&mut self, keyboard: &WlKeyboard, mut locked: bool) -> bool { - let Ok(focus) = self.wl_surface.upgrade() else { return false; }; - let Some(info) = self.keyboard_info.as_mut() else { return true; }; - - if !locked { - keyboard.enter(0, &focus, vec![]); - if keyboard.version() >= wl_keyboard::EVT_REPEAT_INFO_SINCE { - keyboard.repeat_info(0, 0); - } - locked = info.keymap.send(keyboard).is_ok(); - } - while let Some(event) = self.keyboard_queue.pop_front() { - debug!(locked, ?event, "Process keyboard event"); - match (locked, event) { - (true, KeyboardEvent::Keymap) => { - let _ = info.keymap.send(keyboard); - } - (true, KeyboardEvent::Key { key, state }) => { - if let Ok(key_count) = info.process(key, state, keyboard) { - if key_count == 0 { - keyboard.leave(SERIAL_COUNTER.inc(), &focus); - return false; - } - } - } - (locked, event) => { - warn!(locked, ?event, "Invalid keyboard event!"); - } - } - } - self.flush(); - locked - } -} - -pub struct SeatData { - pub client: OnceCell, - global_id: OnceCell, - surfaces: Mutex>, - pointer: OnceCell<(WlPointer, Mutex)>, - keyboard: OnceCell<(WlKeyboard, Mutex)>, - touch: OnceCell, - touches: Mutex>, -} -impl SeatData { - pub fn new(dh: &DisplayHandle) -> Arc { - let seat_data = Arc::new(SeatData { - client: OnceCell::new(), - global_id: OnceCell::new(), - surfaces: Mutex::new(FxHashMap::default()), - pointer: OnceCell::new(), - keyboard: OnceCell::new(), - touch: OnceCell::new(), - touches: Mutex::new(FxHashMap::default()), - }); - - let _ = seat_data - .global_id - .set(dh.create_global::(7, seat_data.clone())); - - seat_data - } - - pub fn set_keymap(&self, keymap_str: String, surfaces: Vec) -> Result<()> { - let context = xkb::Context::new(0); - let keymap = - Keymap::new_from_string(&context, keymap_str.clone(), XKB_KEYMAP_FORMAT_TEXT_V1, 0) - .ok_or_else(|| eyre!("Keymap is not valid"))?; - let mut panels = self.surfaces.lock(); - let Some((_, focus)) = self.keyboard.get() else {bail!("Could not get keyboard")}; - for surface in surfaces { - let Some(surface_info) = panels.get_mut(&surface.id()) else {continue}; - if let Some(keyboard_info) = &mut surface_info.keyboard_info { - if &keyboard_info.keymap_string == &keymap_str { - continue; - } - } - surface_info - .keyboard_info - .replace(KeyboardInfo::new(keymap_str.clone(), &keymap)); - - if *focus.lock() == surface.id() { - surface_info.keyboard_queue.push_back(KeyboardEvent::Keymap); - } - } - Ok(()) - } - - pub fn pointer_event(&self, surface: &WlSurface, event: PointerEvent) { - let mut surfaces = self.surfaces.lock(); - let Some(surface_info) = surfaces.get_mut(&surface.id()) else {return}; - surface_info.pointer_queue.push_back(event); - drop(surfaces); - self.handle_pointer_events(); - } - pub fn keyboard_event(&self, surface: &WlSurface, event: KeyboardEvent) { - let mut surfaces = self.surfaces.lock(); - let Some(surface_info) = surfaces.get_mut(&surface.id()) else {return}; - surface_info.keyboard_queue.push_back(event); - drop(surfaces); - self.handle_keyboard_events(); - } - - fn handle_pointer_events(&self) { - let mut surfaces = self.surfaces.lock(); - let Some((pointer, pointer_focus)) = self.pointer.get() else {return}; - let mut pointer_focus = pointer_focus.lock(); - - loop { - let locked = !pointer_focus.is_null(); - // Pick a pointer to focus on if there is none - if pointer_focus.is_null() { - *pointer_focus = surfaces - .iter() - .filter(|(_k, v)| !v.pointer_queue.is_empty()) - .map(|(k, _v)| k) - .choose(&mut thread_rng()) - .cloned() - .unwrap_or(ObjectId::null()); - } - if pointer_focus.is_null() { - // If there's still none, guess we're done with pointer events for the time being - break; - } - let Some(surface_info) = surfaces.get_mut(&pointer_focus) else {break}; - if surface_info.handle_pointer_events(pointer, locked) { - // We haven't gotten to a point where we can switch the focus - break; - } else { - *pointer_focus = ObjectId::null(); - } - } - } - fn handle_keyboard_events(&self) { - let mut surfaces = self.surfaces.lock(); - let Some((keyboard, keyboard_focus)) = self.keyboard.get() else {return}; - let mut keyboard_focus = keyboard_focus.lock(); - loop { - let locked = !keyboard_focus.is_null(); - // Pick a keyboard to focus on if there is none - if keyboard_focus.is_null() { - *keyboard_focus = surfaces - .iter() - .filter(|(_k, v)| v.keyboard_info.is_some()) - .filter(|(_k, v)| !v.keyboard_queue.is_empty()) - .map(|(k, _v)| k) - .choose(&mut thread_rng()) - .cloned() - .unwrap_or(ObjectId::null()); - } - // If there's still none, guess we're done with keyboard events for the time being - let Some(surface_info) = surfaces.get_mut(&keyboard_focus) else {break}; - if surface_info.handle_keyboard_events(keyboard, locked) { - // We haven't gotten to a point where we can switch the focus - break; - } else { - *keyboard_focus = ObjectId::null(); - } - } - } - - pub fn new_surface(&self, surface: &WlSurface) -> watch::Receiver> { - let (tx, rx) = watch::channel(None); - self.surfaces - .lock() - .insert(surface.id(), SurfaceInfo::new(surface, tx)); - - rx - } - pub fn drop_surface(&self, surface: &WlSurface) { - self.surfaces.lock().remove(&surface.id()); - if let Some((_, pointer_focus)) = self.pointer.get() { - let mut pointer_focus = pointer_focus.lock(); - if *pointer_focus == surface.id() { - *pointer_focus = ObjectId::null(); - } - } - if let Some((_, keyboard_focus)) = self.keyboard.get() { - let mut keyboard_focus = keyboard_focus.lock(); - if *keyboard_focus == surface.id() { - *keyboard_focus = ObjectId::null(); - } - } - self.touches.lock().remove(&surface.id()); - } - - pub fn touch_down(&self, surface: &WlSurface, id: u32, position: Vector2) { - let Some(touch) = self.touch.get() else {return}; - touch.down( - SERIAL_COUNTER.inc(), - 0, - surface, - id as i32, - position.x as f64, - position.y as f64, - ); - self.touches.lock().insert(surface.id(), id); - } - pub fn touch_move(&self, id: u32, position: Vector2) { - let Some(touch) = self.touch.get() else {return}; - touch.motion(0, id as i32, position.x as f64, position.y as f64); - } - pub fn touch_up(&self, id: u32) { - let Some(touch) = self.touch.get() else {return}; - touch.up(SERIAL_COUNTER.inc(), 0, id as i32); - let mut touches = self.touches.lock(); - touches.retain(|_, tid| *tid != id); - } - pub fn reset_touches(&self) { - let Some(touch) = self.touch.get() else {return}; - for (_, touch_id) in self.touches.lock().drain() { - touch.up(SERIAL_COUNTER.inc(), 0, touch_id as i32); - } - } -} - -pub struct CursorInfo { - pub surface: WlWeak, - pub hotspot_x: i32, - pub hotspot_y: i32, -} -impl CursorInfo { - pub fn cursor_data(&self) -> Option { - let cursor_size = CoreSurface::from_wl_surface(&self.surface.upgrade().ok()?)?.size()?; - Some(Geometry { - origin: [self.hotspot_x, self.hotspot_y].into(), - size: cursor_size, - }) - } -} - -impl GlobalDispatch, WaylandState> for WaylandState { - fn bind( - _state: &mut WaylandState, - _handle: &DisplayHandle, - _client: &Client, - resource: New, - data: &Arc, - data_init: &mut DataInit<'_, WaylandState>, - ) { - let resource = data_init.init(resource, data.clone()); - - if resource.version() >= EVT_NAME_SINCE { - resource.name(nanoid!()); - } - - resource.capabilities(Capability::Pointer | Capability::Keyboard | Capability::Touch); - } - - fn can_view(client: Client, data: &Arc) -> bool { - let Some(seat_client) = data.client.get().cloned() else {return false}; - client.id() == seat_client - } -} - -impl Dispatch, WaylandState> for WaylandState { - fn request( - _state: &mut WaylandState, - _client: &Client, - _resource: &WlSeat, - request: wl_seat::Request, - data: &Arc, - _dh: &DisplayHandle, - data_init: &mut DataInit<'_, WaylandState>, - ) { - match request { - wl_seat::Request::GetPointer { id } => { - let pointer = data_init.init(id, data.clone()); - let _ = data.pointer.set((pointer, Mutex::new(ObjectId::null()))); - } - wl_seat::Request::GetKeyboard { id } => { - let keyboard = data_init.init(id, data.clone()); - if keyboard.version() >= wl_keyboard::EVT_REPEAT_INFO_SINCE { - keyboard.repeat_info(0, 0); - } - let _ = data.keyboard.set((keyboard, Mutex::new(ObjectId::null()))); - } - wl_seat::Request::GetTouch { id } => { - let _ = data.touch.set(data_init.init(id, data.clone())); - } - wl_seat::Request::Release => (), - _ => unreachable!(), - } - } -} - -impl Dispatch, WaylandState> for WaylandState { - fn request( - _state: &mut WaylandState, - _client: &Client, - _resource: &WlPointer, - request: wl_pointer::Request, - seat_data: &Arc, - dh: &DisplayHandle, - _data_init: &mut DataInit<'_, WaylandState>, - ) { - match request { - wl_pointer::Request::SetCursor { - serial: _, - surface, - hotspot_x, - hotspot_y, - } => { - if let Some(surface) = surface.as_ref() { - CoreSurface::add_to(dh.clone(), surface, || (), |_| ()); - compositor::with_states(surface, |data| { - if let Some(core_surface) = data.data_map.get::>() { - core_surface.set_material_offset(1); - } - }) - } - - let Some((_, focus)) = seat_data.pointer.get() else {return}; - let focus = focus.lock(); - let surfaces = seat_data.surfaces.lock(); - let Some(surface_info) = surfaces.get(&focus) else {return}; - let cursor_info = surface.map(|surface| CursorInfo { - surface: surface.downgrade(), - hotspot_x, - hotspot_y, - }); - let _ = surface_info.cursor_sender.send_replace(cursor_info); - } - wl_pointer::Request::Release => (), - _ => unreachable!(), - } - } -} - -impl Dispatch, WaylandState> for WaylandState { - fn request( - _state: &mut WaylandState, - _client: &Client, - _resource: &WlKeyboard, - request: ::Request, - _data: &Arc, - _dh: &DisplayHandle, - _data_init: &mut DataInit<'_, WaylandState>, - ) { - match request { - wl_keyboard::Request::Release => (), - _ => unreachable!(), - } - } -} - -impl Dispatch, WaylandState> for WaylandState { - fn request( - _state: &mut WaylandState, - _client: &Client, - _resource: &WlTouch, - request: ::Request, - _data: &Arc, - _dh: &DisplayHandle, - _data_init: &mut DataInit<'_, WaylandState>, - ) { - match request { - wl_touch::Request::Release => (), - _ => unreachable!(), - } - } -} diff --git a/src/wayland/state.rs b/src/wayland/state.rs index 77a8a31..ad8d8d6 100644 --- a/src/wayland/state.rs +++ b/src/wayland/state.rs @@ -12,10 +12,7 @@ use smithay::{ input::{keyboard::XkbConfig, SeatState}, output::{Mode, Output, Scale, Subpixel}, reexports::{ - wayland_protocols::xdg::{ - decoration::zv1::server::zxdg_decoration_manager_v1::ZxdgDecorationManagerV1, - shell::server::xdg_wm_base::XdgWmBase, - }, + wayland_protocols::xdg::decoration::zv1::server::zxdg_decoration_manager_v1::ZxdgDecorationManagerV1, wayland_protocols_misc::server_decoration::server::org_kde_kwin_server_decoration_manager::Mode as DecorationMode, wayland_server::{ backend::{ClientData, ClientId, DisconnectReason}, @@ -34,7 +31,7 @@ use smithay::{ self, DmabufFeedback, DmabufFeedbackBuilder, DmabufGlobal, DmabufHandler, DmabufState, }, output::OutputHandler, - shell::kde::decoration::KdeDecorationState, + shell::{kde::decoration::KdeDecorationState, xdg::XdgShellState}, shm::{ShmHandler, ShmState}, }, }; @@ -43,6 +40,7 @@ use tokio::sync::mpsc::UnboundedSender; use tracing::{info, warn}; pub struct ClientState { + pub pid: Option, pub id: OnceCell, pub compositor_state: CompositorClientState, pub display: Weak, @@ -83,6 +81,7 @@ pub struct WaylandState { pub dmabuf_tx: UnboundedSender<(Dmabuf, Option)>, pub seat_state: SeatState, pub seat: Arc, + pub xdg_shell: XdgShellState, pub output: Output, } @@ -163,8 +162,9 @@ impl WaylandState { None, ); output.set_preferred(mode); + + let xdg_shell = XdgShellState::new::(&display_handle); display_handle.create_global::(3, ()); - display_handle.create_global::(5, ()); display_handle.create_global::(1, ()); display_handle.create_global::(2, ()); @@ -184,6 +184,7 @@ impl WaylandState { dmabuf_tx, seat_state, seat: Arc::new(SeatWrapper::new(weak.clone(), seat)), + xdg_shell, output, }) }) diff --git a/src/wayland/surface.rs b/src/wayland/surface.rs index 937229b..4d93f47 100644 --- a/src/wayland/surface.rs +++ b/src/wayland/surface.rs @@ -43,8 +43,8 @@ pub struct CoreSurface { sk_tex: OnceCell, sk_mat: OnceCell>, material_offset: Mutex>, - on_mapped: Box, - on_commit: Box, + on_mapped: Mutex>, + on_commit: Mutex>, pub pending_material_applications: Registry, } @@ -64,8 +64,8 @@ impl CoreSurface { sk_tex: OnceCell::new(), sk_mat: OnceCell::new(), material_offset: Mutex::new(Delta::new(0)), - on_mapped: Box::new(on_mapped) as Box, - on_commit: Box::new(on_commit) as Box, + on_mapped: Mutex::new(Box::new(on_mapped) as Box), + on_commit: Mutex::new(Box::new(on_commit) as Box), pending_material_applications: Registry::new(), }) }); @@ -73,13 +73,18 @@ impl CoreSurface { } pub fn commit(&self, count: u32) { - (self.on_commit)(count); + (*self.on_commit.lock())(count); } pub fn from_wl_surface(surf: &WlSurface) -> Option> { get_data(surf) } + pub fn decycle(&self) { + *self.on_mapped.lock() = Box::new(|| {}); + *self.on_commit.lock() = Box::new(|_| {}); + } + pub fn process(&self, sk: &impl StereoKitDraw, renderer: &mut GlesRenderer) { let Some(wl_surface) = self.wl_surface() else { return; @@ -170,7 +175,7 @@ impl CoreSurface { }); drop(mapped_data); if just_mapped { - (self.on_mapped)(); + (*self.on_mapped.lock())(); } self.apply_surface_materials(); } diff --git a/src/wayland/xdg_shell.rs b/src/wayland/xdg_shell.rs new file mode 100644 index 0000000..9c1dcc8 --- /dev/null +++ b/src/wayland/xdg_shell.rs @@ -0,0 +1,601 @@ +use super::{ + seat::{handle_cursor, SeatWrapper}, + state::{ClientState, WaylandState}, + surface::CoreSurface, + utils, +}; +use crate::nodes::{ + drawable::model::ModelPart, + items::panel::{ + Backend, ChildInfo, Geometry, PanelItem, PanelItemInitData, SurfaceID, ToplevelInfo, + }, +}; +use color_eyre::eyre::Result; +use mint::Vector2; +use parking_lot::Mutex; +use rustc_hash::FxHashMap; +use smithay::{ + delegate_xdg_shell, + reexports::{ + wayland_protocols::xdg::{ + decoration::zv1::server::zxdg_toplevel_decoration_v1::Mode, + shell::server::xdg_toplevel::{ResizeEdge, State}, + }, + wayland_server::{ + protocol::{wl_output::WlOutput, wl_seat::WlSeat, wl_surface::WlSurface}, + Resource, + }, + }, + utils::{Logical, Rectangle, Serial}, + wayland::{ + compositor, + shell::xdg::{ + Configure, PopupSurface, PositionerState, ShellClient, SurfaceCachedState, + ToplevelSurface, XdgShellHandler, XdgShellState, XdgToplevelSurfaceData, + }, + }, +}; +use std::sync::{Arc, Weak}; +use tracing::warn; + +impl From> for Geometry { + fn from(value: Rectangle) -> Self { + Geometry { + origin: [value.loc.x, value.loc.y].into(), + size: [value.size.w as u32, value.size.h as u32].into(), + } + } +} + +fn surface_panel_item(wl_surface: &WlSurface) -> Option>> { + let panel_item = utils::get_data::>>(wl_surface) + .as_deref() + .and_then(Weak::upgrade); + if panel_item.is_none() { + warn!("Couldn't get panel item"); + // println!("panel item not found at \n{}\n\n", { + // let backtrace = Backtrace::force_capture().to_string(); + // let mut split_backtrace = backtrace + // .split('\n') + // .map(|s| s.to_string()) + // .collect::>(); + // split_backtrace.resize(4, "".to_string()); + // split_backtrace.join("\n") + // }); + } + panel_item +} + +impl XdgShellHandler for WaylandState { + fn xdg_shell_state(&mut self) -> &mut XdgShellState { + &mut self.xdg_shell + } + + fn new_client(&mut self, _client: ShellClient) {} + fn client_destroyed(&mut self, _client: ShellClient) {} + + fn new_toplevel(&mut self, toplevel: ToplevelSurface) { + toplevel.with_pending_state(|s| { + s.decoration_mode = Some(Mode::ClientSide); + s.states.set(State::TiledTop); + s.states.set(State::TiledBottom); + s.states.set(State::TiledLeft); + s.states.set(State::TiledRight); + s.states.set(State::Maximized); + s.states.unset(State::Fullscreen); + }); + toplevel.send_configure(); + utils::insert_data(toplevel.wl_surface(), SurfaceID::Toplevel); + CoreSurface::add_to( + self.display_handle.clone(), + toplevel.wl_surface(), + { + let toplevel = toplevel.clone(); + move || { + let wl_surface = toplevel.wl_surface().client().unwrap(); + let client_state = wl_surface.get_data::().unwrap(); + let (node, panel_item) = PanelItem::create( + Box::new(XdgBackend::create( + toplevel.clone(), + client_state.seat.clone(), + )), + client_state.pid.clone(), + ); + handle_cursor(&panel_item, panel_item.backend.seat.cursor_info_rx.clone()); + utils::insert_data(toplevel.wl_surface(), Arc::downgrade(&panel_item)); + utils::insert_data_raw(toplevel.wl_surface(), node); + } + }, + { + let toplevel = toplevel.clone(); + move |_c| { + let Some(panel_item) = surface_panel_item(toplevel.wl_surface()) else { + // if the wayland toplevel isn't mapped, hammer it again with a configure until it cooperates + toplevel.send_configure(); + return; + }; + let Some(core_surface) = CoreSurface::from_wl_surface(toplevel.wl_surface()) + else { + return; + }; + let Some(size) = core_surface.size() else { + return; + }; + panel_item.toplevel_size_changed(size); + } + }, + ); + } + fn toplevel_destroyed(&mut self, toplevel: ToplevelSurface) { + if let Some(core_surface) = CoreSurface::from_wl_surface(toplevel.wl_surface()) { + core_surface.decycle(); + } + let Some(panel_item) = surface_panel_item(toplevel.wl_surface()) else { + return; + }; + panel_item.backend.seat.unfocus(toplevel.wl_surface(), self); + panel_item.backend.toplevel.lock().take(); + panel_item.backend.popups.lock().clear(); + panel_item.drop_toplevel(); + // println!( + // "Dropping toplevel resulted in {} references", + // Arc::strong_count(&panel_item) + // ); + } + fn app_id_changed(&mut self, toplevel: ToplevelSurface) { + let Some(panel_item) = surface_panel_item(toplevel.wl_surface()) else { + return; + }; + + panel_item.toplevel_app_id_changed(&compositor::with_states( + toplevel.wl_surface(), + |states| { + states + .data_map + .get::() + .unwrap() + .lock() + .unwrap() + .app_id + .clone() + .unwrap() + }, + )) + } + fn title_changed(&mut self, toplevel: ToplevelSurface) { + let Some(panel_item) = surface_panel_item(toplevel.wl_surface()) else { + return; + }; + + panel_item.toplevel_title_changed(&compositor::with_states( + toplevel.wl_surface(), + |states| { + states + .data_map + .get::() + .unwrap() + .lock() + .unwrap() + .title + .clone() + .unwrap() + }, + )) + } + + fn new_popup(&mut self, popup: PopupSurface, positioner: PositionerState) { + let uid = nanoid::nanoid!(); + let Some(parent) = popup.get_parent_surface() else { + return; + }; + let Some(panel_item) = surface_panel_item(&parent) else { + return; + }; + if popup.send_configure().is_err() { + return; + } + utils::insert_data(popup.wl_surface(), SurfaceID::Child(uid.clone())); + utils::insert_data(popup.wl_surface(), uid.clone()); + utils::insert_data(popup.wl_surface(), Arc::downgrade(&panel_item)); + CoreSurface::add_to( + self.display_handle.clone(), + popup.wl_surface(), + { + let popup = popup.clone(); + move || { + panel_item + .backend + .new_popup(&uid, popup.clone(), positioner); + } + }, + { + let popup = popup.clone(); + move |_c| { + if surface_panel_item(popup.wl_surface()).is_none() { + // if the popup toplevel isn't mapped, hammer it again with a configure until it cooperates + let _ = popup.send_configure(); + }; + } + }, + ); + } + fn reposition_request( + &mut self, + popup: PopupSurface, + positioner: PositionerState, + _token: u32, + ) { + let Some(panel_item) = surface_panel_item(popup.wl_surface()) else { + return; + }; + let Some(uid) = utils::get_data::(popup.wl_surface()) + .as_deref() + .cloned() + else { + return; + }; + + panel_item.backend.reposition_popup(&uid, popup, positioner) + } + fn popup_destroyed(&mut self, popup: PopupSurface) { + if let Some(core_surface) = CoreSurface::from_wl_surface(popup.wl_surface()) { + core_surface.decycle(); + } + let Some(panel_item) = surface_panel_item(popup.wl_surface()) else { + return; + }; + let Some(uid) = utils::get_data::(popup.wl_surface()) + .as_deref() + .cloned() + else { + return; + }; + panel_item.backend.seat.unfocus(popup.wl_surface(), self); + panel_item.backend.drop_popup(&uid); + } + + fn grab(&mut self, _popup: PopupSurface, _seat: WlSeat, _serial: Serial) {} + + fn move_request(&mut self, toplevel: ToplevelSurface, _seat: WlSeat, _serial: Serial) { + let Some(panel_item) = surface_panel_item(toplevel.wl_surface()) else { + return; + }; + panel_item.toplevel_move_request(); + } + fn resize_request( + &mut self, + toplevel: ToplevelSurface, + _seat: WlSeat, + _serial: Serial, + edges: ResizeEdge, + ) { + let Some(panel_item) = surface_panel_item(toplevel.wl_surface()) else { + return; + }; + let (up, down, left, right) = match edges { + ResizeEdge::None => (false, false, false, false), + ResizeEdge::Top => (true, false, false, false), + ResizeEdge::Bottom => (false, true, false, false), + ResizeEdge::Left => (false, false, true, false), + ResizeEdge::TopLeft => (true, false, true, false), + ResizeEdge::BottomLeft => (false, true, true, false), + ResizeEdge::Right => (false, false, false, true), + ResizeEdge::TopRight => (true, false, false, true), + ResizeEdge::BottomRight => (false, true, false, true), + _ => (false, false, false, false), + }; + panel_item.toplevel_resize_request(up, down, left, right); + } + + fn maximize_request(&mut self, toplevel: ToplevelSurface) { + toplevel.with_pending_state(|s| { + s.states.set(State::Maximized); + s.states.unset(State::Fullscreen); + }); + toplevel.send_configure(); + + let Some(panel_item) = surface_panel_item(toplevel.wl_surface()) else { + return; + }; + panel_item.toplevel_fullscreen_active(false); + } + fn fullscreen_request(&mut self, toplevel: ToplevelSurface, _output: Option) { + toplevel.with_pending_state(|s| { + s.states.set(State::Fullscreen); + s.states.unset(State::Maximized); + }); + toplevel.send_configure(); + + let Some(panel_item) = surface_panel_item(toplevel.wl_surface()) else { + return; + }; + panel_item.toplevel_fullscreen_active(true); + } + + fn ack_configure(&mut self, surface: WlSurface, configure: Configure) { + let Some(panel_item) = surface_panel_item(&surface) else { + return; + }; + match configure { + Configure::Toplevel(t) => { + if let Some(size) = t.state.size { + panel_item.toplevel_size_changed([size.w as u32, size.h as u32].into()) + } + } + Configure::Popup(_p) => (), + } + } +} +delegate_xdg_shell!(WaylandState); + +pub struct XdgBackend { + toplevel: Mutex>, + popups: Mutex>, + pub seat: Arc, +} +impl XdgBackend { + pub fn create(toplevel: ToplevelSurface, seat: Arc) -> Self { + XdgBackend { + toplevel: Mutex::new(Some(toplevel)), + popups: Mutex::new(FxHashMap::default()), + seat, + } + } + fn wl_surface_from_id(&self, id: &SurfaceID) -> Option { + match id { + SurfaceID::Cursor => self + .seat + .cursor_info_rx + .borrow() + .surface + .clone()? + .upgrade() + .ok(), + SurfaceID::Toplevel => Some(self.toplevel.lock().clone()?.wl_surface().clone()), + SurfaceID::Child(popup) => { + let popups = self.popups.lock(); + Some(popups.get(popup)?.0.wl_surface().clone()) + } + } + } + fn panel_item(&self) -> Option>> { + surface_panel_item(self.toplevel.lock().clone()?.wl_surface()) + } + + pub fn new_popup(&self, uid: &str, popup: PopupSurface, positioner: PositionerState) { + let Some(panel_item) = self.panel_item() else { + return; + }; + + self.popups + .lock() + .insert(uid.to_string(), (popup, positioner)); + + panel_item.new_child(uid, self.child_data(uid).unwrap()); + } + pub fn reposition_popup(&self, uid: &str, _popup: PopupSurface, positioner: PositionerState) { + self.popups.lock().get_mut(uid).unwrap().1 = positioner; + + let Some(panel_item) = self.panel_item() else { + return; + }; + let geometry = positioner.get_geometry(); + + panel_item.reposition_child(uid, geometry.into()); + } + pub fn drop_popup(&self, uid: &str) { + let Some(panel_item) = self.panel_item() else { + return; + }; + panel_item.drop_child(uid); + } + + fn child_data(&self, uid: &str) -> Option { + let (popup, positioner) = self.popups.lock().get(uid)?.clone(); + Some(ChildInfo { + parent: (*utils::get_data::(&popup.get_parent_surface()?)?).clone(), + geometry: positioner.get_geometry().into(), + }) + } +} +impl Backend for XdgBackend { + fn start_data(&self) -> Result { + let cursor = self + .seat + .cursor_info_rx + .borrow() + .surface + .clone() + .and_then(|s| s.upgrade().ok()) + .as_ref() + .and_then(CoreSurface::from_wl_surface) + .and_then(|c| c.size()) + .map(|size| Geometry { + origin: [0; 2].into(), + size, + }); + + let toplevel = self.toplevel.lock().clone().unwrap(); + let app_id = compositor::with_states(toplevel.wl_surface(), |states| { + states + .data_map + .get::() + .unwrap() + .lock() + .unwrap() + .title + .clone() + }); + let title = compositor::with_states(toplevel.wl_surface(), |states| { + states + .data_map + .get::() + .unwrap() + .lock() + .unwrap() + .app_id + .clone() + }); + let toplevel_cached_state = compositor::with_states(toplevel.wl_surface(), |states| { + states.cached_state.current::().clone() + }); + let toplevel_core_surface = CoreSurface::from_wl_surface(toplevel.wl_surface()).unwrap(); + + let size = toplevel + .current_state() + .size + .clone() + .map(|s| [s.w as u32, s.h as u32].into()) + .or_else(|| toplevel_core_surface.size()) + .unwrap_or([0; 2].into()); + let toplevel = ToplevelInfo { + parent: toplevel + .parent() + .as_ref() + .and_then(surface_panel_item) + .map(|p| p.uid.clone()), + title, + app_id, + size, + min_size: if toplevel_cached_state.min_size.w != 0 + && toplevel_cached_state.min_size.h != 0 + { + Some( + [ + toplevel_cached_state.min_size.w as u32, + toplevel_cached_state.min_size.h as u32, + ] + .into(), + ) + } else { + None + }, + max_size: if toplevel_cached_state.max_size.w != 0 + && toplevel_cached_state.max_size.h != 0 + { + Some( + [ + toplevel_cached_state.max_size.w as u32, + toplevel_cached_state.max_size.h as u32, + ] + .into(), + ) + } else { + None + }, + logical_rectangle: toplevel_cached_state + .geometry + .map(Into::into) + .unwrap_or(Geometry { + origin: [0; 2].into(), + size, + }), + }; + + let children = self + .popups + .lock() + .keys() + .map(|k| (k.clone(), self.child_data(k).unwrap())) + .collect(); + + Ok(PanelItemInitData { + cursor, + toplevel, + children, + pointer_grab: None, + keyboard_grab: None, + }) + } + + fn surface_alive(&self, surface: &SurfaceID) -> bool { + let Some(surface) = self.wl_surface_from_id(surface) else { + return false; + }; + surface.is_alive() + } + + fn apply_surface_material(&self, surface: SurfaceID, model_part: &Arc) { + let Some(surface) = self.wl_surface_from_id(&surface) else { + return; + }; + let Some(core_surface) = CoreSurface::from_wl_surface(&surface) else { + return; + }; + core_surface.apply_material(model_part); + } + + fn close_toplevel(&self) { + if let Some(toplevel) = self.toplevel.lock().clone() { + toplevel.send_close(); + } + } + + fn auto_size_toplevel(&self) { + let Some(toplevel) = self.toplevel.lock().clone() else { + return; + }; + toplevel.with_pending_state(|s| s.size = None); + toplevel.send_configure(); + } + fn set_toplevel_size(&self, size: Vector2) { + let Some(toplevel) = self.toplevel.lock().clone() else { + return; + }; + toplevel.with_pending_state(|s| s.size = Some((size.x as i32, size.y as i32).into())); + toplevel.send_configure(); + } + fn set_toplevel_focused_visuals(&self, focused: bool) { + let Some(toplevel) = self.toplevel.lock().clone() else { + return; + }; + toplevel.with_pending_state(|s| { + if focused { + s.states.set(State::Activated); + } else { + s.states.unset(State::Activated); + } + }) + } + + fn pointer_motion(&self, surface: &SurfaceID, position: Vector2) { + let Some(surface) = self.wl_surface_from_id(&surface) else { + return; + }; + self.seat.pointer_motion(surface, position) + } + fn pointer_button(&self, _surface: &SurfaceID, button: u32, pressed: bool) { + self.seat.pointer_button(button, pressed) + } + fn pointer_scroll( + &self, + _surface: &SurfaceID, + scroll_distance: Option>, + scroll_steps: Option>, + ) { + self.seat.pointer_scroll(scroll_distance, scroll_steps) + } + + fn keyboard_keys(&self, surface: &SurfaceID, keymap_id: &str, keys: Vec) { + let Some(surface) = self.wl_surface_from_id(&surface) else { + return; + }; + self.seat.keyboard_keys(surface, keymap_id, keys) + } + + fn touch_down(&self, surface: &SurfaceID, id: u32, position: Vector2) { + let Some(surface) = self.wl_surface_from_id(&surface) else { + return; + }; + self.seat.touch_down(surface, id, position) + } + fn touch_move(&self, id: u32, position: Vector2) { + self.seat.touch_move(id, position) + } + fn touch_up(&self, id: u32) { + self.seat.touch_up(id) + } + fn reset_touches(&self) { + self.seat.reset_touches() + } +} diff --git a/src/wayland/xdg_shell/backend.rs b/src/wayland/xdg_shell/backend.rs deleted file mode 100644 index c4ed0dc..0000000 --- a/src/wayland/xdg_shell/backend.rs +++ /dev/null @@ -1,266 +0,0 @@ -use super::{popup::PopupData, surface::XdgSurfaceData, ToplevelData}; -use crate::{ - nodes::{ - drawable::model::ModelPart, - items::panel::{Backend, ChildInfo, PanelItem, PanelItemInitData, SurfaceID}, - }, - wayland::{seat::SeatWrapper, state::ClientState, surface::CoreSurface, utils, SERIAL_COUNTER}, -}; -use color_eyre::eyre::{eyre, Result}; -use mint::Vector2; -use parking_lot::Mutex; -use rustc_hash::FxHashMap; -use smithay::reexports::{ - wayland_protocols::xdg::shell::server::xdg_toplevel::XdgToplevel, - wayland_server::{protocol::wl_surface::WlSurface, Resource, Weak as WlWeak}, -}; -use std::sync::Arc; -use tracing::debug; - -pub struct XdgToplevelState { - pub fullscreen: bool, - pub activated: bool, -} - -pub struct XdgBackend { - toplevel: WlWeak, - toplevel_wl_surface: WlWeak, - pub toplevel_state: Mutex, - popups: Mutex>>, - pub seat: Arc, -} -impl XdgBackend { - pub fn create( - toplevel_wl_surface: WlSurface, - toplevel: XdgToplevel, - seat: Arc, - ) -> Self { - XdgBackend { - toplevel: toplevel.downgrade(), - toplevel_wl_surface: toplevel_wl_surface.downgrade(), - toplevel_state: Mutex::new(XdgToplevelState { - fullscreen: false, - activated: false, - }), - popups: Mutex::new(FxHashMap::default()), - seat, - } - } - fn wl_surface_from_id(&self, id: &SurfaceID) -> Option { - match id { - SurfaceID::Cursor => self - .seat - .cursor_info_rx - .borrow() - .surface - .clone()? - .upgrade() - .ok(), - SurfaceID::Toplevel => self.toplevel_wl_surface(), - SurfaceID::Child(popup) => { - let popups = self.popups.lock(); - popups.get(popup)?.upgrade().ok() - } - } - } - fn toplevel_wl_surface(&self) -> Option { - self.toplevel_wl_surface.upgrade().ok() - } - - pub fn configure(&self, size: Option>) { - let Ok(xdg_toplevel) = self.toplevel.upgrade() else { - return; - }; - let Some(wl_surface) = self.toplevel_wl_surface() else { - return; - }; - let Some(xdg_surface_data) = wl_surface.data::() else { - return; - }; - let Some(core_surface) = CoreSurface::from_wl_surface(&wl_surface) else { - return; - }; - let Some(surface_size) = core_surface.size() else { - return; - }; - - xdg_toplevel.configure( - size.unwrap_or(surface_size).x as i32, - size.unwrap_or(surface_size).y as i32, - self.states() - .into_iter() - .flat_map(|state| state.to_ne_bytes()) - .collect(), - ); - xdg_surface_data.xdg_surface.configure(SERIAL_COUNTER.inc()); - self.flush_client(); - } - fn states(&self) -> Vec { - let mut states = vec![1, 5, 6, 7, 8]; // maximized always and tiled - let toplevel_state = self.toplevel_state.lock(); - if toplevel_state.fullscreen { - states.push(2); - } - if toplevel_state.activated { - states.push(4); - } - states - } - - pub fn new_popup( - &self, - panel_item: &PanelItem, - popup_wl_surface: &WlSurface, - data: &PopupData, - ) { - self.popups - .lock() - .insert(data.uid.clone(), popup_wl_surface.downgrade()); - - let Some(geometry) = data.geometry() else { - return; - }; - panel_item.new_child( - &data.uid, - ChildInfo { - parent: utils::get_data::(&data.parent()) - .unwrap() - .as_ref() - .clone(), - geometry, - }, - ) - } - pub fn reposition_popup(&self, panel_item: &PanelItem, popup_state: &PopupData) { - let Some(geometry) = popup_state.geometry() else { - return; - }; - panel_item.reposition_child(&popup_state.uid, geometry) - } - pub fn drop_popup(&self, panel_item: &PanelItem, uid: &str) { - panel_item.drop_child(uid); - } - - fn child_data(&self) -> FxHashMap { - FxHashMap::from_iter(self.popups.lock().iter().filter_map(|(uid, v)| { - let wl_surface = v.upgrade().ok()?; - let popup_data = utils::get_data::(&wl_surface)?; - let parent = utils::get_data::(&popup_data.parent())? - .as_ref() - .clone(); - let geometry = utils::get_data::(&wl_surface)? - .geometry - .lock() - .clone()?; - Some((uid.clone(), ChildInfo { parent, geometry })) - })) - } - - fn flush_client(&self) { - let Some(client) = self.toplevel_wl_surface().and_then(|s| s.client()) else { - return; - }; - if let Some(client_state) = client.get_data::() { - client_state.flush(); - } - } -} -impl Drop for XdgBackend { - fn drop(&mut self) { - debug!("Dropped panel item gracefully"); - } -} -impl Backend for XdgBackend { - fn start_data(&self) -> Result { - let toplevel = self.toplevel_wl_surface(); - let toplevel_data = toplevel.as_ref().and_then(utils::get_data::); - let toplevel_data = toplevel_data - .as_deref() - .clone() - .ok_or_else(|| eyre!("Could not get toplevel"))?; - - Ok(PanelItemInitData { - cursor: (*self.seat.cursor_info_rx.borrow()).cursor_data(), - toplevel: toplevel_data.into(), - children: self.child_data(), - pointer_grab: None, - keyboard_grab: None, - }) - } - fn surface_alive(&self, surface: &SurfaceID) -> bool { - match surface { - SurfaceID::Cursor => true, - SurfaceID::Toplevel => true, - SurfaceID::Child(c) => self.popups.lock().contains_key(c), - } - } - - fn apply_surface_material(&self, surface: SurfaceID, model_part: &Arc) { - let Some(wl_surface) = self.wl_surface_from_id(&surface) else { - return; - }; - let Some(core_surface) = CoreSurface::from_wl_surface(&wl_surface) else { - return; - }; - - core_surface.apply_material(model_part); - } - - fn close_toplevel(&self) { - let Ok(xdg_toplevel) = self.toplevel.upgrade() else { - return; - }; - xdg_toplevel.close(); - } - fn auto_size_toplevel(&self) { - self.configure(Some([0, 0].into())); - } - fn set_toplevel_size(&self, size: Vector2) { - self.configure(Some(size)); - } - fn set_toplevel_focused_visuals(&self, focused: bool) { - self.toplevel_state.lock().activated = focused; - self.configure(None); - } - - fn pointer_motion(&self, surface: &SurfaceID, position: Vector2) { - let Some(surface) = self.wl_surface_from_id(surface) else { - return; - }; - self.seat.pointer_motion(surface, position) - } - fn pointer_button(&self, _surface: &SurfaceID, button: u32, pressed: bool) { - self.seat.pointer_button(button, pressed) - } - fn pointer_scroll( - &self, - _surface: &SurfaceID, - scroll_distance: Option>, - scroll_steps: Option>, - ) { - self.seat.pointer_scroll(scroll_distance, scroll_steps) - } - - fn keyboard_keys(&self, surface: &SurfaceID, keymap_id: &str, keys: Vec) { - let Some(surface) = self.wl_surface_from_id(surface) else { - return; - }; - self.seat.keyboard_keys(surface, keymap_id, keys) - } - - fn touch_down(&self, surface: &SurfaceID, id: u32, position: Vector2) { - let Some(surface) = self.wl_surface_from_id(surface) else { - return; - }; - self.seat.touch_down(surface, id, position) - } - fn touch_move(&self, id: u32, position: Vector2) { - self.seat.touch_move(id, position) - } - fn touch_up(&self, id: u32) { - self.seat.touch_up(id) - } - fn reset_touches(&self) { - self.seat.reset_touches() - } -} diff --git a/src/wayland/xdg_shell/mod.rs b/src/wayland/xdg_shell/mod.rs deleted file mode 100644 index 89c60af..0000000 --- a/src/wayland/xdg_shell/mod.rs +++ /dev/null @@ -1,69 +0,0 @@ -use self::{backend::XdgBackend, toplevel::ToplevelData}; -use super::state::WaylandState; -use crate::wayland::{ - utils::insert_data, - xdg_shell::{positioner::PositionerData, surface::XdgSurfaceData}, -}; -use parking_lot::Mutex; -use smithay::reexports::{ - wayland_protocols::xdg::shell::server::xdg_wm_base::{self, XdgWmBase}, - wayland_server::{Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource}, -}; -use tracing::debug; - -mod backend; -mod popup; -mod positioner; -mod surface; -mod toplevel; - -impl GlobalDispatch for WaylandState { - fn bind( - _state: &mut WaylandState, - _handle: &DisplayHandle, - _client: &Client, - resource: New, - _global_data: &(), - data_init: &mut DataInit<'_, WaylandState>, - ) { - data_init.init(resource, ()); - } -} - -impl Dispatch for WaylandState { - fn request( - _state: &mut WaylandState, - _client: &Client, - _resource: &XdgWmBase, - request: xdg_wm_base::Request, - _data: &(), - _dhandle: &DisplayHandle, - data_init: &mut DataInit<'_, WaylandState>, - ) { - match request { - xdg_wm_base::Request::CreatePositioner { id } => { - let positioner = data_init.init(id, Mutex::new(PositionerData::default())); - debug!(?positioner, "Create XDG positioner"); - } - xdg_wm_base::Request::GetXdgSurface { id, surface } => { - let xdg_surface = data_init.init(id, surface.downgrade()); - debug!(?xdg_surface, "Create XDG surface"); - insert_data( - &surface, - XdgSurfaceData { - wl_surface: surface.downgrade(), - xdg_surface, - geometry: Mutex::new(None), - }, - ); - } - xdg_wm_base::Request::Pong { serial } => { - debug!(serial, "Client pong"); - } - xdg_wm_base::Request::Destroy => { - debug!("Destroy XDG WM base"); - } - _ => unreachable!(), - } - } -} diff --git a/src/wayland/xdg_shell/popup.rs b/src/wayland/xdg_shell/popup.rs deleted file mode 100644 index a474335..0000000 --- a/src/wayland/xdg_shell/popup.rs +++ /dev/null @@ -1,119 +0,0 @@ -use super::{backend::XdgBackend, positioner::PositionerData}; -use crate::{ - nodes::items::panel::{Geometry, PanelItem, SurfaceID}, - wayland::{state::WaylandState, utils::get_data}, -}; -use parking_lot::Mutex; -use smithay::reexports::{ - wayland_protocols::xdg::shell::server::{ - xdg_popup::{self, XdgPopup}, - xdg_positioner::XdgPositioner, - }, - wayland_server::{ - protocol::wl_surface::WlSurface, Client, DataInit, Dispatch, DisplayHandle, Resource, - Weak as WlWeak, - }, -}; -use std::sync::{Arc, Weak}; -use tracing::{debug, error}; -use wayland_backend::server::ClientId; - -#[derive(Debug)] -pub struct PopupData { - pub uid: String, - grabbed: Mutex, - parent: Mutex>, - panel_item: Weak>, - positioner: Mutex, -} -impl PopupData { - pub fn new( - uid: impl ToString, - parent: WlSurface, - panel_item: &Arc>, - positioner: XdgPositioner, - ) -> Self { - PopupData { - uid: uid.to_string(), - grabbed: Mutex::new(false), - parent: Mutex::new(parent.downgrade()), - panel_item: Arc::downgrade(panel_item), - positioner: Mutex::new(positioner), - } - } - pub fn geometry(&self) -> Option { - let positioner = self.positioner.lock().clone(); - let positioner_data = positioner.data::>()?.lock(); - Some(positioner_data.clone().into()) - } - pub fn parent(&self) -> WlSurface { - self.parent.lock().upgrade().unwrap() - } -} - -impl Dispatch, WaylandState> for WaylandState { - fn request( - _state: &mut WaylandState, - _client: &Client, - xdg_popup: &XdgPopup, - request: xdg_popup::Request, - wl_surface_resource: &WlWeak, - _dhandle: &DisplayHandle, - _data_init: &mut DataInit<'_, WaylandState>, - ) { - let Ok(wl_surface) = wl_surface_resource.upgrade() else { - error!("Couldn't get the wayland surface of the xdg popup"); - return; - }; - let Some(popup_data) = get_data::(&wl_surface) else { - error!("Couldn't get the XdgPopup"); - return; - }; - let Some(panel_item) = popup_data.panel_item.upgrade() else { - error!("Couldn't get the panel item"); - return; - }; - match request { - xdg_popup::Request::Grab { seat, serial } => { - *popup_data.grabbed.lock() = true; - debug!(?xdg_popup, ?seat, serial, "XDG popup grab"); - panel_item.grab_keyboard(Some(SurfaceID::Child(popup_data.uid.clone()))); - } - xdg_popup::Request::Reposition { positioner, token } => { - debug!(?xdg_popup, ?positioner, token, "XDG popup reposition"); - *popup_data.positioner.lock() = positioner; - panel_item - .backend - .reposition_popup(&panel_item, &popup_data); - } - xdg_popup::Request::Destroy => { - debug!(?xdg_popup, "Destroy XDG popup"); - if *popup_data.grabbed.lock() { - panel_item.grab_keyboard(None); - } - } - _ => unreachable!(), - } - } - - fn destroyed( - _state: &mut WaylandState, - _client: ClientId, - _popup: &XdgPopup, - data: &WlWeak, - ) { - let Ok(wl_surface) = data.upgrade() else { - error!("Couldn't get the wayland surface of the xdg popup"); - return; - }; - let Some(popup_data) = get_data::(&wl_surface) else { - error!("Couldn't get the XdgPopup"); - return; - }; - let Some(panel_item) = popup_data.panel_item.upgrade() else { - error!("Couldn't get the panel item"); - return; - }; - panel_item.backend.drop_popup(&panel_item, &popup_data.uid); - } -} diff --git a/src/wayland/xdg_shell/positioner.rs b/src/wayland/xdg_shell/positioner.rs deleted file mode 100644 index 24caf26..0000000 --- a/src/wayland/xdg_shell/positioner.rs +++ /dev/null @@ -1,226 +0,0 @@ -use crate::{nodes::items::panel::Geometry, wayland::state::WaylandState}; -use mint::Vector2; -use parking_lot::Mutex; -use smithay::reexports::{ - wayland_protocols::xdg::shell::server::xdg_positioner::{ - self, Anchor, ConstraintAdjustment, Gravity, XdgPositioner, - }, - wayland_server::{Client, DataInit, Dispatch, DisplayHandle, Resource}, -}; -use tracing::{debug, warn}; -use wayland_backend::protocol::WEnum; - -#[derive(Debug, Clone, Copy)] -pub struct PositionerData { - size: Vector2, - anchor_rect_pos: Vector2, - anchor_rect_size: Vector2, - anchor: Anchor, - gravity: Gravity, - constraint_adjustment: ConstraintAdjustment, - offset: Vector2, - reactive: bool, -} -impl Default for PositionerData { - fn default() -> Self { - Self { - size: Vector2::from([0; 2]), - anchor_rect_pos: Vector2::from([0; 2]), - anchor_rect_size: Vector2::from([0; 2]), - anchor: Anchor::None, - gravity: Gravity::None, - constraint_adjustment: ConstraintAdjustment::None, - offset: Vector2::from([0; 2]), - reactive: false, - } - } -} - -impl PositionerData { - fn anchor_has_edge(&self, edge: Anchor) -> bool { - match edge { - Anchor::Top => { - self.anchor == Anchor::Top - || self.anchor == Anchor::TopLeft - || self.anchor == Anchor::TopRight - } - Anchor::Bottom => { - self.anchor == Anchor::Bottom - || self.anchor == Anchor::BottomLeft - || self.anchor == Anchor::BottomRight - } - Anchor::Left => { - self.anchor == Anchor::Left - || self.anchor == Anchor::TopLeft - || self.anchor == Anchor::BottomLeft - } - Anchor::Right => { - self.anchor == Anchor::Right - || self.anchor == Anchor::TopRight - || self.anchor == Anchor::BottomRight - } - _ => unreachable!(), - } - } - - fn gravity_has_edge(&self, edge: Gravity) -> bool { - match edge { - Gravity::Top => { - self.gravity == Gravity::Top - || self.gravity == Gravity::TopLeft - || self.gravity == Gravity::TopRight - } - Gravity::Bottom => { - self.gravity == Gravity::Bottom - || self.gravity == Gravity::BottomLeft - || self.gravity == Gravity::BottomRight - } - Gravity::Left => { - self.gravity == Gravity::Left - || self.gravity == Gravity::TopLeft - || self.gravity == Gravity::BottomLeft - } - Gravity::Right => { - self.gravity == Gravity::Right - || self.gravity == Gravity::TopRight - || self.gravity == Gravity::BottomRight - } - _ => unreachable!(), - } - } - - pub fn get_pos(&self) -> Vector2 { - let mut pos = self.offset; - - if self.anchor_has_edge(Anchor::Top) { - pos.y += self.anchor_rect_pos.y; - } else if self.anchor_has_edge(Anchor::Bottom) { - pos.y += self.anchor_rect_pos.y + self.anchor_rect_size.y as i32; - } else { - pos.y += self.anchor_rect_pos.y + self.anchor_rect_size.y as i32 / 2; - } - - if self.anchor_has_edge(Anchor::Left) { - pos.x += self.anchor_rect_pos.x; - } else if self.anchor_has_edge(Anchor::Right) { - pos.x += self.anchor_rect_pos.x + self.anchor_rect_size.x as i32; - } else { - pos.x += self.anchor_rect_pos.x + self.anchor_rect_size.x as i32 / 2; - } - - if self.gravity_has_edge(Gravity::Top) { - pos.y -= self.size.y as i32; - } else if !self.gravity_has_edge(Gravity::Bottom) { - pos.y -= self.size.y as i32 / 2; - } - - if self.gravity_has_edge(Gravity::Left) { - pos.x -= self.size.x as i32; - } else if !self.gravity_has_edge(Gravity::Right) { - pos.x -= self.size.x as i32 / 2; - } - - pos - } -} -impl From for Geometry { - fn from(value: PositionerData) -> Self { - Geometry { - origin: value.get_pos(), - size: value.size, - } - } -} - -impl Dispatch, WaylandState> for WaylandState { - fn request( - _state: &mut WaylandState, - _client: &Client, - positioner: &XdgPositioner, - request: xdg_positioner::Request, - data: &Mutex, - _dhandle: &DisplayHandle, - _data_init: &mut DataInit<'_, WaylandState>, - ) { - match request { - xdg_positioner::Request::SetSize { width, height } => { - debug!(?positioner, width, height, "Set positioner size"); - data.lock().size = Vector2::from([width as u32, height as u32]); - } - xdg_positioner::Request::SetAnchorRect { - x, - y, - width, - height, - } => { - if width < 1 || height < 1 { - positioner.post_error( - xdg_positioner::Error::InvalidInput, - "Invalid size for positioner's anchor rectangle.", - ); - warn!( - ?positioner, - width, height, "Invalid size for positioner's anchor rectangle" - ); - return; - } - - debug!( - ?positioner, - x, y, width, height, "Set positioner anchor rectangle" - ); - let mut data = data.lock(); - data.anchor_rect_pos = [x, y].into(); - data.anchor_rect_size = [width as u32, height as u32].into(); - } - xdg_positioner::Request::SetAnchor { anchor } => { - if let WEnum::Value(anchor) = anchor { - debug!(?positioner, ?anchor, "Set positioner anchor"); - data.lock().anchor = anchor; - } - } - xdg_positioner::Request::SetGravity { gravity } => { - if let WEnum::Value(gravity) = gravity { - debug!(?positioner, ?gravity, "Set positioner gravity"); - data.lock().gravity = gravity; - } - } - xdg_positioner::Request::SetConstraintAdjustment { - constraint_adjustment, - } => { - debug!( - ?positioner, - constraint_adjustment, "Set positioner constraint adjustment" - ); - let Some(constraint_adjustment) = - ConstraintAdjustment::from_bits(constraint_adjustment) - else { - return; - }; - data.lock().constraint_adjustment = constraint_adjustment; - } - xdg_positioner::Request::SetOffset { x, y } => { - debug!(?positioner, x, y, "Set positioner offset"); - data.lock().offset = [x, y].into(); - } - xdg_positioner::Request::SetReactive => { - debug!(?positioner, "Set positioner reactive"); - data.lock().reactive = true; - } - xdg_positioner::Request::SetParentSize { - parent_width, - parent_height, - } => { - debug!( - ?positioner, - parent_width, parent_height, "Set positioner parent size" - ); - } - xdg_positioner::Request::SetParentConfigure { serial } => { - debug!(?positioner, serial, "Set positioner parent size"); - } - xdg_positioner::Request::Destroy => (), - _ => unreachable!(), - } - } -} diff --git a/src/wayland/xdg_shell/surface.rs b/src/wayland/xdg_shell/surface.rs deleted file mode 100644 index 937c2d5..0000000 --- a/src/wayland/xdg_shell/surface.rs +++ /dev/null @@ -1,242 +0,0 @@ -use crate::{ - nodes::items::panel::{Geometry, PanelItem, SurfaceID}, - wayland::{ - seat::handle_cursor, - state::{ClientState, WaylandState}, - surface::CoreSurface, - utils, - xdg_shell::{popup::PopupData, toplevel::ToplevelData, XdgBackend}, - SERIAL_COUNTER, - }, -}; -use nanoid::nanoid; -use parking_lot::Mutex; -use smithay::reexports::{ - wayland_protocols::xdg::shell::server::{ - xdg_surface::{self, XdgSurface}, - xdg_toplevel::{WmCapabilities, XdgToplevel, EVT_WM_CAPABILITIES_SINCE}, - }, - wayland_server::{ - protocol::wl_surface::WlSurface, Client, DataInit, Dispatch, DisplayHandle, Resource, - Weak as WlWeak, - }, -}; -use std::sync::{Arc, Weak}; -use tracing::{debug, error}; - -#[derive(Debug)] -pub struct XdgSurfaceData { - pub wl_surface: WlWeak, - pub xdg_surface: XdgSurface, - pub geometry: Mutex>, -} - -impl Dispatch, WaylandState> for WaylandState { - fn request( - state: &mut WaylandState, - client: &Client, - xdg_surface: &XdgSurface, - request: xdg_surface::Request, - wl_surface_resource: &WlWeak, - _dhandle: &DisplayHandle, - data_init: &mut DataInit<'_, WaylandState>, - ) { - let Ok(wl_surface) = wl_surface_resource.upgrade() else { - error!("Couldn't get the wayland surface of the xdg surface"); - return; - }; - let Some(xdg_surface_data) = utils::get_data::(&wl_surface) else { - error!("Couldn't get the XdgSurface"); - return; - }; - match request { - xdg_surface::Request::GetToplevel { id } => { - let toplevel = data_init.init(id, wl_surface_resource.clone()); - utils::insert_data(&wl_surface, SurfaceID::Toplevel); - utils::insert_data(&wl_surface, toplevel.clone()); - utils::insert_data(&wl_surface, ToplevelData::new(&wl_surface)); - debug!(?toplevel, ?xdg_surface, "Create XDG toplevel"); - - if toplevel.version() >= EVT_WM_CAPABILITIES_SINCE { - toplevel.wm_capabilities( - vec![WmCapabilities::Maximize, WmCapabilities::Fullscreen] - .into_iter() - .map(u32::from) - .flat_map(u32::to_ne_bytes) - .collect(), - ); - } - toplevel.configure( - 0, - 0, - if toplevel.version() >= 2 { - vec![1, 5, 6, 7, 8] - .into_iter() - .flat_map(u32::to_ne_bytes) - .collect() - } else { - vec![1].into_iter().flat_map(u32::to_ne_bytes).collect() - }, - ); - xdg_surface.configure(SERIAL_COUNTER.inc()); - - let client_credentials = client.get_credentials(&state.display_handle).ok(); - let Some(seat_data) = client.get_data::().map(|s| s.seat.clone()) - else { - return; - }; - - let xdg_surface = xdg_surface.clone(); - CoreSurface::add_to( - state.display_handle.clone(), - &wl_surface, - { - let wl_surface_resource = wl_surface_resource.clone(); - move || { - let wl_surface = wl_surface_resource.upgrade().unwrap(); - - let backend = XdgBackend::create( - wl_surface.clone(), - toplevel.clone(), - seat_data.clone(), - ); - let (node, panel_item) = PanelItem::create( - Box::new(backend), - client_credentials.map(|c| c.pid), - ); - utils::insert_data(&wl_surface, Arc::downgrade(&panel_item)); - utils::insert_data_raw(&wl_surface, node); - handle_cursor( - &panel_item, - panel_item.backend.seat.cursor_info_rx.clone(), - ); - } - }, - { - let wl_surface_resource = wl_surface_resource.clone(); - move |_| { - let wl_surface = wl_surface_resource.upgrade().unwrap(); - - let Some(panel_item) = - utils::get_data::>(&wl_surface) - else { - let Some(toplevel) = utils::get_data::(&wl_surface) - else { - return; - }; - // if the wayland toplevel isn't mapped, hammer it again with a configure until it cooperates - toplevel.configure( - 0, - 0, - if toplevel.version() >= 2 { - vec![5, 6, 7, 8] - .into_iter() - .flat_map(u32::to_ne_bytes) - .collect() - } else { - vec![] - }, - ); - xdg_surface.configure(SERIAL_COUNTER.inc()); - return; - }; - let Some(core_surface) = CoreSurface::from_wl_surface(&wl_surface) - else { - return; - }; - let Some(size) = core_surface.size() else { - return; - }; - panel_item.toplevel_size_changed(size); - } - }, - ); - } - xdg_surface::Request::GetPopup { - id, - parent, - positioner, - } => { - let Some(parent) = parent else { return }; - let Some(parent_wl_surface) = parent - .data::>() - .map(WlWeak::upgrade) - .map(Result::ok) - .flatten() - else { - return; - }; - let Some(panel_item) = - utils::get_data::>>(&parent_wl_surface) - .as_deref() - .and_then(Weak::upgrade) - else { - return; - }; - - let uid = nanoid!(); - let popup_data = PopupData::new( - uid.clone(), - parent_wl_surface.clone(), - &panel_item, - positioner, - ); - handle_cursor(&panel_item, panel_item.backend.seat.cursor_info_rx.clone()); - let xdg_popup = data_init.init(id, wl_surface.downgrade()); - utils::insert_data(&wl_surface, SurfaceID::Child(uid)); - utils::insert_data(&wl_surface, Arc::downgrade(&panel_item)); - utils::insert_data(&wl_surface, popup_data); - utils::insert_data(&wl_surface, xdg_popup.clone()); - debug!(?xdg_popup, ?xdg_surface, "Create XDG popup"); - - let xdg_surface = xdg_surface.downgrade(); - let popup_wl_surface = wl_surface.downgrade(); - CoreSurface::add_to( - state.display_handle.clone(), - &wl_surface, - move || { - let Ok(wl_surface) = popup_wl_surface.upgrade() else { - return; - }; - let Some(popup_data) = utils::get_data::(&wl_surface) else { - return; - }; - panel_item - .backend - .new_popup(&panel_item, &wl_surface, &*popup_data); - }, - move |commit_count| { - if commit_count == 0 { - if let Ok(xdg_surface) = xdg_surface.upgrade() { - xdg_surface.configure(SERIAL_COUNTER.inc()) - } - } - }, - ); - } - xdg_surface::Request::SetWindowGeometry { - x, - y, - width, - height, - } => { - debug!( - ?xdg_surface, - x, y, width, height, "Set XDG surface geometry" - ); - let geometry = Geometry { - origin: [x, y].into(), - size: [width as u32, height as u32].into(), - }; - xdg_surface_data.geometry.lock().replace(geometry); - } - xdg_surface::Request::AckConfigure { serial } => { - debug!(?xdg_surface, serial, "Acknowledge XDG surface configure"); - } - xdg_surface::Request::Destroy => { - debug!(?xdg_surface, "Destroy XDG surface"); - } - _ => unreachable!(), - } - } -} diff --git a/src/wayland/xdg_shell/toplevel.rs b/src/wayland/xdg_shell/toplevel.rs deleted file mode 100644 index e196f98..0000000 --- a/src/wayland/xdg_shell/toplevel.rs +++ /dev/null @@ -1,238 +0,0 @@ -use super::{backend::XdgBackend, surface::XdgSurfaceData}; -use crate::{ - nodes::items::panel::{Geometry, PanelItem, ToplevelInfo}, - wayland::{ - state::WaylandState, - surface::CoreSurface, - utils::{self, get_data}, - }, -}; -use mint::Vector2; -use once_cell::sync::OnceCell; -use parking_lot::Mutex; -use smithay::reexports::{ - wayland_protocols::xdg::shell::server::xdg_toplevel::{self, ResizeEdge, XdgToplevel}, - wayland_server::{ - protocol::wl_surface::WlSurface, Client, DataInit, Dispatch, DisplayHandle, Resource, - Weak as WlWeak, - }, -}; -use std::sync::Weak; -use tracing::{debug, error}; -use wayland_backend::protocol::WEnum; - -pub struct ToplevelData { - panel_item: OnceCell>>, - wl_surface: WlWeak, - parent: Mutex>>, - title: Mutex>, - app_id: Mutex>, - max_size: Mutex>>, - min_size: Mutex>>, -} -impl ToplevelData { - pub fn new(wl_surface: &WlSurface) -> Self { - ToplevelData { - panel_item: OnceCell::new(), - wl_surface: wl_surface.downgrade(), - parent: Mutex::new(None), - title: Mutex::new(None), - app_id: Mutex::new(None), - max_size: Mutex::new(None), - min_size: Mutex::new(None), - } - } - pub fn parent(&self) -> Option { - self.parent - .lock() - .as_ref() - .map(WlWeak::upgrade) - .map(Result::ok) - .flatten() - } -} -impl From<&ToplevelData> for ToplevelInfo { - fn from(value: &ToplevelData) -> Self { - let wl_surface = value.wl_surface.upgrade().ok(); - let size = CoreSurface::from_wl_surface(wl_surface.as_ref().unwrap()) - .unwrap() - .size() - .unwrap(); - let logical_rectangle = wl_surface - .as_ref() - .and_then(utils::get_data::) - .and_then(|d| d.geometry.lock().clone()) - .unwrap_or_else(|| Geometry { - origin: [0, 0].into(), - size, - }); - let parent = value - .parent() - .as_ref() - .and_then(utils::get_data::>>) - .as_deref() - .and_then(Weak::upgrade) - .map(|i| i.uid.clone()); - ToplevelInfo { - parent, - title: value.title.lock().clone(), - app_id: value.app_id.lock().clone(), - size, - min_size: value.min_size.lock().clone(), - max_size: value.max_size.lock().clone(), - logical_rectangle, - } - } -} -impl Drop for ToplevelData { - fn drop(&mut self) { - // let Some(panel_item) = self.panel_item.get().and_then(Weak::upgrade) else { - // return; - // }; - // panel_item.drop_toplevel(); - } -} - -impl Dispatch, WaylandState> for WaylandState { - fn request( - state: &mut WaylandState, - _client: &Client, - xdg_toplevel: &XdgToplevel, - request: xdg_toplevel::Request, - wl_surface_resource: &WlWeak, - _dhandle: &DisplayHandle, - _data_init: &mut DataInit<'_, WaylandState>, - ) { - let Ok(wl_surface) = wl_surface_resource.upgrade() else { - error!("Couldn't get the wayland surface of the xdg toplevel"); - return; - }; - let Some(toplevel_data) = utils::get_data::(&wl_surface) else { - error!("Couldn't get the XdgToplevel"); - return; - }; - match request { - xdg_toplevel::Request::SetParent { parent } => { - debug!(?xdg_toplevel, ?parent, "Set XDG Toplevel parent"); - let Some(parent_xdg_toplevel) = parent else { - *toplevel_data.parent.lock() = None; - return; - }; - let Some(parent_toplevel_data) = parent_xdg_toplevel.data::() else { - error!("Couldn't get XDG toplevel parent data"); - return; - }; - let Ok(parent_wl_surface) = parent_toplevel_data.wl_surface.upgrade() else { - error!("Couldn't get XDG toplevel parent wl surface"); - return; - }; - *toplevel_data.parent.lock() = Some(parent_wl_surface.downgrade()); - let Some(parent_panel_item) = parent_toplevel_data - .panel_item - .get() - .and_then(Weak::upgrade) - else { - return; - }; - let Some(panel_item) = get_data::>(&wl_surface) else { - error!("Couldn't get the panel item"); - return; - }; - panel_item.toplevel_parent_changed(&parent_panel_item.uid); - } - xdg_toplevel::Request::SetTitle { title } => { - debug!(?xdg_toplevel, ?title, "Set XDG Toplevel title"); - *toplevel_data.title.lock() = (!title.is_empty()).then_some(title.clone()); - let Some(panel_item) = get_data::>(&wl_surface) else { - error!("Couldn't get the panel item"); - return; - }; - panel_item.toplevel_title_changed(&title); - } - xdg_toplevel::Request::SetAppId { app_id } => { - debug!(?xdg_toplevel, ?app_id, "Set XDG Toplevel app ID"); - *toplevel_data.app_id.lock() = (!app_id.is_empty()).then_some(app_id.clone()); - let Some(panel_item) = get_data::>(&wl_surface) else { - error!("Couldn't get the panel item"); - return; - }; - panel_item.toplevel_app_id_changed(&app_id); - } - xdg_toplevel::Request::Move { seat, serial } => { - debug!(?xdg_toplevel, ?seat, serial, "XDG Toplevel move request"); - let Some(panel_item) = get_data::>(&wl_surface) else { - error!("Couldn't get the panel item"); - return; - }; - panel_item.toplevel_move_request(); - } - xdg_toplevel::Request::Resize { - seat, - serial, - edges, - } => { - let WEnum::Value(edges) = edges else { return }; - debug!( - ?xdg_toplevel, - ?seat, - serial, - ?edges, - "XDG Toplevel resize request" - ); - let (up, down, left, right) = match edges { - ResizeEdge::Top => (true, false, false, false), - ResizeEdge::Bottom => (false, true, false, false), - ResizeEdge::Left => (false, false, true, false), - ResizeEdge::TopLeft => (true, false, true, false), - ResizeEdge::BottomLeft => (false, true, true, false), - ResizeEdge::Right => (false, false, false, true), - ResizeEdge::TopRight => (true, false, false, true), - ResizeEdge::BottomRight => (false, true, false, true), - _ => (false, false, false, false), - }; - let Some(panel_item) = get_data::>(&wl_surface) else { - error!("Couldn't get the panel item"); - return; - }; - panel_item.toplevel_resize_request(up, down, left, right) - } - xdg_toplevel::Request::SetMaxSize { width, height } => { - debug!(?xdg_toplevel, width, height, "Set XDG Toplevel max size"); - *toplevel_data.max_size.lock() = (width > 1 || height > 1) - .then_some(Vector2::from([width as u32, height as u32])); - } - xdg_toplevel::Request::SetMinSize { width, height } => { - debug!(?xdg_toplevel, width, height, "Set XDG Toplevel min size"); - *toplevel_data.min_size.lock() = (width > 1 || height > 1) - .then_some(Vector2::from([width as u32, height as u32])); - } - xdg_toplevel::Request::SetFullscreen { output: _ } => { - let Some(panel_item) = get_data::>(&wl_surface) else { - error!("Couldn't get the panel item"); - return; - }; - panel_item.backend.toplevel_state.lock().fullscreen = true; - panel_item.backend.configure(None); - panel_item.toplevel_fullscreen_active(true); - } - xdg_toplevel::Request::UnsetFullscreen => { - let Some(panel_item) = get_data::>(&wl_surface) else { - error!("Couldn't get the panel item"); - return; - }; - panel_item.backend.toplevel_state.lock().fullscreen = false; - panel_item.backend.configure(None); - panel_item.toplevel_fullscreen_active(false); - } - xdg_toplevel::Request::Destroy => { - debug!(?xdg_toplevel, "Destroy XDG Toplevel"); - let Some(panel_item) = get_data::>(&wl_surface) else { - error!("Couldn't get the panel item"); - return; - }; - panel_item.drop_toplevel(); - } - _ => {} - } - } -}