Skip to content

Commit dfbcc43

Browse files
committed
feat: subsurfaces
refactor(wayland): move some stuff around (broken) Signed-off-by: Schmarni <[email protected]> fix(wayland): don't do broken frame event stuff lol Signed-off-by: Schmarni <[email protected]> fix(wayland/keyboard): send the correct ids for keyboard leave event Signed-off-by: Schmarni <[email protected]>
1 parent 658df5c commit dfbcc43

File tree

12 files changed

+702
-88
lines changed

12 files changed

+702
-88
lines changed

src/wayland/core/compositor.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ impl WlCompositor for Compositor {
1919
_sender_id: ObjectId,
2020
id: ObjectId,
2121
) -> WaylandResult<()> {
22-
let surface = client.insert(id, Surface::new(client, id))?;
22+
let surface = Surface::new(client, id);
23+
client.insert_raw(id, surface.clone())?;
2324
if let Some(output) = client.display().output.get() {
2425
surface.enter(client, id, output.id).await?;
2526
}

src/wayland/core/keyboard.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ impl Keyboard {
163163
// Send leave to old surface if it exists and is still alive
164164
if let Some(old_surface) = focused.upgrade() {
165165
let serial = client.next_event_serial();
166-
self.leave(client, old_surface.id, serial, self.id).await?;
166+
self.leave(client, self.id, serial, old_surface.id).await?;
167167
// println!("Left surface {}", old_surface.id);
168168
}
169169

src/wayland/core/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,6 @@ pub mod seat;
99
pub mod shm;
1010
pub mod shm_buffer_backing;
1111
pub mod shm_pool;
12+
pub mod subcompositor;
1213
pub mod surface;
1314
pub mod touch;

src/wayland/core/pointer.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -237,11 +237,7 @@ impl WlPointer for Pointer {
237237
&& let Some(panel_item) = focused_surface.panel_item.lock().upgrade()
238238
{
239239
panel_item.set_cursor(surface.and_then(|s| client.get::<Surface>(s)).map(|s| {
240-
let size = s
241-
.current_state()
242-
.buffer
243-
.map(|b| b.buffer.size())
244-
.unwrap_or([16; 2].into());
240+
let size = s.current_buffer_size().unwrap_or([16; 2].into());
245241
Geometry {
246242
origin: [hotspot_x, hotspot_y].into(),
247243
size: [size.x as u32, size.y as u32].into(),

src/wayland/core/subcompositor.rs

Lines changed: 352 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,352 @@
1+
use super::surface::{Surface, SurfaceRole};
2+
use crate::core::Id;
3+
use crate::nodes::items::panel::{ChildInfo, Geometry, SurfaceId};
4+
use crate::wayland::util::{BufferedState, SurfaceCommitAwareBuffer};
5+
use crate::wayland::{WaylandError, WaylandResult};
6+
use mint::Vector2;
7+
use parking_lot::Mutex;
8+
use rand::Rng;
9+
use std::sync::Arc;
10+
use std::sync::atomic::{AtomicBool, Ordering};
11+
use waynest::ObjectId;
12+
use waynest_protocols::server::core::wayland::wl_subcompositor::{self, WlSubcompositor};
13+
use waynest_protocols::server::core::wayland::wl_subsurface::WlSubsurface;
14+
use waynest_server::{Client as _, RequestDispatcher};
15+
16+
#[derive(Debug, waynest_server::RequestDispatcher)]
17+
#[waynest(error = WaylandError, connection = crate::wayland::Client)]
18+
pub struct Subcompositor;
19+
20+
impl WlSubcompositor for Subcompositor {
21+
type Connection = crate::wayland::Client;
22+
23+
/// https://wayland.app/protocols/wayland#wl_subcompositor:request:destroy
24+
async fn destroy(
25+
&self,
26+
client: &mut Self::Connection,
27+
sender_id: ObjectId,
28+
) -> WaylandResult<()> {
29+
client.remove(sender_id);
30+
Ok(())
31+
}
32+
33+
/// https://wayland.app/protocols/wayland#wl_subcompositor:request:get_subsurface
34+
async fn get_subsurface(
35+
&self,
36+
client: &mut Self::Connection,
37+
_sender_id: ObjectId,
38+
id: ObjectId,
39+
surface_id: ObjectId,
40+
parent_id: ObjectId,
41+
) -> WaylandResult<()> {
42+
let Some(parent) = client.get::<Surface>(parent_id) else {
43+
return Err(WaylandError::Fatal {
44+
object_id: parent_id,
45+
code: wl_subcompositor::Error::BadSurface as u32,
46+
message: "Parent surface does not exist",
47+
});
48+
};
49+
50+
let Some(surface) = client.get::<Surface>(surface_id) else {
51+
return Err(WaylandError::Fatal {
52+
object_id: surface_id,
53+
code: wl_subcompositor::Error::BadSurface as u32,
54+
message: "Surface does not exist",
55+
});
56+
};
57+
58+
// Set the subsurface role
59+
surface
60+
.try_set_role(SurfaceRole::Subsurface, wl_subcompositor::Error::BadSurface)
61+
.await?;
62+
63+
// Create the subsurface
64+
let subsurface = Arc::new(Subsurface::new(id, surface.clone(), parent.clone()));
65+
client.insert_raw(id, subsurface.clone())?;
66+
67+
// Set up commit handler and register child
68+
subsurface.setup();
69+
70+
Ok(())
71+
}
72+
}
73+
74+
#[derive(Debug, Clone, Copy, PartialEq)]
75+
struct SubsurfaceState {
76+
position: (i32, i32),
77+
z_order: i32,
78+
}
79+
80+
impl Default for SubsurfaceState {
81+
fn default() -> Self {
82+
Self {
83+
position: (0, 0),
84+
z_order: 0, // Initially below parent (parent is 0)
85+
}
86+
}
87+
}
88+
impl BufferedState for SubsurfaceState {
89+
fn apply(&mut self, pending: &mut Self) {
90+
*self = pending.clone();
91+
}
92+
93+
fn get_initial_pending(&self) -> Self {
94+
self.clone()
95+
}
96+
}
97+
98+
#[derive(Debug, RequestDispatcher)]
99+
#[waynest(error = WaylandError, connection = crate::wayland::Client)]
100+
pub struct Subsurface {
101+
id: ObjectId,
102+
surface: Arc<Surface>,
103+
state: Arc<Mutex<SurfaceCommitAwareBuffer<SubsurfaceState>>>,
104+
child_id: Mutex<Option<Id>>,
105+
is_sync: AtomicBool,
106+
}
107+
108+
impl Subsurface {
109+
pub fn new(id: ObjectId, surface: Arc<Surface>, parent: Arc<Surface>) -> Self {
110+
let child_id = Id(rand::rng().random());
111+
let _ = surface.surface_id.set(SurfaceId::Child(child_id));
112+
surface.set_parent(&parent);
113+
114+
Self {
115+
id,
116+
state: SurfaceCommitAwareBuffer::new(SubsurfaceState::default(), &surface),
117+
surface,
118+
child_id: Mutex::new(Some(child_id)),
119+
is_sync: AtomicBool::new(true), // Subsurfaces start in sync mode
120+
}
121+
}
122+
123+
/// Check if this subsurface is effectively synchronized
124+
/// Per spec: "even if a sub-surface is set to desynchronized,
125+
/// a parent sub-surface may override it to behave as synchronized"
126+
fn is_effectively_sync(&self) -> bool {
127+
if !self.is_sync.load(Ordering::Acquire) {
128+
// We're desync, but check if parent is a synchronized subsurface
129+
if let Some(parent) = self.surface.parent() {
130+
if parent.role.get() == Some(&SurfaceRole::Subsurface) {
131+
// Parent is a subsurface - we inherit synchronized behavior
132+
// TODO: Could walk the chain recursively for perfect correctness
133+
return true;
134+
}
135+
}
136+
return false;
137+
}
138+
true
139+
}
140+
141+
fn setup(self: &Arc<Self>) {
142+
// Set up commit filter to control when surface state is applied
143+
let subsurface_weak = Arc::downgrade(self);
144+
self.surface.set_parent_syncronized_filter(move || {
145+
let Some(subsurface) = subsurface_weak.upgrade() else {
146+
return true; // Subsurface gone, allow commit
147+
};
148+
149+
subsurface.is_effectively_sync()
150+
});
151+
152+
// First commit: add child when buffer is ready
153+
let subsurface_weak = Arc::downgrade(self);
154+
self.surface
155+
.add_updated_current_state_handler(move |surface| {
156+
let Some(subsurface) = subsurface_weak.upgrade() else {
157+
return true;
158+
};
159+
let Some(parent) = subsurface.surface.parent() else {
160+
return true;
161+
};
162+
let Some(panel_item) = parent.panel_item.lock().upgrade() else {
163+
return true;
164+
};
165+
166+
if surface.currently_has_valid_buffer() {
167+
*surface.panel_item.lock() = Arc::downgrade(&panel_item);
168+
let info = subsurface.create_child_info(surface.current_buffer_size());
169+
panel_item.backend.add_child(&subsurface.surface, info);
170+
return false; // Remove handler after adding child once
171+
}
172+
true
173+
});
174+
let subsurface_weak = Arc::downgrade(self);
175+
self.surface.add_commit_handler(move |_| {
176+
let Some(subsurface) = subsurface_weak.upgrade() else {
177+
return true;
178+
};
179+
subsurface.state.lock().apply();
180+
true
181+
});
182+
// update subsurface geometry
183+
let subsurface_weak = Arc::downgrade(self);
184+
self.surface.add_updated_current_state_handler(move |_| {
185+
let Some(subsurface) = subsurface_weak.upgrade() else {
186+
return true;
187+
};
188+
let surface = subsurface.surface.clone();
189+
190+
if surface.currently_has_valid_buffer() {
191+
if let Some(panel_item) = surface.panel_item.lock().upgrade() {
192+
let state = subsurface.state.lock();
193+
let subsurface_state = *state.current();
194+
drop(state);
195+
let size = surface
196+
.current_buffer_size()
197+
.map(|b| [b.x as u32, b.y as u32].into())
198+
.unwrap_or([0; 2].into());
199+
200+
tracing::debug!("Updating backend after cached state apply: size={:?}", size);
201+
202+
let geometry = Geometry {
203+
origin: [subsurface_state.position.0, subsurface_state.position.1].into(),
204+
size,
205+
};
206+
panel_item.backend.reposition_child(&surface, geometry);
207+
panel_item
208+
.backend
209+
.update_child_z_order(&surface, subsurface_state.z_order);
210+
}
211+
}
212+
true
213+
});
214+
}
215+
216+
fn create_child_info(&self, buffer_size: Option<Vector2<usize>>) -> ChildInfo {
217+
let state = self.state.lock();
218+
219+
let size = buffer_size
220+
.map(|b| [b.x as u32, b.y as u32].into())
221+
.unwrap_or([0; 2].into());
222+
223+
// Determine parent surface ID
224+
let parent_surface_id = self
225+
.surface
226+
.parent()
227+
.and_then(|p| p.surface_id.get().cloned())
228+
.unwrap_or(SurfaceId::Toplevel(()));
229+
230+
ChildInfo {
231+
id: self.child_id.lock().unwrap(),
232+
parent: parent_surface_id,
233+
geometry: Geometry {
234+
origin: [state.current().position.0, state.current().position.1].into(),
235+
size,
236+
},
237+
z_order: state.current().z_order,
238+
receives_input: true,
239+
}
240+
}
241+
}
242+
243+
impl WlSubsurface for Subsurface {
244+
type Connection = crate::wayland::Client;
245+
246+
/// https://wayland.app/protocols/wayland#wl_subsurface:request:destroy
247+
async fn destroy(
248+
&self,
249+
client: &mut Self::Connection,
250+
_sender_id: ObjectId,
251+
) -> WaylandResult<()> {
252+
// Remove the child from the parent's backend
253+
if let Some(parent) = self.surface.parent() {
254+
let Some(panel_item) = parent.panel_item.lock().upgrade() else {
255+
client.remove(self.id);
256+
return Ok(());
257+
};
258+
panel_item.backend.remove_child(&self.surface);
259+
}
260+
261+
// Clear the commit filter
262+
self.surface.clear_parent_syncronized_filter();
263+
264+
client.remove(self.id);
265+
Ok(())
266+
}
267+
268+
/// https://wayland.app/protocols/wayland#wl_subsurface:request:set_position
269+
async fn set_position(
270+
&self,
271+
_client: &mut Self::Connection,
272+
_sender_id: ObjectId,
273+
x: i32,
274+
y: i32,
275+
) -> WaylandResult<()> {
276+
self.state.lock().pending.position = (x, y);
277+
Ok(())
278+
}
279+
280+
/// https://wayland.app/protocols/wayland#wl_subsurface:request:place_above
281+
async fn place_above(
282+
&self,
283+
client: &mut Self::Connection,
284+
_sender_id: ObjectId,
285+
sibling: ObjectId,
286+
) -> WaylandResult<()> {
287+
// Get the sibling's z_order
288+
let sibling_z_order = if let Some(sibling_surface) = client.get::<Surface>(sibling)
289+
&& let Some(SurfaceId::Child(sibling_id)) = sibling_surface.surface_id.get()
290+
&& let Some(parent) = self.surface.parent()
291+
&& let Some(panel_item) = parent.panel_item.lock().upgrade()
292+
&& let Some(child_entry) = panel_item.backend.children.get(sibling_id)
293+
{
294+
child_entry.1.z_order
295+
} else {
296+
0
297+
};
298+
299+
// Place this subsurface one level above the sibling
300+
self.state.lock().pending.z_order = sibling_z_order + 1;
301+
Ok(())
302+
}
303+
304+
/// https://wayland.app/protocols/wayland#wl_subsurface:request:place_below
305+
async fn place_below(
306+
&self,
307+
client: &mut Self::Connection,
308+
_sender_id: ObjectId,
309+
sibling: ObjectId,
310+
) -> WaylandResult<()> {
311+
// Get the sibling's z_order
312+
let sibling_z_order = if let Some(sibling_surface) = client.get::<Surface>(sibling)
313+
&& let Some(SurfaceId::Child(sibling_id)) = sibling_surface.surface_id.get()
314+
&& let Some(parent) = self.surface.parent()
315+
&& let Some(panel_item) = parent.panel_item.lock().upgrade()
316+
&& let Some(child_entry) = panel_item.backend.children.get(sibling_id)
317+
{
318+
child_entry.1.z_order
319+
} else {
320+
0
321+
};
322+
// Place this subsurface one level below the sibling
323+
self.state.lock().pending.z_order = sibling_z_order - 1;
324+
Ok(())
325+
}
326+
327+
/// https://wayland.app/protocols/wayland#wl_subsurface:request:set_sync
328+
async fn set_sync(
329+
&self,
330+
_client: &mut Self::Connection,
331+
_sender_id: ObjectId,
332+
) -> WaylandResult<()> {
333+
self.is_sync.store(true, Ordering::Release);
334+
Ok(())
335+
}
336+
337+
/// https://wayland.app/protocols/wayland#wl_subsurface:request:set_desync
338+
async fn set_desync(
339+
&self,
340+
_client: &mut Self::Connection,
341+
_sender_id: ObjectId,
342+
) -> WaylandResult<()> {
343+
let was_sync = self.is_sync.swap(false, Ordering::AcqRel);
344+
345+
if was_sync {
346+
// TODO: figure out if this should be recursive or only for this surface
347+
self.surface.update_current_state_recursive();
348+
}
349+
350+
Ok(())
351+
}
352+
}

0 commit comments

Comments
 (0)