diff --git a/src/wayland/core/surface.rs b/src/wayland/core/surface.rs index bfe2a53..a249bd5 100644 --- a/src/wayland/core/surface.rs +++ b/src/wayland/core/surface.rs @@ -71,6 +71,13 @@ impl Default for SurfaceState { } } } +impl SurfaceState { + pub fn has_valid_buffer(&self) -> bool { + self.buffer + .as_ref() + .is_some_and(|b| b.buffer.size().x > 0 && b.buffer.size().y > 0) + } +} // if returning false, don't run this callback again... just remove it pub type OnCommitCallback = Box bool + Send + Sync>; diff --git a/src/wayland/xdg/backend.rs b/src/wayland/xdg/backend.rs index bca3a00..1461341 100644 --- a/src/wayland/xdg/backend.rs +++ b/src/wayland/xdg/backend.rs @@ -3,10 +3,16 @@ use crate::{ core::error::Result, nodes::{ drawable::model::ModelPart, - items::panel::{Backend, Geometry, PanelItemInitData, SurfaceId, ToplevelInfo}, + items::panel::{ + Backend, ChildInfo, Geometry, PanelItem, PanelItemInitData, SurfaceId, ToplevelInfo, + }, + }, + wayland::{ + Message, + core::{seat::SeatMessage, surface::Surface}, }, - wayland::{Message, core::surface::Surface}, }; +use dashmap::DashMap; use mint::Vector2; use std::sync::Arc; use std::sync::Weak; @@ -15,12 +21,14 @@ use tracing; #[derive(Debug)] pub struct XdgBackend { toplevel: Weak, + children: DashMap, ChildInfo)>, } impl XdgBackend { pub fn new(toplevel: Arc) -> Self { Self { toplevel: Arc::downgrade(&toplevel), + children: DashMap::new(), } } @@ -32,14 +40,59 @@ impl XdgBackend { .expect("Toplevel should always be valid while XdgBackend exists") } - fn surface_from_id(&self, id: SurfaceId) -> Option> { + pub fn panel_item(&self) -> Option>> { + self.toplevel().wl_surface().panel_item.lock().upgrade() + } + + fn surface_from_id(&self, id: &SurfaceId) -> Option> { match id { SurfaceId::Toplevel(_) => Some(self.toplevel().wl_surface().clone()), - SurfaceId::Child(_) => None, + SurfaceId::Child(id) => self.children.get(id).as_deref().and_then(|c| c.0.upgrade()), } } -} + pub fn add_child(&self, surface: &Arc, info: ChildInfo) { + let Some(SurfaceId::Child(id)) = surface.surface_id.get().cloned() else { + return; + }; + self.children + .insert(id, (Arc::downgrade(surface), info.clone())); + + let Some(panel_item) = self.panel_item() else { + tracing::error!("Couldn't find panel item in add_child"); + return; + }; + panel_item.create_child(id, &info); + } + + pub fn reposition_child(&self, surface: &Arc, geometry: Geometry) { + let Some(SurfaceId::Child(id)) = surface.surface_id.get() else { + return; + }; + + if let Some(mut child) = self.children.get_mut(&id) { + child.1.geometry = geometry; + } + let Some(panel_item) = self.panel_item() else { + tracing::error!("Couldn't find panel item in reposition_child"); + return; + }; + panel_item.reposition_child(*id, &geometry); + } + + pub fn remove_child(&self, surface: &Surface) { + let Some(SurfaceId::Child(id)) = surface.surface_id.get() else { + return; + }; + self.children.remove(id); + + let Some(panel_item) = self.panel_item() else { + tracing::error!("Couldn't find panel item in remove_child"); + return; + }; + panel_item.destroy_child(*id); + } +} impl Backend for XdgBackend { fn start_data(&self) -> Result { let surface_state = self.toplevel().wl_surface().current_state(); @@ -76,7 +129,7 @@ impl Backend for XdgBackend { fn apply_cursor_material(&self, _model_part: &Arc) {} fn apply_surface_material(&self, surface: SurfaceId, model_part: &Arc) { - if let Some(surface) = self.surface_from_id(surface) { + if let Some(surface) = self.surface_from_id(&surface) { surface.apply_material(model_part); } } @@ -123,30 +176,29 @@ impl Backend for XdgBackend { } fn pointer_motion(&self, surface: &SurfaceId, position: Vector2) { - if let Some(surface) = self.surface_from_id(surface.clone()) { + if let Some(surface) = self.surface_from_id(surface) { let _ = self .toplevel() .wl_surface() .message_sink - .send(Message::Seat( - crate::wayland::core::seat::SeatMessage::PointerMotion { surface, position }, - )); + .send(Message::Seat(SeatMessage::PointerMotion { + surface, + position, + })); } } fn pointer_button(&self, surface: &SurfaceId, button: u32, pressed: bool) { - if let Some(surface) = self.surface_from_id(surface.clone()) { + if let Some(surface) = self.surface_from_id(surface) { let _ = self .toplevel() .wl_surface() .message_sink - .send(Message::Seat( - crate::wayland::core::seat::SeatMessage::PointerButton { - surface, - button, - pressed, - }, - )); + .send(Message::Seat(SeatMessage::PointerButton { + surface, + button, + pressed, + })); } } @@ -156,18 +208,16 @@ impl Backend for XdgBackend { scroll_distance: Option>, scroll_steps: Option>, ) { - if let Some(surface) = self.surface_from_id(surface.clone()) { + if let Some(surface) = self.surface_from_id(surface) { let _ = self .toplevel() .wl_surface() .message_sink - .send(Message::Seat( - crate::wayland::core::seat::SeatMessage::PointerScroll { - surface, - scroll_distance, - scroll_steps, - }, - )); + .send(Message::Seat(SeatMessage::PointerScroll { + surface, + scroll_distance, + scroll_steps, + })); } } @@ -177,19 +227,17 @@ impl Backend for XdgBackend { key, if pressed { "pressed" } else { "released" } ); - if let Some(surface) = self.surface_from_id(surface.clone()) { + if let Some(surface) = self.surface_from_id(surface) { let _ = self .toplevel() .wl_surface() .message_sink - .send(Message::Seat( - crate::wayland::core::seat::SeatMessage::KeyboardKey { - surface, - keymap_id, - key, - pressed, - }, - )); + .send(Message::Seat(SeatMessage::KeyboardKey { + surface, + keymap_id, + key, + pressed, + })); } } @@ -200,18 +248,16 @@ impl Backend for XdgBackend { position.x, position.y ); - if let Some(surface) = self.surface_from_id(surface.clone()) { + if let Some(surface) = self.surface_from_id(surface) { let _ = self .toplevel() .wl_surface() .message_sink - .send(Message::Seat( - crate::wayland::core::seat::SeatMessage::TouchDown { - surface, - id, - position, - }, - )); + .send(Message::Seat(SeatMessage::TouchDown { + surface, + id, + position, + })); } } @@ -223,24 +269,27 @@ impl Backend for XdgBackend { position.y ); let toplevel = self.toplevel(); - let _ = toplevel.wl_surface().message_sink.send(Message::Seat( - crate::wayland::core::seat::SeatMessage::TouchMove { id, position }, - )); + let _ = toplevel + .wl_surface() + .message_sink + .send(Message::Seat(SeatMessage::TouchMove { id, position })); } fn touch_up(&self, id: u32) { tracing::debug!("Backend: Touch up {}", id); let toplevel = self.toplevel(); - let _ = toplevel.wl_surface().message_sink.send(Message::Seat( - crate::wayland::core::seat::SeatMessage::TouchUp { id }, - )); + let _ = toplevel + .wl_surface() + .message_sink + .send(Message::Seat(SeatMessage::TouchUp { id })); } fn reset_input(&self) { tracing::debug!("Backend: Reset input"); let toplevel = self.toplevel(); - let _ = toplevel.wl_surface().message_sink.send(Message::Seat( - crate::wayland::core::seat::SeatMessage::Reset, - )); + let _ = toplevel + .wl_surface() + .message_sink + .send(Message::Seat(SeatMessage::Reset)); } } diff --git a/src/wayland/xdg/popup.rs b/src/wayland/xdg/popup.rs index 7fade2a..773001e 100644 --- a/src/wayland/xdg/popup.rs +++ b/src/wayland/xdg/popup.rs @@ -1,15 +1,17 @@ use super::{ - backend::XdgBackend, positioner::{Positioner, PositionerData}, surface::Surface, }; use crate::{ - nodes::items::panel::{Geometry, PanelItem, SurfaceId}, + nodes::items::panel::{Geometry, SurfaceId}, wayland::util::DoubleBuffer, }; use parking_lot::Mutex; use rand::Rng; -use std::sync::{Arc, Weak, atomic::AtomicBool}; +use std::sync::{ + Arc, + atomic::{AtomicBool, Ordering}, +}; use waynest::{ server::{Client, Dispatcher, Result, protocol::stable::xdg_shell::xdg_popup::XdgPopup}, wire::ObjectId, @@ -20,8 +22,7 @@ pub struct Popup { id: ObjectId, version: u32, parent: Arc, - surface: Arc, - pub panel_item: Weak>, + pub surface: Arc, positioner_data: Mutex, geometry: DoubleBuffer, mapped: AtomicBool, @@ -31,7 +32,6 @@ impl Popup { id: ObjectId, version: u32, parent: Arc, - panel_item: &Arc>, surface: Arc, positioner: &Positioner, ) -> Self { @@ -46,7 +46,6 @@ impl Popup { version, parent, surface, - panel_item: Arc::downgrade(panel_item), positioner_data: Mutex::new(positioner_data), geometry: DoubleBuffer::new(positioner_data.infinite_geometry()), mapped: AtomicBool::new(false), @@ -90,6 +89,13 @@ impl XdgPopup for Popup { ) .await?; self.surface.reconfigure(client).await?; + + let Some(panel_item) = self.surface.wl_surface.panel_item.lock().upgrade() else { + return Ok(()); + }; + panel_item + .backend + .reposition_child(&self.surface.wl_surface, geometry); Ok(()) } @@ -98,3 +104,14 @@ impl XdgPopup for Popup { Ok(()) } } +impl Drop for Popup { + fn drop(&mut self) { + if !self.mapped.load(Ordering::Relaxed) { + return; + } + let Some(panel_item) = self.surface.wl_surface.panel_item.lock().upgrade() else { + return; + }; + panel_item.backend.remove_child(&self.surface.wl_surface); + } +} diff --git a/src/wayland/xdg/surface.rs b/src/wayland/xdg/surface.rs index 1d1354d..5ddc3c7 100644 --- a/src/wayland/xdg/surface.rs +++ b/src/wayland/xdg/surface.rs @@ -1,8 +1,8 @@ use super::{popup::Popup, positioner::Positioner, toplevel::MappedInner}; +use crate::nodes::items::panel::{ChildInfo, SurfaceId}; use crate::wayland::util::ClientExt; use crate::wayland::{core::surface::SurfaceRole, display::Display, xdg::toplevel::Toplevel}; use std::sync::Arc; -use waynest::server; pub use waynest::server::protocol::stable::xdg_shell::xdg_surface::*; use waynest::{ server::{Client, Dispatcher, Result}, @@ -81,23 +81,19 @@ impl XdgSurface for Surface { let toplevel_weak = Arc::downgrade(&toplevel); let pid = client.get::(ObjectId::DISPLAY).unwrap().pid; let configured = self.configured.clone(); - self.wl_surface.add_commit_handler(move |_surface, state| { + self.wl_surface.add_commit_handler(move |surface, state| { let Some(toplevel) = toplevel_weak.upgrade() else { return true; }; - // Only proceed if configured and has valid buffer - let has_valid_buffer = state - .buffer - .as_ref() - .is_some_and(|b| b.buffer.size().x > 0 && b.buffer.size().y > 0); - let mut mapped_lock = toplevel.mapped.lock(); if mapped_lock.is_none() && configured.load(std::sync::atomic::Ordering::SeqCst) - && has_valid_buffer + && state.has_valid_buffer() { - mapped_lock.replace(MappedInner::create(toplevel.clone(), pid)); + let mapped_inner = MappedInner::create(toplevel.clone(), pid); + *surface.panel_item.lock() = Arc::downgrade(&mapped_inner.panel_item); + mapped_lock.replace(mapped_inner); return false; } true @@ -152,53 +148,52 @@ impl XdgSurface for Surface { ) .await; }; - let Some(panel_item) = parent.wl_surface.panel_item.lock().upgrade() else { - return Err(server::Error::Custom( - "Parent surface does not have a panel item".to_string(), - )); - }; + *self.wl_surface.panel_item.lock() = parent.wl_surface.panel_item.lock().clone(); let positioner = client.get::(positioner).unwrap(); let surface = client.get::(self.id).unwrap(); let popup = client.insert( popup_id, - Popup::new( - popup_id, - self.version, - parent, - &panel_item, - surface, - &positioner, - ), + Popup::new(popup_id, self.version, parent.clone(), surface, &positioner), ); let serial = client.next_event_serial(); self.configure(client, sender_id, serial).await?; - // let pid = client.get::(ObjectId::DISPLAY).unwrap().pid; - // let configured = self.configured.clone(); - // surface.add_commit_handler(move |surface, state| { - // let Some(SurfaceRole::XDGPopup(popup)) = &mut *surface.role.lock() else { - // return true; - // }; + let Some(SurfaceId::Child(id)) = self.wl_surface.surface_id.get() else { + return Ok(()); + }; + let Some(parent_id) = parent.wl_surface.surface_id.get() else { + return Ok(()); + }; - // // Only proceed if configured and has valid buffer - // let has_valid_buffer = state - // .buffer - // .as_ref() - // .is_some_and(|b| b.buffer.size().x > 0 && b.buffer.size().y > 0); + let child_info = ChildInfo { + id: *id, + parent: parent_id.clone(), + geometry: positioner.data().infinite_geometry(), + z_order: 1, + receives_input: true, + }; - // let mut mapped_lock = popup.mapped.lock(); - // if mapped_lock.is_none() - // && configured.load(std::sync::atomic::Ordering::SeqCst) - // && has_valid_buffer - // { - // mapped_lock.replace(Mapped::create(popup.clone(), pid)); - // return false; - // } - // true - // }); + let popup_weak = Arc::downgrade(&popup); + let configured = self.configured.clone(); + self.wl_surface.add_commit_handler(move |surface, state| { + let Some(popup) = popup_weak.upgrade() else { + return true; + }; + let Some(panel_item) = surface.panel_item.lock().upgrade() else { + return true; + }; + + if configured.load(std::sync::atomic::Ordering::SeqCst) && state.has_valid_buffer() { + panel_item + .backend + .add_child(&popup.surface.wl_surface, child_info.clone()); + return false; + } + true + }); Ok(()) } diff --git a/src/wayland/xdg/toplevel.rs b/src/wayland/xdg/toplevel.rs index c1a81a2..687e486 100644 --- a/src/wayland/xdg/toplevel.rs +++ b/src/wayland/xdg/toplevel.rs @@ -18,16 +18,16 @@ use waynest::{ #[derive(Debug)] pub struct MappedInner { pub panel_item_node: Arc, - pub _panel_item: Arc>, + pub panel_item: Arc>, } impl MappedInner { pub fn create(toplevel: Arc, pid: Option) -> Self { - let (panel_item_node, _panel_item) = + let (panel_item_node, panel_item) = PanelItem::create(Box::new(XdgBackend::new(toplevel)), pid); Self { panel_item_node, - _panel_item, + panel_item, } } }