refactor(wayland): remove surface specialization from roles

This commit is contained in:
Nova
2025-09-06 21:16:55 -07:00
parent 50e1921cd9
commit a0b014576e
3 changed files with 67 additions and 163 deletions

View File

@@ -4,14 +4,14 @@ use crate::{
core::registry::Registry, core::registry::Registry,
nodes::{ nodes::{
drawable::model::ModelPart, drawable::model::ModelPart,
items::panel::{Geometry, SurfaceId}, items::panel::{Geometry, PanelItem, SurfaceId},
}, },
wayland::{ wayland::{
Message, MessageSink, Message, MessageSink,
core::buffer::BufferUsage, core::buffer::BufferUsage,
presentation::{MonotonicTimestamp, PresentationFeedback}, presentation::{MonotonicTimestamp, PresentationFeedback},
util::{ClientExt, DoubleBuffer}, util::{ClientExt, DoubleBuffer},
xdg::wm_base::XdgSurfaceRole, xdg::backend::XdgBackend,
}, },
}; };
use bevy::{ use bevy::{
@@ -22,7 +22,7 @@ use bevy::{
use bevy_dmabuf::import::ImportedDmatexs; use bevy_dmabuf::import::ImportedDmatexs;
use mint::Vector2; use mint::Vector2;
use parking_lot::Mutex; use parking_lot::Mutex;
use std::sync::{Arc, OnceLock}; use std::sync::{Arc, OnceLock, Weak};
use waynest::{ use waynest::{
server::{ server::{
Client, Dispatcher, Result, Client, Dispatcher, Result,
@@ -40,7 +40,8 @@ pub static WL_SURFACE_REGISTRY: Registry<Surface> = Registry::new();
pub enum SurfaceRole { pub enum SurfaceRole {
Cursor, Cursor,
Subsurface, Subsurface,
Xdg(OnceLock<XdgSurfaceRole>), XdgToplevel,
XdgPopup,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@@ -80,6 +81,7 @@ pub struct Surface {
state: Mutex<DoubleBuffer<SurfaceState>>, state: Mutex<DoubleBuffer<SurfaceState>>,
pub message_sink: MessageSink, pub message_sink: MessageSink,
pub role: OnceLock<SurfaceRole>, pub role: OnceLock<SurfaceRole>,
pub panel_item: Mutex<Weak<PanelItem<XdgBackend>>>,
on_commit_handlers: Mutex<Vec<OnCommitCallback>>, on_commit_handlers: Mutex<Vec<OnCommitCallback>>,
material: OnceLock<Handle<BevyMaterial>>, material: OnceLock<Handle<BevyMaterial>>,
pending_material_applications: Registry<ModelPart>, pending_material_applications: Registry<ModelPart>,
@@ -111,6 +113,7 @@ impl Surface {
state: Default::default(), state: Default::default(),
message_sink: client.message_sink(), message_sink: client.message_sink(),
role: OnceLock::new(), role: OnceLock::new(),
panel_item: Mutex::new(Weak::default()),
on_commit_handlers: Mutex::new(Vec::new()), on_commit_handlers: Mutex::new(Vec::new()),
material: OnceLock::new(), material: OnceLock::new(),
pending_material_applications: Registry::new(), pending_material_applications: Registry::new(),

View File

@@ -1,11 +1,8 @@
use super::{popup::Popup, positioner::Positioner, toplevel::MappedInner}; use super::{popup::Popup, positioner::Positioner, toplevel::MappedInner};
use crate::wayland::util::ClientExt; use crate::wayland::util::ClientExt;
use crate::wayland::{ use crate::wayland::{core::surface::SurfaceRole, display::Display, xdg::toplevel::Toplevel};
core::surface::SurfaceRole, use std::sync::Arc;
display::Display, use waynest::server;
xdg::{toplevel::Toplevel, wm_base::XdgSurfaceRole},
};
use std::sync::{Arc, OnceLock};
pub use waynest::server::protocol::stable::xdg_shell::xdg_surface::*; pub use waynest::server::protocol::stable::xdg_shell::xdg_surface::*;
use waynest::{ use waynest::{
server::{Client, Dispatcher, Result}, server::{Client, Dispatcher, Result},
@@ -62,62 +59,30 @@ impl XdgSurface for Surface {
); );
// Check if the surface already has an XDG role // Check if the surface already has an XDG role
let surface_role = self.wl_surface.role.get().unwrap(); match self.wl_surface.role.get() {
Some(SurfaceRole::XdgToplevel) => (),
// Now check if this is an XDG surface and set the sub-role None => {
if let SurfaceRole::Xdg(role) = surface_role { let _ = self.wl_surface.role.set(SurfaceRole::XdgToplevel);
if role.get().is_some() { }
_ => {
return client return client
.protocol_error( .protocol_error(
sender_id, sender_id,
toplevel_id, toplevel_id,
1, // XDG_WM_BASE_ERROR_ROLE 1, // XDG_WM_BASE_ERROR_ROLE
"XDG surface already has a sub-role".to_string(), "Surface has a non-XDG role".to_string(),
) )
.await; .await;
} }
if role
.set(XdgSurfaceRole::Toplevel(Arc::downgrade(&toplevel)))
.is_err()
{
return client
.protocol_error(
sender_id,
toplevel_id,
1, // XDG_WM_BASE_ERROR_ROLE
"Failed to set XDG sub-role (race condition)".to_string(),
)
.await;
}
} else {
return client
.protocol_error(
sender_id,
toplevel_id,
1, // XDG_WM_BASE_ERROR_ROLE
"Surface has a non-XDG role".to_string(),
)
.await;
} }
toplevel.reconfigure(client).await?; toplevel.reconfigure(client).await?;
let toplevel_weak = Arc::downgrade(&toplevel);
let pid = client.get::<Display>(ObjectId::DISPLAY).unwrap().pid; let pid = client.get::<Display>(ObjectId::DISPLAY).unwrap().pid;
let configured = self.configured.clone(); 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(role_ref) = surface.role.get() else { let Some(toplevel) = toplevel_weak.upgrade() else {
return true;
};
let SurfaceRole::Xdg(role) = role_ref else {
return true;
};
let Some(XdgSurfaceRole::Toplevel(toplevel)) = role.get() else {
return true;
};
let Some(toplevel) = toplevel.upgrade() else {
return true; return true;
}; };
@@ -150,6 +115,23 @@ impl XdgSurface for Surface {
parent: Option<ObjectId>, parent: Option<ObjectId>,
positioner: ObjectId, positioner: ObjectId,
) -> Result<()> { ) -> Result<()> {
match self.wl_surface.role.get() {
Some(SurfaceRole::XdgPopup) => (),
None => {
let _ = self.wl_surface.role.set(SurfaceRole::XdgPopup);
}
_ => {
return client
.protocol_error(
sender_id,
popup_id,
1, // XDG_WM_BASE_ERROR_ROLE
"Surface has an incomparible role".to_string(),
)
.await;
}
}
let Some(parent) = parent else { let Some(parent) = parent else {
return client return client
.protocol_error( .protocol_error(
@@ -160,49 +142,20 @@ impl XdgSurface for Surface {
) )
.await; .await;
}; };
let parent = client.get::<Surface>(parent).unwrap(); let Some(parent) = client.get::<Surface>(parent) else {
let panel_item = match parent.wl_surface.role.get().unwrap() { return client
SurfaceRole::Xdg(role) => match role.get().unwrap() { .protocol_error(
XdgSurfaceRole::Toplevel(toplevel) => { sender_id,
if let Some(toplevel) = toplevel.upgrade() { popup_id,
let toplevel_lock = toplevel.mapped.lock(); 3, // INVALID_POPUP_PARENT
toplevel_lock.as_ref().unwrap()._panel_item.clone() "Parent surface does not exist".to_string(),
} else { )
return client .await;
.protocol_error( };
sender_id, let Some(panel_item) = parent.wl_surface.panel_item.lock().upgrade() else {
popup_id, return Err(server::Error::Custom(
3, // INVALID_POPUP_PARENT "Parent surface does not have a panel item".to_string(),
"Parent surface does not have an XDG role".to_string(), ));
)
.await;
}
}
XdgSurfaceRole::Popup(popup) => {
if let Some(popup) = popup.upgrade() {
popup.panel_item.upgrade().unwrap()
} else {
return client
.protocol_error(
sender_id,
popup_id,
3, // INVALID_POPUP_PARENT
"Parent surface does not have an XDG role".to_string(),
)
.await;
}
}
},
_ => {
return client
.protocol_error(
sender_id,
popup_id,
1, // XDG_WM_BASE_ERROR_ROLE
"Parent surface does not have an XDG role".to_string(),
)
.await;
}
}; };
let positioner = client.get::<Positioner>(positioner).unwrap(); let positioner = client.get::<Positioner>(positioner).unwrap();
@@ -220,62 +173,6 @@ impl XdgSurface for Surface {
), ),
); );
// Set up the XDG role if not already done
if self.wl_surface.role.get().is_none() {
let xdg_role = SurfaceRole::Xdg(OnceLock::new());
if self.wl_surface.role.set(xdg_role).is_err() {
return client
.protocol_error(
sender_id,
popup_id,
1, // XDG_WM_BASE_ERROR_ROLE
"Failed to set surface role (race condition)".to_string(),
)
.await;
}
}
// Now check if this is an XDG surface and set the sub-role
match self.wl_surface.role.get().unwrap() {
SurfaceRole::Xdg(role) => {
if role.get().is_some() {
return client
.protocol_error(
sender_id,
popup_id,
1, // XDG_WM_BASE_ERROR_ROLE
"XDG surface already has a sub-role".to_string(),
)
.await;
}
if role
.set(XdgSurfaceRole::Popup(Arc::downgrade(&popup)))
.is_err()
{
return client
.protocol_error(
sender_id,
popup_id,
1, // XDG_WM_BASE_ERROR_ROLE
"Failed to set XDG sub-role (race condition)".to_string(),
)
.await;
}
}
_ => {
return client
.protocol_error(
sender_id,
popup_id,
1, // XDG_WM_BASE_ERROR_ROLE
"Surface has a non-XDG role".to_string(),
)
.await;
}
}
let serial = client.next_event_serial(); let serial = client.next_event_serial();
self.configure(client, sender_id, serial).await?; self.configure(client, sender_id, serial).await?;

View File

@@ -1,20 +1,11 @@
use super::popup::Popup;
use super::positioner::Positioner; use super::positioner::Positioner;
use super::toplevel::Toplevel; use crate::wayland::{core::surface::SurfaceRole, util::ClientExt, xdg::surface::Surface};
use crate::wayland::{core::surface::SurfaceRole, xdg::surface::Surface};
use std::sync::{OnceLock, Weak};
pub use waynest::server::protocol::stable::xdg_shell::xdg_wm_base::*; pub use waynest::server::protocol::stable::xdg_shell::xdg_wm_base::*;
use waynest::{ use waynest::{
server::{Client, Dispatcher, Result}, server::{Client, Dispatcher, Result},
wire::ObjectId, wire::ObjectId,
}; };
#[derive(Debug, Clone)]
pub enum XdgSurfaceRole {
Toplevel(Weak<Toplevel>),
Popup(Weak<Popup>),
}
#[derive(Debug, Dispatcher, Default)] #[derive(Debug, Dispatcher, Default)]
pub struct WmBase { pub struct WmBase {
pub version: u32, pub version: u32,
@@ -37,7 +28,7 @@ impl XdgWmBase for WmBase {
async fn get_xdg_surface( async fn get_xdg_surface(
&self, &self,
client: &mut Client, client: &mut Client,
_sender_id: ObjectId, sender_id: ObjectId,
xdg_surface_id: ObjectId, xdg_surface_id: ObjectId,
wl_surface_id: ObjectId, wl_surface_id: ObjectId,
) -> Result<()> { ) -> Result<()> {
@@ -46,7 +37,20 @@ impl XdgWmBase for WmBase {
.ok_or(waynest::server::Error::Custom( .ok_or(waynest::server::Error::Custom(
"can't get wayland surface id".to_string(), "can't get wayland surface id".to_string(),
))?; ))?;
let _ = wl_surface.role.set(SurfaceRole::Xdg(OnceLock::new())); match wl_surface.role.get() {
Some(SurfaceRole::XdgToplevel | SurfaceRole::XdgPopup) => (),
None => (),
_ => {
client
.protocol_error(
sender_id,
xdg_surface_id,
0,
"invalid surface role".to_string(),
)
.await?
}
};
let xdg_surface = Surface::new(xdg_surface_id, self.version, wl_surface); let xdg_surface = Surface::new(xdg_surface_id, self.version, wl_surface);
client.insert(xdg_surface_id, xdg_surface); client.insert(xdg_surface_id, xdg_surface);