veridian_kernel/desktop/wayland/
shell.rs1#![allow(dead_code)]
11
12use alloc::{collections::BTreeMap, string::String, vec, vec::Vec};
13
14use spin::RwLock;
15
16use super::protocol::{self, Argument, WaylandMessage};
17use crate::error::KernelError;
18
19pub const XDG_WM_BASE_DESTROY: u16 = 0;
26pub const XDG_WM_BASE_CREATE_POSITIONER: u16 = 1;
28pub const XDG_WM_BASE_GET_XDG_SURFACE: u16 = 2;
30pub const XDG_WM_BASE_PONG: u16 = 3;
32
33pub const XDG_WM_BASE_PING: u16 = 0;
36
37pub const XDG_SURFACE_DESTROY: u16 = 0;
40pub const XDG_SURFACE_GET_TOPLEVEL: u16 = 1;
42pub const XDG_SURFACE_GET_POPUP: u16 = 2;
44pub const XDG_SURFACE_SET_WINDOW_GEOMETRY: u16 = 3;
46pub const XDG_SURFACE_ACK_CONFIGURE: u16 = 4;
48
49pub const XDG_SURFACE_CONFIGURE: u16 = 0;
52
53pub const XDG_TOPLEVEL_DESTROY: u16 = 0;
56pub const XDG_TOPLEVEL_SET_PARENT: u16 = 1;
58pub const XDG_TOPLEVEL_SET_TITLE: u16 = 2;
60pub const XDG_TOPLEVEL_SET_APP_ID: u16 = 3;
62pub const XDG_TOPLEVEL_MOVE: u16 = 5;
64pub const XDG_TOPLEVEL_RESIZE: u16 = 6;
66pub const XDG_TOPLEVEL_SET_MAX_SIZE: u16 = 7;
68pub const XDG_TOPLEVEL_SET_MIN_SIZE: u16 = 8;
70pub const XDG_TOPLEVEL_SET_MAXIMIZED: u16 = 9;
72pub const XDG_TOPLEVEL_UNSET_MAXIMIZED: u16 = 10;
74pub const XDG_TOPLEVEL_SET_FULLSCREEN: u16 = 11;
76pub const XDG_TOPLEVEL_UNSET_FULLSCREEN: u16 = 12;
78pub const XDG_TOPLEVEL_SET_MINIMIZED: u16 = 13;
80
81pub const XDG_TOPLEVEL_CONFIGURE: u16 = 0;
84pub const XDG_TOPLEVEL_CLOSE: u16 = 1;
86
87pub const XDG_TOPLEVEL_STATE_MAXIMIZED: u32 = 1;
90pub const XDG_TOPLEVEL_STATE_FULLSCREEN: u32 = 2;
92pub const XDG_TOPLEVEL_STATE_RESIZING: u32 = 3;
94pub const XDG_TOPLEVEL_STATE_ACTIVATED: u32 = 4;
96
97pub const ZXDG_DECORATION_MANAGER_V1: &str = "zxdg_decoration_manager_v1";
103
104pub const ZXDG_DECORATION_MANAGER_V1_VERSION: u32 = 1;
106
107pub const ZXDG_DECORATION_MANAGER_V1_DESTROY: u16 = 0;
110pub const ZXDG_DECORATION_MANAGER_V1_GET_TOPLEVEL_DECORATION: u16 = 1;
112
113pub const ZXDG_TOPLEVEL_DECORATION_V1_DESTROY: u16 = 0;
116pub const ZXDG_TOPLEVEL_DECORATION_V1_SET_MODE: u16 = 1;
118pub const ZXDG_TOPLEVEL_DECORATION_V1_UNSET_MODE: u16 = 2;
120
121pub const ZXDG_TOPLEVEL_DECORATION_V1_CONFIGURE: u16 = 0;
124
125pub const ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE: u32 = 1;
128pub const ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE: u32 = 2;
130
131#[derive(Debug, Clone, Copy, PartialEq, Eq)]
133pub enum DecorationMode {
134 ClientSide,
136 ServerSide,
138}
139
140impl DecorationMode {
141 pub fn from_u32(v: u32) -> Option<Self> {
143 match v {
144 ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE => Some(Self::ClientSide),
145 ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE => Some(Self::ServerSide),
146 _ => None,
147 }
148 }
149
150 pub fn to_u32(self) -> u32 {
152 match self {
153 Self::ClientSide => ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE,
154 Self::ServerSide => ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE,
155 }
156 }
157}
158
159pub struct ToplevelDecoration {
161 pub id: u32,
163 pub toplevel_id: u32,
165 pub mode: DecorationMode,
167}
168
169pub fn negotiate_decoration(_client_preference: Option<DecorationMode>) -> DecorationMode {
174 DecorationMode::ServerSide
176}
177
178#[derive(Debug, Clone, Copy, PartialEq, Eq)]
184pub enum WindowState {
185 Normal,
186 Maximized,
187 Fullscreen,
188 Minimized,
189}
190
191pub struct XdgSurface {
197 pub id: u32,
199 pub surface_id: u32,
201 pub configured: bool,
203 pub configure_serial: u32,
205 pub geometry: Option<(i32, i32, u32, u32)>,
207 pub toplevel: Option<XdgToplevel>,
209}
210
211impl XdgSurface {
212 pub fn new(id: u32, surface_id: u32) -> Self {
213 Self {
214 id,
215 surface_id,
216 configured: false,
217 configure_serial: 0,
218 geometry: None,
219 toplevel: None,
220 }
221 }
222
223 pub fn ack_configure(&mut self, serial: u32) -> bool {
225 if serial == self.configure_serial {
226 self.configured = true;
227 true
228 } else {
229 false
230 }
231 }
232
233 pub fn set_geometry(&mut self, x: i32, y: i32, width: u32, height: u32) {
235 self.geometry = Some((x, y, width, height));
236 }
237}
238
239pub struct XdgToplevel {
245 pub id: u32,
247 pub xdg_surface_id: u32,
249 pub title: String,
251 pub app_id: String,
253 pub state: WindowState,
255 pub activated: bool,
257 pub min_size: (u32, u32),
259 pub max_size: (u32, u32),
261}
262
263impl XdgToplevel {
264 pub fn new(id: u32, xdg_surface_id: u32) -> Self {
265 Self {
266 id,
267 xdg_surface_id,
268 title: String::new(),
269 app_id: String::new(),
270 state: WindowState::Normal,
271 activated: false,
272 min_size: (0, 0),
273 max_size: (0, 0),
274 }
275 }
276
277 pub fn set_title(&mut self, title: String) {
278 self.title = title;
279 }
280
281 pub fn set_app_id(&mut self, app_id: String) {
282 self.app_id = app_id;
283 }
284
285 pub fn set_maximized(&mut self) {
286 self.state = WindowState::Maximized;
287 }
288
289 pub fn set_fullscreen(&mut self) {
290 self.state = WindowState::Fullscreen;
291 }
292
293 pub fn set_minimized(&mut self) {
294 self.state = WindowState::Minimized;
295 }
296
297 pub fn set_normal(&mut self) {
298 self.state = WindowState::Normal;
299 }
300}
301
302pub struct XdgShell {
309 xdg_surfaces: BTreeMap<u32, XdgSurface>,
311 next_serial: u32,
313 pending_ping: Option<u32>,
315 client_alive: bool,
317}
318
319impl XdgShell {
320 pub fn new() -> Self {
321 Self {
322 xdg_surfaces: BTreeMap::new(),
323 next_serial: 1,
324 pending_ping: None,
325 client_alive: true,
326 }
327 }
328
329 fn next_serial(&mut self) -> u32 {
331 let s = self.next_serial;
332 self.next_serial += 1;
333 s
334 }
335
336 pub fn handle_pong(&mut self, serial: u32) -> bool {
340 if self.pending_ping == Some(serial) {
341 self.pending_ping = None;
342 self.client_alive = true;
343 true
344 } else {
345 false
346 }
347 }
348
349 pub fn build_ping(&mut self, wm_base_id: u32) -> Vec<u8> {
351 let serial = self.next_serial();
352 self.pending_ping = Some(serial);
353 protocol::serialize_message(&WaylandMessage::new(
354 wm_base_id,
355 XDG_WM_BASE_PING,
356 vec![Argument::Uint(serial)],
357 ))
358 }
359
360 pub fn create_xdg_surface(
364 &mut self,
365 xdg_surface_id: u32,
366 surface_id: u32,
367 ) -> Result<(), KernelError> {
368 let xdg = XdgSurface::new(xdg_surface_id, surface_id);
369 self.xdg_surfaces.insert(xdg_surface_id, xdg);
370 Ok(())
371 }
372
373 pub fn get_xdg_surface(&self, id: u32) -> Option<&XdgSurface> {
375 self.xdg_surfaces.get(&id)
376 }
377
378 pub fn get_xdg_surface_mut(&mut self, id: u32) -> Option<&mut XdgSurface> {
380 self.xdg_surfaces.get_mut(&id)
381 }
382
383 pub fn destroy_xdg_surface(&mut self, id: u32) -> bool {
385 self.xdg_surfaces.remove(&id).is_some()
386 }
387
388 pub fn with_toplevel_mut<R, F: FnOnce(&mut XdgToplevel) -> R>(
393 &mut self,
394 toplevel_id: u32,
395 f: F,
396 ) -> Option<R> {
397 for xdg in self.xdg_surfaces.values_mut() {
398 if let Some(ref mut tl) = xdg.toplevel {
399 if tl.id == toplevel_id {
400 return Some(f(tl));
401 }
402 }
403 }
404 None
405 }
406
407 pub fn create_toplevel(
411 &mut self,
412 xdg_surface_id: u32,
413 toplevel_id: u32,
414 ) -> Result<(), KernelError> {
415 let xdg = self
416 .xdg_surfaces
417 .get_mut(&xdg_surface_id)
418 .ok_or(KernelError::NotFound {
419 resource: "xdg_surface",
420 id: xdg_surface_id as u64,
421 })?;
422
423 if xdg.toplevel.is_some() {
424 return Err(KernelError::AlreadyExists {
425 resource: "xdg_toplevel",
426 id: toplevel_id as u64,
427 });
428 }
429
430 xdg.toplevel = Some(XdgToplevel::new(toplevel_id, xdg_surface_id));
431 Ok(())
432 }
433
434 pub fn build_initial_configure(
441 &mut self,
442 xdg_surface_id: u32,
443 width: u32,
444 height: u32,
445 ) -> Vec<u8> {
446 let serial = self.next_serial();
447
448 if let Some(xdg) = self.xdg_surfaces.get_mut(&xdg_surface_id) {
449 xdg.configure_serial = serial;
450 }
451
452 let mut events = Vec::new();
453
454 if let Some(xdg) = self.xdg_surfaces.get(&xdg_surface_id) {
456 if let Some(ref toplevel) = xdg.toplevel {
457 let mut states_data = Vec::new();
458 if toplevel.activated {
459 states_data.extend_from_slice(&XDG_TOPLEVEL_STATE_ACTIVATED.to_ne_bytes());
460 }
461 match toplevel.state {
462 WindowState::Maximized => {
463 states_data.extend_from_slice(&XDG_TOPLEVEL_STATE_MAXIMIZED.to_ne_bytes());
464 }
465 WindowState::Fullscreen => {
466 states_data.extend_from_slice(&XDG_TOPLEVEL_STATE_FULLSCREEN.to_ne_bytes());
467 }
468 _ => {}
469 }
470
471 let toplevel_configure = WaylandMessage::new(
472 toplevel.id,
473 XDG_TOPLEVEL_CONFIGURE,
474 vec![
475 Argument::Int(width as i32),
476 Argument::Int(height as i32),
477 Argument::Array(states_data),
478 ],
479 );
480 events.extend_from_slice(&protocol::serialize_message(&toplevel_configure));
481 }
482 }
483
484 let surface_configure = WaylandMessage::new(
486 xdg_surface_id,
487 XDG_SURFACE_CONFIGURE,
488 vec![Argument::Uint(serial)],
489 );
490 events.extend_from_slice(&protocol::serialize_message(&surface_configure));
491
492 events
493 }
494
495 pub fn build_close_event(&self, xdg_surface_id: u32) -> Option<Vec<u8>> {
497 let xdg = self.xdg_surfaces.get(&xdg_surface_id)?;
498 let toplevel = xdg.toplevel.as_ref()?;
499
500 Some(protocol::serialize_message(&WaylandMessage::new(
501 toplevel.id,
502 XDG_TOPLEVEL_CLOSE,
503 vec![],
504 )))
505 }
506}
507
508impl Default for XdgShell {
509 fn default() -> Self {
510 Self::new()
511 }
512}
513
514static XDG_SHELL: RwLock<Option<XdgShell>> = RwLock::new(None);
519
520pub fn init_xdg_shell() {
522 let mut shell = XDG_SHELL.write();
523 if shell.is_none() {
524 *shell = Some(XdgShell::new());
525 }
526}
527
528pub fn with_xdg_shell<R, F: FnOnce(&XdgShell) -> R>(f: F) -> Option<R> {
530 let guard = XDG_SHELL.read();
531 guard.as_ref().map(f)
532}
533
534pub fn with_xdg_shell_mut<R, F: FnOnce(&mut XdgShell) -> R>(f: F) -> Option<R> {
536 let mut guard = XDG_SHELL.write();
537 guard.as_mut().map(f)
538}
539
540#[cfg(test)]
541mod tests {
542 use super::*;
543
544 #[test]
545 fn test_xdg_toplevel_title() {
546 let mut tl = XdgToplevel::new(1, 1);
547 tl.set_title(String::from("Hello"));
548 assert_eq!(tl.title, "Hello");
549 }
550
551 #[test]
552 fn test_xdg_toplevel_state() {
553 let mut tl = XdgToplevel::new(1, 1);
554 assert_eq!(tl.state, WindowState::Normal);
555 tl.set_maximized();
556 assert_eq!(tl.state, WindowState::Maximized);
557 tl.set_fullscreen();
558 assert_eq!(tl.state, WindowState::Fullscreen);
559 }
560
561 #[test]
562 fn test_xdg_shell_ping_pong() {
563 let mut shell = XdgShell::new();
564 let _ping_event = shell.build_ping(10);
565 let serial = shell.pending_ping.unwrap();
566 assert!(shell.handle_pong(serial));
567 assert!(shell.pending_ping.is_none());
568 }
569
570 #[test]
571 fn test_xdg_surface_configure() {
572 let mut shell = XdgShell::new();
573 shell.create_xdg_surface(5, 3).unwrap();
574 shell.create_toplevel(5, 6).unwrap();
575
576 let events = shell.build_initial_configure(5, 800, 600);
577 assert!(!events.is_empty());
578
579 let serial = shell.get_xdg_surface(5).unwrap().configure_serial;
581 assert!(shell.get_xdg_surface_mut(5).unwrap().ack_configure(serial));
582 }
583
584 #[test]
585 fn test_xdg_surface_ack_wrong_serial() {
586 let mut shell = XdgShell::new();
587 shell.create_xdg_surface(5, 3).unwrap();
588 assert!(!shell.get_xdg_surface_mut(5).unwrap().ack_configure(999));
590 }
591
592 #[test]
593 fn test_close_event() {
594 let mut shell = XdgShell::new();
595 shell.create_xdg_surface(5, 3).unwrap();
596 shell.create_toplevel(5, 6).unwrap();
597 let close = shell.build_close_event(5);
598 assert!(close.is_some());
599 }
600}