WIP: Handle xdg shell popups as backend child surfaces #48
@@ -1,28 +1,30 @@
|
||||
use super::toplevel::Toplevel;
|
||||
use crate::{
|
||||
core::error::Result,
|
||||
nodes::{
|
||||
drawable::model::ModelPart,
|
||||
items::panel::{Backend, Geometry, PanelItemInitData, SurfaceId, ToplevelInfo},
|
||||
},
|
||||
wayland::{Message, core::surface::Surface},
|
||||
core::error::Result,
|
||||
nodes::{
|
||||
drawable::model::ModelPart,
|
||||
items::panel::{Backend, Geometry, PanelItemInitData, SurfaceId, ToplevelInfo},
|
||||
},
|
||||
wayland::{Message, core::surface::Surface},
|
||||
};
|
||||
use mint::Vector2;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Weak;
|
||||
use parking_lot::Mutex;
|
||||
use std::{collections::HashMap, sync::{Arc, Weak}};
|
||||
use tracing;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct XdgBackend {
|
||||
toplevel: Weak<Toplevel>,
|
||||
toplevel: Weak<Toplevel>,
|
||||
children: Mutex<HashMap<u64, Weak<Surface>>>,
|
||||
}
|
||||
|
||||
impl XdgBackend {
|
||||
pub fn new(toplevel: Arc<Toplevel>) -> Self {
|
||||
Self {
|
||||
toplevel: Arc::downgrade(&toplevel),
|
||||
}
|
||||
}
|
||||
pub fn new(toplevel: Arc<Toplevel>) -> Self {
|
||||
Self {
|
||||
toplevel: Arc::downgrade(&toplevel),
|
||||
children: Mutex::new(HashMap::new()),
|
||||
}
|
||||
}
|
||||
|
||||
// Since XdgBackend is created and owned by Mapped which is owned by Toplevel,
|
||||
// we can safely assume the Toplevel reference will always be valid
|
||||
@@ -32,12 +34,20 @@ impl XdgBackend {
|
||||
.expect("Toplevel should always be valid while XdgBackend exists")
|
||||
}
|
||||
|
||||
fn surface_from_id(&self, id: SurfaceId) -> Option<Arc<Surface>> {
|
||||
match id {
|
||||
SurfaceId::Toplevel(_) => Some(self.toplevel().surface()),
|
||||
SurfaceId::Child(_) => None,
|
||||
}
|
||||
}
|
||||
fn surface_from_id(&self, id: SurfaceId) -> Option<Arc<Surface>> {
|
||||
match id {
|
||||
SurfaceId::Toplevel(_) => Some(self.toplevel().surface()),
|
||||
SurfaceId::Child(uid) => self.children.lock().get(&uid).and_then(Weak::upgrade),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register_child(&self, id: u64, surface: &Arc<Surface>) {
|
||||
self.children.lock().insert(id, Arc::downgrade(surface));
|
||||
}
|
||||
|
||||
pub fn unregister_child(&self, id: u64) {
|
||||
self.children.lock().remove(&id);
|
||||
}
|
||||
}
|
||||
|
||||
impl Backend for XdgBackend {
|
||||
|
||||
@@ -4,8 +4,8 @@ use super::{
|
||||
surface::Surface,
|
||||
};
|
||||
use crate::{
|
||||
nodes::items::panel::{Geometry, PanelItem, SurfaceId},
|
||||
wayland::util::DoubleBuffer,
|
||||
nodes::items::panel::{ChildInfo, Geometry, PanelItem, SurfaceId},
|
||||
wayland::util::DoubleBuffer,
|
||||
};
|
||||
use parking_lot::Mutex;
|
||||
use rand::Rng;
|
||||
@@ -25,10 +25,10 @@ pub struct Popup {
|
||||
surface_id: SurfaceId,
|
||||
parent: Arc<Surface>,
|
||||
surface: Weak<Surface>,
|
||||
pub panel_item: Weak<PanelItem<XdgBackend>>,
|
||||
positioner_data: Mutex<PositionerData>,
|
||||
geometry: DoubleBuffer<Geometry>,
|
||||
mapped: AtomicBool,
|
||||
pub panel_item: Weak<PanelItem<XdgBackend>>,
|
||||
positioner_data: Mutex<PositionerData>,
|
||||
geometry: Mutex<DoubleBuffer<Geometry>>,
|
||||
mapped: AtomicBool,
|
||||
}
|
||||
impl Popup {
|
||||
pub fn new(
|
||||
@@ -46,12 +46,83 @@ impl Popup {
|
||||
surface_id: SurfaceId::Child(rand::thread_rng().gen_range(0..u64::MAX)),
|
||||
parent,
|
||||
surface: Arc::downgrade(xdg_surface),
|
||||
panel_item: Arc::downgrade(panel_item),
|
||||
positioner_data: Mutex::new(positioner_data),
|
||||
geometry: DoubleBuffer::new(positioner_data.infinite_geometry()),
|
||||
mapped: AtomicBool::new(false),
|
||||
}
|
||||
}
|
||||
panel_item: Arc::downgrade(panel_item),
|
||||
positioner_data: Mutex::new(positioner_data),
|
||||
geometry: Mutex::new(DoubleBuffer::new(positioner_data.infinite_geometry())),
|
||||
mapped: AtomicBool::new(false),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Popup {
|
||||
fn id(&self) -> u64 {
|
||||
match self.surface_id {
|
||||
SurfaceId::Child(id) => id,
|
||||
SurfaceId::Toplevel(_) => 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn surface_id(&self) -> SurfaceId { self.surface_id.clone() }
|
||||
|
||||
pub fn current_geometry(&self) -> Geometry {
|
||||
*self.geometry.lock().current()
|
||||
}
|
||||
|
||||
pub fn is_mapped(&self) -> bool {
|
||||
self.mapped.load(std::sync::atomic::Ordering::SeqCst)
|
||||
}
|
||||
|
||||
pub fn map(&self) {
|
||||
if self.is_mapped() {
|
||||
return;
|
||||
}
|
||||
|
||||
let Some(panel_item) = self.panel_item.upgrade() else { return; };
|
||||
let xdg_surface = match self.surface.upgrade() { Some(s) => s, None => return };
|
||||
let core_surface = xdg_surface.wl_surface();
|
||||
|
||||
// Determine parent surface id
|
||||
let parent_wl_surface = self.parent.wl_surface();
|
||||
let parent_role = parent_wl_surface.role.lock();
|
||||
let parent_id = match parent_role.as_ref().unwrap() {
|
||||
crate::wayland::core::surface::SurfaceRole::XdgToplevel(_) => {
|
||||
SurfaceId::Toplevel(())
|
||||
}
|
||||
crate::wayland::core::surface::SurfaceRole::XDGPopup(p) => p.surface_id(),
|
||||
};
|
||||
|
||||
let geometry = *self.geometry.lock().current();
|
||||
let info = ChildInfo {
|
||||
id: self.id(),
|
||||
parent: parent_id,
|
||||
geometry,
|
||||
z_order: 0,
|
||||
receives_input: true,
|
||||
};
|
||||
panel_item.create_child(self.id(), &info);
|
||||
panel_item.backend.register_child(self.id(), &core_surface);
|
||||
self.mapped.store(true, std::sync::atomic::Ordering::SeqCst);
|
||||
}
|
||||
|
||||
pub fn unmap(&self) {
|
||||
if !self.mapped.swap(false, std::sync::atomic::Ordering::SeqCst) {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(panel_item) = self.panel_item.upgrade() {
|
||||
panel_item.destroy_child(self.id());
|
||||
panel_item.backend.unregister_child(self.id());
|
||||
}
|
||||
}
|
||||
|
||||
fn reposition_child(&self) {
|
||||
if self.is_mapped() {
|
||||
if let Some(panel_item) = self.panel_item.upgrade() {
|
||||
let geometry = *self.geometry.lock().current();
|
||||
panel_item.reposition_child(self.id(), &geometry);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
impl XdgPopup for Popup {
|
||||
/// https://wayland.app/protocols/xdg-shell#xdg_popup:request:grab
|
||||
@@ -73,28 +144,35 @@ impl XdgPopup for Popup {
|
||||
positioner: ObjectId,
|
||||
token: u32,
|
||||
) -> Result<()> {
|
||||
let positioner = client.get::<Positioner>(positioner).unwrap();
|
||||
let positioner_data = positioner.data();
|
||||
*self.positioner_data.lock() = positioner_data;
|
||||
if self.version >= 5 {
|
||||
self.repositioned(client, sender_id, token).await?;
|
||||
}
|
||||
let geometry = positioner_data.infinite_geometry();
|
||||
self.configure(
|
||||
client,
|
||||
sender_id,
|
||||
geometry.origin.x,
|
||||
geometry.origin.y,
|
||||
geometry.size.x as i32,
|
||||
geometry.size.y as i32,
|
||||
)
|
||||
.await?;
|
||||
self.surface.upgrade().unwrap().reconfigure(client).await?;
|
||||
Ok(())
|
||||
}
|
||||
let positioner = client.get::<Positioner>(positioner).unwrap();
|
||||
let positioner_data = positioner.data();
|
||||
*self.positioner_data.lock() = positioner_data;
|
||||
if self.version >= 5 {
|
||||
self.repositioned(client, sender_id, token).await?;
|
||||
}
|
||||
let geometry = positioner_data.infinite_geometry();
|
||||
{
|
||||
let mut geo = self.geometry.lock();
|
||||
geo.pending = geometry;
|
||||
geo.apply();
|
||||
}
|
||||
self.configure(
|
||||
client,
|
||||
sender_id,
|
||||
geometry.origin.x,
|
||||
geometry.origin.y,
|
||||
geometry.size.x as i32,
|
||||
geometry.size.y as i32,
|
||||
)
|
||||
.await?;
|
||||
self.surface.upgrade().unwrap().reconfigure(client).await?;
|
||||
self.reposition_child();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// https://wayland.app/protocols/xdg-shell#xdg_popup:request:destroy
|
||||
async fn destroy(&self, _client: &mut Client, _sender_id: ObjectId) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
async fn destroy(&self, _client: &mut Client, _sender_id: ObjectId) -> Result<()> {
|
||||
self.unmap();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use super::{popup::Popup, positioner::Positioner, toplevel::Mapped};
|
||||
use crate::wayland::{core::surface::SurfaceRole, display::Display, xdg::toplevel::Toplevel};
|
||||
use waynest::server::protocol::stable::xdg_shell::xdg_popup::XdgPopup;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Weak;
|
||||
pub use waynest::server::protocol::stable::xdg_shell::xdg_surface::*;
|
||||
@@ -119,29 +120,43 @@ impl XdgSurface for Surface {
|
||||
parent: Option<ObjectId>,
|
||||
positioner: ObjectId,
|
||||
) -> Result<()> {
|
||||
let parent = client.get::<Surface>(parent.unwrap()).unwrap();
|
||||
let panel_item = match parent.wl_surface().role.lock().as_ref().unwrap() {
|
||||
SurfaceRole::XdgToplevel(toplevel) => {
|
||||
let toplevel_lock = toplevel.mapped.lock();
|
||||
toplevel_lock.as_ref().unwrap()._panel_item.clone()
|
||||
}
|
||||
SurfaceRole::XDGPopup(popup) => popup.panel_item.upgrade().unwrap(),
|
||||
};
|
||||
let positioner = client.get::<Positioner>(positioner).unwrap();
|
||||
let parent = client.get::<Surface>(parent.unwrap()).unwrap();
|
||||
let panel_item = match parent.wl_surface().role.lock().as_ref().unwrap() {
|
||||
SurfaceRole::XdgToplevel(toplevel) => {
|
||||
let toplevel_lock = toplevel.mapped.lock();
|
||||
toplevel_lock.as_ref().unwrap()._panel_item.clone()
|
||||
}
|
||||
SurfaceRole::XDGPopup(popup) => popup.panel_item.upgrade().unwrap(),
|
||||
};
|
||||
let positioner = client.get::<Positioner>(positioner).unwrap();
|
||||
|
||||
let surface = client.get::<Surface>(self.id).unwrap();
|
||||
let surface = client.get::<Surface>(self.id).unwrap();
|
||||
let wl_surface = surface.wl_surface();
|
||||
|
||||
let popup = client.insert(
|
||||
popup_id,
|
||||
Popup::new(
|
||||
popup_id,
|
||||
self.version,
|
||||
parent,
|
||||
&panel_item,
|
||||
&surface,
|
||||
&positioner,
|
||||
),
|
||||
);
|
||||
let popup = client.insert(
|
||||
popup_id,
|
||||
Popup::new(
|
||||
popup_id,
|
||||
self.version,
|
||||
parent,
|
||||
&panel_item,
|
||||
&surface,
|
||||
&positioner,
|
||||
),
|
||||
);
|
||||
|
||||
// Initial popup configure
|
||||
let geometry = popup.current_geometry();
|
||||
popup
|
||||
.configure(
|
||||
client,
|
||||
popup_id,
|
||||
geometry.origin.x,
|
||||
geometry.origin.y,
|
||||
geometry.size.x as i32,
|
||||
geometry.size.y as i32,
|
||||
)
|
||||
.await?;
|
||||
|
||||
{
|
||||
let wl_surface = self.wl_surface();
|
||||
@@ -155,8 +170,27 @@ impl XdgSurface for Surface {
|
||||
}
|
||||
}
|
||||
|
||||
let serial = client.next_event_serial();
|
||||
self.configure(client, sender_id, serial).await?;
|
||||
let serial = client.next_event_serial();
|
||||
self.configure(client, sender_id, serial).await?;
|
||||
|
||||
let configured = self.configured.clone();
|
||||
wl_surface.add_commit_handler(move |surface, state| {
|
||||
let Some(SurfaceRole::XDGPopup(popup)) = &*surface.role.lock() else {
|
||||
return true;
|
||||
};
|
||||
let has_valid_buffer = state
|
||||
.buffer
|
||||
.as_ref()
|
||||
.is_some_and(|b| b.buffer.size().x > 0 && b.buffer.size().y > 0);
|
||||
if !popup.is_mapped()
|
||||
&& configured.load(std::sync::atomic::Ordering::SeqCst)
|
||||
&& has_valid_buffer
|
||||
{
|
||||
popup.map();
|
||||
return false;
|
||||
}
|
||||
true
|
||||
});
|
||||
|
||||
// let pid = client.get::<Display>(ObjectId::DISPLAY).unwrap().pid;
|
||||
// let configured = self.configured.clone();
|
||||
|
||||
Reference in New Issue
Block a user