1#![allow(dead_code)]
11
12use alloc::{collections::BTreeMap, vec::Vec};
13
14use spin::RwLock;
15
16use crate::{error::KernelError, sync::once_lock::GlobalState};
17
18pub type WindowId = u32;
20
21pub type WorkspaceId = u8;
23
24pub const MAX_WORKSPACES: usize = 4;
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub enum WindowState {
30 Normal,
31 Minimized,
32 Maximized,
33 Fullscreen,
34 Hidden,
35}
36
37#[derive(Debug, Clone, Copy, PartialEq, Eq)]
39pub enum PlacementHeuristic {
40 Cascade,
42 Center,
44 Smart,
46 Manual { x: i32, y: i32 },
48}
49
50#[derive(Debug, Clone, Copy, PartialEq, Eq)]
52pub enum SnapZone {
53 None,
54 Left,
55 Right,
56 Top,
57 Bottom,
58 TopLeft,
59 TopRight,
60 BottomLeft,
61 BottomRight,
62 Maximize,
63}
64
65#[derive(Debug, Clone, Copy)]
67pub enum TileLayout {
68 EqualColumns,
70 MasterStack,
73 Grid,
75}
76
77#[derive(Debug, Clone)]
79pub struct Window {
80 pub id: WindowId,
81 pub x: i32,
82 pub y: i32,
83 pub width: u32,
84 pub height: u32,
85 pub title: [u8; 64],
86 pub title_len: usize,
87 pub state: WindowState,
88 pub visible: bool,
89 pub focused: bool,
90 pub owner_pid: u64,
91 pub opacity: u8,
93 pub saved_geometry: Option<(i32, i32, u32, u32)>,
95 pub snap_zone: SnapZone,
97 pub workspace: WorkspaceId,
99}
100
101impl Window {
102 pub fn new(id: WindowId, x: i32, y: i32, width: u32, height: u32, owner_pid: u64) -> Self {
104 Self {
105 id,
106 x,
107 y,
108 width,
109 height,
110 title: [0; 64],
111 title_len: 0,
112 state: WindowState::Normal,
113 visible: true,
114 focused: false,
115 owner_pid,
116 opacity: 255,
117 saved_geometry: None,
118 snap_zone: SnapZone::None,
119 workspace: 0,
120 }
121 }
122
123 pub fn set_title(&mut self, title: &str) {
125 let bytes = title.as_bytes();
126 let len = bytes.len().min(64);
127 self.title[..len].copy_from_slice(&bytes[..len]);
128 self.title_len = len;
129 }
130
131 pub fn title_str(&self) -> &str {
133 core::str::from_utf8(&self.title[..self.title_len]).unwrap_or("")
134 }
135}
136
137pub struct Workspace {
139 pub id: WorkspaceId,
140 pub name: [u8; 32],
141 pub name_len: usize,
142 pub windows: Vec<WindowId>,
143}
144
145impl Workspace {
146 fn new(id: WorkspaceId) -> Self {
148 let mut name = [0u8; 32];
149 let digit = b'1' + id;
150 name[0] = digit;
151 Self {
152 id,
153 name,
154 name_len: 1,
155 windows: Vec::new(),
156 }
157 }
158
159 pub fn name_str(&self) -> &str {
161 core::str::from_utf8(&self.name[..self.name_len]).unwrap_or("")
162 }
163}
164
165#[derive(Debug, Clone, Copy)]
167pub enum InputEvent {
168 KeyPress {
169 scancode: u8,
170 character: char,
171 },
172 KeyRelease {
173 scancode: u8,
174 },
175 MouseMove {
176 x: i32,
177 y: i32,
178 },
179 MouseButton {
180 button: u8,
181 pressed: bool,
182 x: i32,
183 y: i32,
184 },
185 MouseScroll {
186 delta_x: i16,
187 delta_y: i16,
188 },
189}
190
191#[derive(Debug, Clone)]
193pub struct WindowEvent {
194 pub window_id: WindowId,
195 pub event: InputEvent,
196}
197
198pub struct WindowManager {
200 windows: RwLock<BTreeMap<WindowId, Window>>,
202
203 z_order: RwLock<Vec<WindowId>>,
205
206 focused_window: RwLock<Option<WindowId>>,
208
209 event_queue: RwLock<Vec<WindowEvent>>,
211
212 next_window_id: RwLock<WindowId>,
214
215 mouse_x: RwLock<i32>,
217 mouse_y: RwLock<i32>,
218
219 placement_heuristic: RwLock<PlacementHeuristic>,
222
223 cascade_offset: RwLock<(i32, i32)>,
225
226 screen_width: RwLock<u32>,
228 screen_height: RwLock<u32>,
229
230 workspaces: RwLock<Vec<Workspace>>,
233
234 active_workspace: RwLock<WorkspaceId>,
236}
237
238impl WindowManager {
239 pub fn new() -> Self {
241 let mut workspaces = Vec::with_capacity(MAX_WORKSPACES);
242 for i in 0..MAX_WORKSPACES {
243 workspaces.push(Workspace::new(i as WorkspaceId));
244 }
245
246 Self {
247 windows: RwLock::new(BTreeMap::new()),
248 z_order: RwLock::new(Vec::new()),
249 focused_window: RwLock::new(None),
250 event_queue: RwLock::new(Vec::new()),
251 next_window_id: RwLock::new(1),
252 mouse_x: RwLock::new(0),
253 mouse_y: RwLock::new(0),
254 placement_heuristic: RwLock::new(PlacementHeuristic::Cascade),
255 cascade_offset: RwLock::new((32, 32)),
256 screen_width: RwLock::new(1280),
257 screen_height: RwLock::new(800),
258 workspaces: RwLock::new(workspaces),
259 active_workspace: RwLock::new(0),
260 }
261 }
262
263 pub fn create_window(
265 &self,
266 x: i32,
267 y: i32,
268 width: u32,
269 height: u32,
270 owner_pid: u64,
271 ) -> Result<WindowId, KernelError> {
272 let id = {
273 let mut next_id = self.next_window_id.write();
274 let id = *next_id;
275 *next_id += 1;
276 id
277 };
278
279 let window = Window::new(id, x, y, width, height, owner_pid);
280
281 self.windows.write().insert(id, window);
282 self.z_order.write().push(id);
283
284 let active = *self.active_workspace.read();
286 {
287 let mut workspaces = self.workspaces.write();
288 if let Some(ws) = workspaces.get_mut(active as usize) {
289 ws.windows.push(id);
290 }
291 }
292
293 println!("[WM] Created window {} for PID {}", id, owner_pid);
294
295 Ok(id)
296 }
297
298 pub fn destroy_window(&self, window_id: WindowId) -> Result<(), KernelError> {
300 self.windows.write().remove(&window_id);
301 self.z_order.write().retain(|&id| id != window_id);
302
303 {
305 let mut workspaces = self.workspaces.write();
306 for ws in workspaces.iter_mut() {
307 ws.windows.retain(|&id| id != window_id);
308 }
309 }
310
311 if *self.focused_window.read() == Some(window_id) {
312 *self.focused_window.write() = None;
313 }
314
315 println!("[WM] Destroyed window {}", window_id);
316
317 Ok(())
318 }
319
320 pub fn move_window(&self, window_id: WindowId, x: i32, y: i32) -> Result<(), KernelError> {
322 if let Some(window) = self.windows.write().get_mut(&window_id) {
323 window.x = x;
324 window.y = y;
325 Ok(())
326 } else {
327 Err(KernelError::NotFound {
328 resource: "window",
329 id: window_id as u64,
330 })
331 }
332 }
333
334 pub fn resize_window(
336 &self,
337 window_id: WindowId,
338 width: u32,
339 height: u32,
340 ) -> Result<(), KernelError> {
341 if let Some(window) = self.windows.write().get_mut(&window_id) {
342 window.width = width;
343 window.height = height;
344 Ok(())
345 } else {
346 Err(KernelError::NotFound {
347 resource: "window",
348 id: window_id as u64,
349 })
350 }
351 }
352
353 pub fn focus_window(&self, window_id: WindowId) -> Result<(), KernelError> {
355 if let Some(prev_id) = *self.focused_window.read() {
357 if let Some(prev_window) = self.windows.write().get_mut(&prev_id) {
358 prev_window.focused = false;
359 }
360 }
361
362 if let Some(window) = self.windows.write().get_mut(&window_id) {
364 window.focused = true;
365 *self.focused_window.write() = Some(window_id);
366
367 let mut z_order = self.z_order.write();
369 z_order.retain(|&id| id != window_id);
370 z_order.push(window_id);
371
372 println!("[WM] Focused window {}", window_id);
373 Ok(())
374 } else {
375 Err(KernelError::NotFound {
376 resource: "window",
377 id: window_id as u64,
378 })
379 }
380 }
381
382 pub fn window_at_position(&self, x: i32, y: i32) -> Option<WindowId> {
384 let windows = self.windows.read();
385 let z_order = self.z_order.read();
386
387 for &window_id in z_order.iter().rev() {
389 if let Some(window) = windows.get(&window_id) {
390 if window.visible
391 && x >= window.x
392 && x < window.x + window.width as i32
393 && y >= window.y
394 && y < window.y + window.height as i32
395 {
396 return Some(window_id);
397 }
398 }
399 }
400
401 None
402 }
403
404 pub fn process_input(&self, event: InputEvent) {
406 match event {
407 InputEvent::MouseMove { x, y } => {
408 *self.mouse_x.write() = x;
409 *self.mouse_y.write() = y;
410
411 if let Some(window_id) = *self.focused_window.read() {
413 self.queue_event(WindowEvent { window_id, event });
414 }
415 }
416 InputEvent::MouseButton {
417 button: _button,
418 pressed,
419 x,
420 y,
421 } => {
422 if pressed {
423 if let Some(window_id) = self.window_at_position(x, y) {
425 if let Err(_e) = self.focus_window(window_id) {
426 crate::println!(
427 "[WM] Warning: failed to focus window {}: {:?}",
428 window_id,
429 _e
430 );
431 }
432
433 self.queue_event(WindowEvent { window_id, event });
435 }
436 } else {
437 if let Some(window_id) = *self.focused_window.read() {
439 self.queue_event(WindowEvent { window_id, event });
440 }
441 }
442 }
443 InputEvent::KeyPress { .. }
444 | InputEvent::KeyRelease { .. }
445 | InputEvent::MouseScroll { .. } => {
446 if let Some(window_id) = *self.focused_window.read() {
448 self.queue_event(WindowEvent { window_id, event });
449 }
450 }
451 }
452 }
453
454 pub fn queue_event(&self, event: WindowEvent) {
456 self.event_queue.write().push(event);
457 }
458
459 pub fn get_events(&self, window_id: WindowId) -> Vec<InputEvent> {
461 let mut queue = self.event_queue.write();
462 let mut events = Vec::new();
463
464 let mut i = 0;
466 while i < queue.len() {
467 if queue[i].window_id == window_id {
468 events.push(queue.remove(i).event);
469 } else {
470 i += 1;
471 }
472 }
473
474 events
475 }
476
477 pub fn set_window_title(&self, window_id: WindowId, title: &str) {
479 if let Some(window) = self.windows.write().get_mut(&window_id) {
480 window.set_title(title);
481 }
482 }
483
484 pub fn get_window(&self, window_id: WindowId) -> Option<Window> {
486 self.windows.read().get(&window_id).cloned()
487 }
488
489 pub fn get_focused_window_id(&self) -> Option<WindowId> {
491 *self.focused_window.read()
492 }
493
494 pub fn get_all_windows(&self) -> Vec<Window> {
496 self.windows.read().values().cloned().collect()
497 }
498
499 pub fn get_visible_windows(&self) -> Vec<Window> {
502 let active = *self.active_workspace.read();
503 let windows = self.windows.read();
504 let z_order = self.z_order.read();
505 z_order
506 .iter()
507 .filter_map(|id| windows.get(id))
508 .filter(|w| w.visible && w.workspace == active && w.state != WindowState::Minimized)
509 .cloned()
510 .collect()
511 }
512
513 pub fn set_screen_size(&self, width: u32, height: u32) {
519 *self.screen_width.write() = width;
520 *self.screen_height.write() = height;
521 }
522
523 pub fn set_placement_heuristic(&self, heuristic: PlacementHeuristic) {
525 *self.placement_heuristic.write() = heuristic;
526 }
527
528 pub fn place_window(&self, window_id: WindowId) -> (i32, i32) {
534 let (win_w, win_h) = {
535 let windows = self.windows.read();
536 match windows.get(&window_id) {
537 Some(w) => (w.width, w.height),
538 None => return (0, 0),
539 }
540 };
541
542 let scr_w = *self.screen_width.read();
543 let scr_h = *self.screen_height.read();
544 let heuristic = *self.placement_heuristic.read();
545
546 match heuristic {
547 PlacementHeuristic::Cascade => {
548 let mut offset = self.cascade_offset.write();
549 let x = offset.0;
550 let y = offset.1;
551
552 offset.0 += 32;
554 offset.1 += 32;
555
556 if offset.0 + win_w as i32 > scr_w as i32 || offset.1 + win_h as i32 > scr_h as i32
558 {
559 offset.0 = 32;
560 offset.1 = 32;
561 }
562
563 (x, y)
564 }
565 PlacementHeuristic::Center => {
566 let x = (scr_w as i32 - win_w as i32) / 2;
567 let y = (scr_h as i32 - win_h as i32) / 2;
568 (x.max(0), y.max(0))
569 }
570 PlacementHeuristic::Smart => self.smart_place(win_w, win_h, scr_w, scr_h),
571 PlacementHeuristic::Manual { x, y } => (x, y),
572 }
573 }
574
575 fn smart_place(&self, win_w: u32, win_h: u32, scr_w: u32, scr_h: u32) -> (i32, i32) {
578 let windows = self.windows.read();
579 let visible: Vec<&Window> = windows
580 .values()
581 .filter(|w| w.visible && w.state != WindowState::Minimized)
582 .collect();
583
584 if visible.is_empty() {
585 let x = (scr_w as i32 - win_w as i32) / 2;
586 let y = (scr_h as i32 - win_h as i32) / 2;
587 return (x.max(0), y.max(0));
588 }
589
590 let step = 64i32;
591 let mut best_x = 0i32;
592 let mut best_y = 0i32;
593 let mut best_overlap = i64::MAX;
594
595 let max_x = (scr_w as i32 - win_w as i32).max(0);
596 let max_y = (scr_h as i32 - win_h as i32).max(0);
597
598 let mut cy = 0i32;
599 while cy <= max_y {
600 let mut cx = 0i32;
601 while cx <= max_x {
602 let mut total_overlap: i64 = 0;
603
604 for w in &visible {
605 let ox1 = cx.max(w.x);
606 let oy1 = cy.max(w.y);
607 let ox2 = (cx + win_w as i32).min(w.x + w.width as i32);
608 let oy2 = (cy + win_h as i32).min(w.y + w.height as i32);
609
610 if ox1 < ox2 && oy1 < oy2 {
611 total_overlap += (ox2 - ox1) as i64 * (oy2 - oy1) as i64;
612 }
613 }
614
615 if total_overlap < best_overlap {
616 best_overlap = total_overlap;
617 best_x = cx;
618 best_y = cy;
619
620 if total_overlap == 0 {
621 return (best_x, best_y);
622 }
623 }
624
625 cx += step;
626 }
627 cy += step;
628 }
629
630 (best_x, best_y)
631 }
632
633 pub fn snap_window(&self, window_id: WindowId, zone: SnapZone) {
637 let scr_w = *self.screen_width.read();
638 let scr_h = *self.screen_height.read();
639 let panel_h: u32 = 32;
640 let usable_h = scr_h.saturating_sub(panel_h);
641
642 let half_w = scr_w / 2;
643 let half_h = usable_h / 2;
644
645 let mut windows = self.windows.write();
646 let window = match windows.get_mut(&window_id) {
647 Some(w) => w,
648 None => return,
649 };
650
651 if window.snap_zone == SnapZone::None {
653 window.saved_geometry = Some((window.x, window.y, window.width, window.height));
654 }
655
656 match zone {
657 SnapZone::None => {
658 if let Some((sx, sy, sw, sh)) = window.saved_geometry.take() {
659 window.x = sx;
660 window.y = sy;
661 window.width = sw;
662 window.height = sh;
663 }
664 window.state = WindowState::Normal;
665 }
666 SnapZone::Left => {
667 window.x = 0;
668 window.y = 0;
669 window.width = half_w;
670 window.height = usable_h;
671 }
672 SnapZone::Right => {
673 window.x = half_w as i32;
674 window.y = 0;
675 window.width = scr_w - half_w;
676 window.height = usable_h;
677 }
678 SnapZone::Top => {
679 window.x = 0;
680 window.y = 0;
681 window.width = scr_w;
682 window.height = half_h;
683 }
684 SnapZone::Bottom => {
685 window.x = 0;
686 window.y = half_h as i32;
687 window.width = scr_w;
688 window.height = usable_h - half_h;
689 }
690 SnapZone::TopLeft => {
691 window.x = 0;
692 window.y = 0;
693 window.width = half_w;
694 window.height = half_h;
695 }
696 SnapZone::TopRight => {
697 window.x = half_w as i32;
698 window.y = 0;
699 window.width = scr_w - half_w;
700 window.height = half_h;
701 }
702 SnapZone::BottomLeft => {
703 window.x = 0;
704 window.y = half_h as i32;
705 window.width = half_w;
706 window.height = usable_h - half_h;
707 }
708 SnapZone::BottomRight => {
709 window.x = half_w as i32;
710 window.y = half_h as i32;
711 window.width = scr_w - half_w;
712 window.height = usable_h - half_h;
713 }
714 SnapZone::Maximize => {
715 window.x = 0;
716 window.y = 0;
717 window.width = scr_w;
718 window.height = usable_h;
719 window.state = WindowState::Maximized;
720 }
721 }
722
723 window.snap_zone = zone;
724 }
725
726 pub fn detect_snap_zone(x: i32, y: i32, screen_w: u32, screen_h: u32) -> SnapZone {
731 const EDGE_THRESHOLD: i32 = 8;
732 let sw = screen_w as i32;
733 let sh = screen_h as i32;
734
735 let near_left = x < EDGE_THRESHOLD;
736 let near_right = x >= sw - EDGE_THRESHOLD;
737 let near_top = y < EDGE_THRESHOLD;
738 let near_bottom = y >= sh - EDGE_THRESHOLD;
739
740 match (near_left, near_right, near_top, near_bottom) {
741 (true, false, true, false) => SnapZone::TopLeft,
742 (true, false, false, true) => SnapZone::BottomLeft,
743 (false, true, true, false) => SnapZone::TopRight,
744 (false, true, false, true) => SnapZone::BottomRight,
745 (true, false, false, false) => SnapZone::Left,
746 (false, true, false, false) => SnapZone::Right,
747 (false, false, true, false) => SnapZone::Top,
748 (false, false, false, true) => SnapZone::Bottom,
749 _ => SnapZone::None,
750 }
751 }
752
753 pub fn tile_windows(&self, layout: TileLayout) {
755 let scr_w = *self.screen_width.read();
756 let scr_h = *self.screen_height.read();
757 let panel_h: u32 = 32;
758 let usable_h = scr_h.saturating_sub(panel_h);
759
760 let active_ws = *self.active_workspace.read();
761 let mut windows = self.windows.write();
762
763 let visible_ids: Vec<WindowId> = windows
764 .values()
765 .filter(|w| {
766 w.visible
767 && w.workspace == active_ws
768 && w.state != WindowState::Minimized
769 && w.state != WindowState::Hidden
770 })
771 .map(|w| w.id)
772 .collect();
773
774 let count = visible_ids.len();
775 if count == 0 {
776 return;
777 }
778
779 match layout {
780 TileLayout::EqualColumns => {
781 let col_width = scr_w / count as u32;
782 for (i, &wid) in visible_ids.iter().enumerate() {
783 if let Some(w) = windows.get_mut(&wid) {
784 w.x = (i as u32 * col_width) as i32;
785 w.y = 0;
786 w.width = col_width;
787 w.height = usable_h;
788 w.snap_zone = SnapZone::None;
789 }
790 }
791 }
792 TileLayout::MasterStack => {
793 if count == 1 {
794 if let Some(w) = windows.get_mut(&visible_ids[0]) {
795 w.x = 0;
796 w.y = 0;
797 w.width = scr_w;
798 w.height = usable_h;
799 w.snap_zone = SnapZone::None;
800 }
801 } else {
802 let master_w = (scr_w * 60) / 100;
803 let stack_w = scr_w - master_w;
804 let stack_count = (count - 1) as u32;
805 let stack_h = usable_h / stack_count;
806
807 if let Some(w) = windows.get_mut(&visible_ids[0]) {
808 w.x = 0;
809 w.y = 0;
810 w.width = master_w;
811 w.height = usable_h;
812 w.snap_zone = SnapZone::None;
813 }
814
815 for (i, &wid) in visible_ids.iter().skip(1).enumerate() {
816 if let Some(w) = windows.get_mut(&wid) {
817 w.x = master_w as i32;
818 w.y = (i as u32 * stack_h) as i32;
819 w.width = stack_w;
820 w.height = stack_h;
821 w.snap_zone = SnapZone::None;
822 }
823 }
824 }
825 }
826 TileLayout::Grid => {
827 let cols = {
828 let mut c = 1u32;
829 while c * c < count as u32 {
830 c += 1;
831 }
832 c
833 };
834 let rows = (count as u32).div_ceil(cols);
835 let cell_w = scr_w / cols;
836 let cell_h = usable_h / rows;
837
838 for (i, &wid) in visible_ids.iter().enumerate() {
839 let col = (i as u32) % cols;
840 let row = (i as u32) / cols;
841 if let Some(w) = windows.get_mut(&wid) {
842 w.x = (col * cell_w) as i32;
843 w.y = (row * cell_h) as i32;
844 w.width = cell_w;
845 w.height = cell_h;
846 w.snap_zone = SnapZone::None;
847 }
848 }
849 }
850 }
851 }
852
853 pub fn set_window_opacity(&self, window_id: WindowId, opacity: u8) {
855 if let Some(window) = self.windows.write().get_mut(&window_id) {
856 window.opacity = opacity;
857 }
858 }
859
860 pub fn switch_workspace(&self, workspace_id: WorkspaceId) {
866 if workspace_id as usize >= MAX_WORKSPACES {
867 return;
868 }
869
870 let current = *self.active_workspace.read();
871 if current == workspace_id {
872 return;
873 }
874
875 let mut windows = self.windows.write();
876
877 for window in windows.values_mut() {
878 if window.workspace == current {
879 window.visible = false;
880 }
881 }
882
883 for window in windows.values_mut() {
884 if window.workspace == workspace_id && window.state != WindowState::Minimized {
885 window.visible = true;
886 }
887 }
888
889 *self.active_workspace.write() = workspace_id;
890 *self.focused_window.write() = None;
891
892 crate::println!("[WM] Switched to workspace {}", workspace_id + 1);
893 }
894
895 pub fn move_window_to_workspace(&self, window_id: WindowId, workspace_id: WorkspaceId) {
897 if workspace_id as usize >= MAX_WORKSPACES {
898 return;
899 }
900
901 let active = *self.active_workspace.read();
902
903 {
904 let mut workspaces = self.workspaces.write();
905 for ws in workspaces.iter_mut() {
906 ws.windows.retain(|&id| id != window_id);
907 }
908 if let Some(ws) = workspaces.get_mut(workspace_id as usize) {
909 ws.windows.push(window_id);
910 }
911 }
912
913 let mut windows = self.windows.write();
914 if let Some(window) = windows.get_mut(&window_id) {
915 window.workspace = workspace_id;
916
917 if workspace_id != active {
918 window.visible = false;
919 if *self.focused_window.read() == Some(window_id) {
920 *self.focused_window.write() = None;
921 }
922 } else {
923 window.visible = true;
924 }
925 }
926
927 crate::println!(
928 "[WM] Moved window {} to workspace {}",
929 window_id,
930 workspace_id + 1
931 );
932 }
933
934 pub fn get_active_workspace(&self) -> WorkspaceId {
936 *self.active_workspace.read()
937 }
938
939 pub fn get_workspace_windows(&self, workspace_id: WorkspaceId) -> Vec<WindowId> {
941 if workspace_id as usize >= MAX_WORKSPACES {
942 return Vec::new();
943 }
944 let workspaces = self.workspaces.read();
945 match workspaces.get(workspace_id as usize) {
946 Some(ws) => ws.windows.clone(),
947 None => Vec::new(),
948 }
949 }
950
951 pub fn event_loop_iteration(&self) {
953 }
958}
959
960impl Default for WindowManager {
961 fn default() -> Self {
962 Self::new()
963 }
964}
965
966static WINDOW_MANAGER: GlobalState<WindowManager> = GlobalState::new();
968
969pub fn init() -> Result<(), KernelError> {
971 let wm = WindowManager::new();
972 WINDOW_MANAGER
973 .init(wm)
974 .map_err(|_| KernelError::InvalidState {
975 expected: "uninitialized",
976 actual: "initialized",
977 })?;
978
979 println!("[WM] Window manager initialized");
980 Ok(())
981}
982
983pub fn with_window_manager<R, F: FnOnce(&WindowManager) -> R>(f: F) -> Option<R> {
985 WINDOW_MANAGER.with(f)
986}
987
988pub fn get_window_manager() -> Result<(), KernelError> {
990 WINDOW_MANAGER
991 .with(|_| ())
992 .ok_or(KernelError::InvalidState {
993 expected: "initialized",
994 actual: "uninitialized",
995 })
996}
997
998#[cfg(test)]
999mod tests {
1000 use super::*;
1001
1002 #[test]
1003 fn test_window_creation() {
1004 let wm = WindowManager::new();
1005 let id = wm.create_window(0, 0, 640, 480, 1).unwrap();
1006 assert_eq!(id, 1);
1007 }
1008
1009 #[test]
1010 fn test_window_focus() {
1011 let wm = WindowManager::new();
1012 let id1 = wm.create_window(0, 0, 640, 480, 1).unwrap();
1013 let id2 = wm.create_window(100, 100, 640, 480, 1).unwrap();
1014
1015 wm.focus_window(id1).unwrap();
1016 assert_eq!(*wm.focused_window.read(), Some(id1));
1017
1018 wm.focus_window(id2).unwrap();
1019 assert_eq!(*wm.focused_window.read(), Some(id2));
1020 }
1021
1022 #[test]
1023 fn test_window_at_position() {
1024 let wm = WindowManager::new();
1025 let id = wm.create_window(100, 100, 200, 150, 1).unwrap();
1026
1027 assert_eq!(wm.window_at_position(150, 150), Some(id));
1028 assert_eq!(wm.window_at_position(50, 50), None);
1029 }
1030
1031 #[test]
1032 fn test_snap_zone_detection() {
1033 assert_eq!(
1034 WindowManager::detect_snap_zone(2, 400, 1280, 800),
1035 SnapZone::Left
1036 );
1037 assert_eq!(
1038 WindowManager::detect_snap_zone(1275, 400, 1280, 800),
1039 SnapZone::Right
1040 );
1041 assert_eq!(
1042 WindowManager::detect_snap_zone(2, 2, 1280, 800),
1043 SnapZone::TopLeft
1044 );
1045 assert_eq!(
1046 WindowManager::detect_snap_zone(640, 400, 1280, 800),
1047 SnapZone::None
1048 );
1049 }
1050}