oxwm

https://git.tonybtw.com/oxwm.git git://git.tonybtw.com/oxwm.git
27,690 bytes raw
1
use crate::Config;
2
use crate::bar::Bar;
3
use crate::errors::WmError;
4
use crate::keyboard::{self, Arg, KeyAction, handlers};
5
use crate::layout::GapConfig;
6
use crate::layout::Layout;
7
use crate::layout::tiling::TilingLayout;
8
use std::collections::HashSet;
9
use x11rb::cursor::Handle as CursorHandle;
10
11
use x11rb::connection::Connection;
12
use x11rb::protocol::Event;
13
use x11rb::protocol::xproto::*;
14
use x11rb::rust_connection::RustConnection;
15
16
pub type TagMask = u32;
17
pub fn tag_mask(tag: usize) -> TagMask {
18
    1 << tag
19
}
20
21
pub struct WindowManager {
22
    config: Config,
23
    connection: RustConnection,
24
    screen_number: usize,
25
    root: Window,
26
    screen: Screen,
27
    windows: Vec<Window>,
28
    focused_window: Option<Window>,
29
    layout: Box<dyn Layout>,
30
    window_tags: std::collections::HashMap<Window, TagMask>,
31
    selected_tags: TagMask,
32
    gaps_enabled: bool,
33
    fullscreen_window: Option<Window>,
34
    floating_windows: HashSet<Window>,
35
    bar: Bar,
36
}
37
38
type WmResult<T> = Result<T, WmError>;
39
40
impl WindowManager {
41
    pub fn new(config: Config) -> WmResult<Self> {
42
        let (connection, screen_number) = x11rb::connect(None)?;
43
        let root = connection.setup().roots[screen_number].root;
44
        let screen = connection.setup().roots[screen_number].clone();
45
46
        let normal_cursor = CursorHandle::new(
47
            &connection,
48
            screen_number,
49
            &x11rb::resource_manager::new_from_default(&connection)?,
50
        )?
51
        .reply()?
52
        .load_cursor(&connection, "left_ptr")?;
53
54
        connection
55
            .change_window_attributes(
56
                root,
57
                &ChangeWindowAttributesAux::new()
58
                    .cursor(normal_cursor)
59
                    .event_mask(
60
                        EventMask::SUBSTRUCTURE_REDIRECT
61
                            | EventMask::SUBSTRUCTURE_NOTIFY
62
                            | EventMask::PROPERTY_CHANGE
63
                            | EventMask::KEY_PRESS
64
                            | EventMask::BUTTON_PRESS,
65
                    ),
66
            )?
67
            .check()?;
68
69
        connection.grab_button(
70
            false,
71
            root,
72
            EventMask::BUTTON_PRESS | EventMask::BUTTON_RELEASE,
73
            GrabMode::SYNC,
74
            GrabMode::ASYNC,
75
            x11rb::NONE,
76
            x11rb::NONE,
77
            ButtonIndex::M1,
78
            u16::from(config.modkey).into(),
79
        )?;
80
81
        connection.grab_button(
82
            false,
83
            root,
84
            EventMask::BUTTON_PRESS | EventMask::BUTTON_RELEASE,
85
            GrabMode::SYNC,
86
            GrabMode::ASYNC,
87
            x11rb::NONE,
88
            x11rb::NONE,
89
            ButtonIndex::M3,
90
            u16::from(config.modkey).into(),
91
        )?;
92
93
        let bar = Bar::new(&connection, &screen, screen_number, &config)?;
94
95
        let selected_tags = Self::get_saved_selected_tags(&connection, root, config.tags.len())?;
96
        let gaps_enabled = config.gaps_enabled;
97
98
        let mut window_manager = Self {
99
            config,
100
            connection,
101
            screen_number,
102
            root,
103
            screen,
104
            windows: Vec::new(),
105
            focused_window: None,
106
            layout: Box::new(TilingLayout),
107
            window_tags: std::collections::HashMap::new(),
108
            selected_tags,
109
            gaps_enabled,
110
            fullscreen_window: None,
111
            floating_windows: HashSet::new(),
112
            bar,
113
        };
114
115
        window_manager.scan_existing_windows()?;
116
        window_manager.update_bar()?;
117
118
        Ok(window_manager)
119
    }
120
121
    fn get_saved_selected_tags(
122
        connection: &RustConnection,
123
        root: Window,
124
        tag_count: usize,
125
    ) -> WmResult<TagMask> {
126
        let net_current_desktop = connection
127
            .intern_atom(false, b"_NET_CURRENT_DESKTOP")?
128
            .reply()?
129
            .atom;
130
131
        match connection
132
            .get_property(false, root, net_current_desktop, AtomEnum::CARDINAL, 0, 1)?
133
            .reply()
134
        {
135
            Ok(prop) if prop.value.len() >= 4 => {
136
                let desktop = u32::from_ne_bytes([
137
                    prop.value[0],
138
                    prop.value[1],
139
                    prop.value[2],
140
                    prop.value[3],
141
                ]);
142
                if desktop < tag_count as u32 {
143
                    let mask = tag_mask(desktop as usize);
144
                    return Ok(mask);
145
                }
146
            }
147
            Ok(_) => {}
148
            Err(e) => {
149
                eprintln!("No _NET_CURRENT_DESKTOP property ({})", e);
150
            }
151
        }
152
        Ok(tag_mask(0))
153
    }
154
155
    fn scan_existing_windows(&mut self) -> WmResult<()> {
156
        let tree = self.connection.query_tree(self.root)?.reply()?;
157
        let net_client_info = self
158
            .connection
159
            .intern_atom(false, b"_NET_CLIENT_INFO")?
160
            .reply()?
161
            .atom;
162
163
        let wm_state_atom = self
164
            .connection
165
            .intern_atom(false, b"WM_STATE")?
166
            .reply()?
167
            .atom;
168
169
        for &window in &tree.children {
170
            if window == self.bar.window() {
171
                continue;
172
            }
173
174
            let Ok(attrs) = self.connection.get_window_attributes(window)?.reply() else {
175
                continue;
176
            };
177
178
            if attrs.override_redirect {
179
                continue;
180
            }
181
182
            if attrs.map_state == MapState::VIEWABLE {
183
                let tag = self.get_saved_tag(window, net_client_info)?;
184
                self.windows.push(window);
185
                self.window_tags.insert(window, tag);
186
                continue;
187
            }
188
189
            if attrs.map_state == MapState::UNMAPPED {
190
                let has_wm_state = self
191
                    .connection
192
                    .get_property(false, window, wm_state_atom, AtomEnum::ANY, 0, 2)?
193
                    .reply()
194
                    .is_ok_and(|prop| !prop.value.is_empty());
195
196
                if !has_wm_state {
197
                    continue;
198
                }
199
200
                let has_wm_class = self
201
                    .connection
202
                    .get_property(false, window, AtomEnum::WM_CLASS, AtomEnum::STRING, 0, 1024)?
203
                    .reply()
204
                    .is_ok_and(|prop| !prop.value.is_empty());
205
206
                if has_wm_class {
207
                    let tag = self.get_saved_tag(window, net_client_info)?;
208
                    self.windows.push(window);
209
                    self.window_tags.insert(window, tag);
210
                }
211
            }
212
        }
213
214
        if let Some(&first) = self.windows.first() {
215
            self.set_focus(Some(first))?;
216
        }
217
218
        self.apply_layout()?;
219
        Ok(())
220
    }
221
222
    fn get_saved_tag(&self, window: Window, net_client_info: Atom) -> WmResult<TagMask> {
223
        match self
224
            .connection
225
            .get_property(false, window, net_client_info, AtomEnum::CARDINAL, 0, 2)?
226
            .reply()
227
        {
228
            Ok(prop) if prop.value.len() >= 4 => {
229
                let tags = u32::from_ne_bytes([
230
                    prop.value[0],
231
                    prop.value[1],
232
                    prop.value[2],
233
                    prop.value[3],
234
                ]);
235
236
                if tags != 0 && tags < (1 << self.config.tags.len()) {
237
                    return Ok(tags);
238
                }
239
            }
240
            Ok(_) => {}
241
            Err(e) => {
242
                eprintln!("No _NET_CLIENT_INFO property ({})", e);
243
            }
244
        }
245
246
        Ok(self.selected_tags)
247
    }
248
249
    fn save_client_tag(&self, window: Window, tag: TagMask) -> WmResult<()> {
250
        let net_client_info = self
251
            .connection
252
            .intern_atom(false, b"_NET_CLIENT_INFO")?
253
            .reply()?
254
            .atom;
255
256
        let bytes = tag.to_ne_bytes().to_vec();
257
258
        self.connection.change_property(
259
            PropMode::REPLACE,
260
            window,
261
            net_client_info,
262
            AtomEnum::CARDINAL,
263
            32,
264
            1,
265
            &bytes,
266
        )?;
267
268
        self.connection.flush()?;
269
        Ok(())
270
    }
271
272
    fn set_wm_state(&self, window: Window, state: u32) -> WmResult<()> {
273
        let wm_state_atom = self
274
            .connection
275
            .intern_atom(false, b"WM_STATE")?
276
            .reply()?
277
            .atom;
278
279
        let data = [state, 0u32];
280
        let bytes: Vec<u8> = data.iter().flat_map(|&v| v.to_ne_bytes()).collect();
281
282
        self.connection.change_property(
283
            PropMode::REPLACE,
284
            window,
285
            wm_state_atom,
286
            wm_state_atom,
287
            32,
288
            2,
289
            &bytes,
290
        )?;
291
292
        self.connection.flush()?;
293
        Ok(())
294
    }
295
296
    pub fn run(&mut self) -> WmResult<bool> {
297
        println!("oxwm started on display {}", self.screen_number);
298
299
        keyboard::setup_keybinds(&self.connection, self.root, &self.config.keybindings)?;
300
        self.update_bar()?;
301
302
        loop {
303
            self.bar.update_blocks();
304
305
            if let Ok(Some(event)) = self.connection.poll_for_event() {
306
                if let Some(should_restart) = self.handle_event(event)? {
307
                    return Ok(should_restart);
308
                }
309
            }
310
311
            if self.bar.needs_redraw() {
312
                self.update_bar()?;
313
            }
314
315
            std::thread::sleep(std::time::Duration::from_millis(10));
316
        }
317
    }
318
319
    fn toggle_floating(&mut self) -> WmResult<()> {
320
        if let Some(focused) = self.focused_window {
321
            if self.floating_windows.contains(&focused) {
322
                self.floating_windows.remove(&focused);
323
                self.apply_layout()?;
324
            } else {
325
                self.floating_windows.insert(focused);
326
                self.connection.configure_window(
327
                    focused,
328
                    &ConfigureWindowAux::new().stack_mode(StackMode::ABOVE),
329
                )?;
330
                self.apply_layout()?;
331
                self.connection.flush()?;
332
            }
333
        }
334
        Ok(())
335
    }
336
337
    fn toggle_fullscreen(&mut self) -> WmResult<()> {
338
        if let Some(focused) = self.focused_window {
339
            if self.fullscreen_window == Some(focused) {
340
                self.fullscreen_window = None;
341
342
                self.connection.map_window(self.bar.window())?;
343
344
                self.apply_layout()?;
345
                self.update_focus_visuals()?;
346
            } else {
347
                self.fullscreen_window = Some(focused);
348
349
                self.connection.unmap_window(self.bar.window())?;
350
351
                let screen_width = self.screen.width_in_pixels as u32;
352
                let screen_height = self.screen.height_in_pixels as u32;
353
354
                self.connection.configure_window(
355
                    focused,
356
                    &ConfigureWindowAux::new()
357
                        .x(0)
358
                        .y(0)
359
                        .width(screen_width)
360
                        .height(screen_height)
361
                        .border_width(0)
362
                        .stack_mode(StackMode::ABOVE),
363
                )?;
364
365
                self.connection.flush()?;
366
            }
367
        }
368
        Ok(())
369
    }
370
371
    fn update_bar(&mut self) -> WmResult<()> {
372
        let mut occupied_tags: TagMask = 0;
373
        for &tags in self.window_tags.values() {
374
            occupied_tags |= tags;
375
        }
376
377
        self.bar.invalidate();
378
        self.bar
379
            .draw(&self.connection, self.selected_tags, occupied_tags)?;
380
        Ok(())
381
    }
382
383
    fn handle_key_action(&mut self, action: KeyAction, arg: &Arg) -> WmResult<()> {
384
        match action {
385
            KeyAction::Spawn => handlers::handle_spawn_action(action, arg)?,
386
            KeyAction::KillClient => {
387
                if let Some(focused) = self.focused_window {
388
                    match self.connection.kill_client(focused) {
389
                        Ok(_) => {
390
                            self.connection.flush()?;
391
                        }
392
                        Err(e) => {
393
                            eprintln!("Failed to kill window {}: {}", focused, e);
394
                        }
395
                    }
396
                }
397
            }
398
            KeyAction::ToggleFullScreen => {
399
                self.toggle_fullscreen()?;
400
            }
401
            KeyAction::ToggleFloating => {
402
                self.toggle_floating()?;
403
            }
404
405
            KeyAction::FocusStack => {
406
                if let Arg::Int(direction) = arg {
407
                    self.cycle_focus(*direction)?;
408
                }
409
            }
410
            KeyAction::Quit | KeyAction::Restart => {
411
                // Handled in handle_event
412
            }
413
            KeyAction::Recompile => {
414
                match std::process::Command::new("oxwm")
415
                    .arg("--recompile")
416
                    .spawn()
417
                {
418
                    Ok(_) => eprintln!("Recompiling in background"),
419
                    Err(e) => eprintln!("Failed to spawn recompile: {}", e),
420
                }
421
            }
422
            KeyAction::ViewTag => {
423
                if let Arg::Int(tag_index) = arg {
424
                    self.view_tag(*tag_index as usize)?;
425
                }
426
            }
427
            KeyAction::MoveToTag => {
428
                if let Arg::Int(tag_index) = arg {
429
                    self.move_to_tag(*tag_index as usize)?;
430
                }
431
            }
432
            KeyAction::ToggleGaps => {
433
                self.gaps_enabled = !self.gaps_enabled;
434
                self.apply_layout()?;
435
            }
436
            KeyAction::None => {}
437
        }
438
        Ok(())
439
    }
440
441
    fn is_window_visible(&self, window: Window) -> bool {
442
        if let Some(&tags) = self.window_tags.get(&window) {
443
            (tags & self.selected_tags) != 0
444
        } else {
445
            false
446
        }
447
    }
448
449
    fn visible_windows(&self) -> Vec<Window> {
450
        self.windows
451
            .iter()
452
            .filter(|&&w| self.is_window_visible(w))
453
            .copied()
454
            .collect()
455
    }
456
457
    fn update_window_visibility(&self) -> WmResult<()> {
458
        for &window in &self.windows {
459
            if self.is_window_visible(window) {
460
                self.connection.map_window(window)?;
461
            } else {
462
                self.connection.unmap_window(window)?;
463
            }
464
        }
465
        self.connection.flush()?;
466
        Ok(())
467
    }
468
469
    pub fn view_tag(&mut self, tag_index: usize) -> WmResult<()> {
470
        if tag_index >= self.config.tags.len() {
471
            return Ok(());
472
        }
473
474
        if self.fullscreen_window.is_some() {
475
            self.fullscreen_window = None;
476
            self.connection.map_window(self.bar.window())?;
477
        }
478
479
        self.selected_tags = tag_mask(tag_index);
480
481
        self.save_selected_tags()?;
482
483
        self.update_window_visibility()?;
484
        self.apply_layout()?;
485
        self.update_bar()?;
486
487
        let visible = self.visible_windows();
488
        self.set_focus(visible.first().copied())?;
489
490
        Ok(())
491
    }
492
493
    fn save_selected_tags(&self) -> WmResult<()> {
494
        let net_current_desktop = self
495
            .connection
496
            .intern_atom(false, b"_NET_CURRENT_DESKTOP")?
497
            .reply()?
498
            .atom;
499
500
        let desktop = self.selected_tags.trailing_zeros();
501
502
        let bytes = (desktop as u32).to_ne_bytes();
503
        self.connection.change_property(
504
            PropMode::REPLACE,
505
            self.root,
506
            net_current_desktop,
507
            AtomEnum::CARDINAL,
508
            32,
509
            1,
510
            &bytes,
511
        )?;
512
513
        self.connection.flush()?;
514
        Ok(())
515
    }
516
517
    pub fn move_to_tag(&mut self, tag_index: usize) -> WmResult<()> {
518
        if tag_index >= self.config.tags.len() {
519
            return Ok(());
520
        }
521
522
        if let Some(focused) = self.focused_window {
523
            let mask = tag_mask(tag_index);
524
            self.window_tags.insert(focused, mask);
525
526
            let _ = self.save_client_tag(focused, mask);
527
528
            self.update_window_visibility()?;
529
            self.apply_layout()?;
530
            self.update_bar()?;
531
        }
532
533
        Ok(())
534
    }
535
536
    pub fn cycle_focus(&mut self, direction: i32) -> WmResult<()> {
537
        let visible = self.visible_windows();
538
539
        if visible.is_empty() {
540
            return Ok(());
541
        }
542
543
        let next_window = if let Some(current) = self.focused_window {
544
            if let Some(current_index) = visible.iter().position(|&w| w == current) {
545
                let next_index = if direction > 0 {
546
                    (current_index + 1) % visible.len()
547
                } else {
548
                    (current_index + visible.len() - 1) % visible.len()
549
                };
550
                visible[next_index]
551
            } else {
552
                visible[0]
553
            }
554
        } else {
555
            visible[0]
556
        };
557
558
        self.set_focus(Some(next_window))?;
559
        Ok(())
560
    }
561
562
    pub fn set_focus(&mut self, window: Option<Window>) -> WmResult<()> {
563
        self.focused_window = window;
564
565
        if let Some(win) = window {
566
            self.connection
567
                .set_input_focus(InputFocus::POINTER_ROOT, win, x11rb::CURRENT_TIME)?;
568
            self.connection.flush()?;
569
        }
570
571
        self.update_focus_visuals()?;
572
        Ok(())
573
    }
574
575
    fn update_focus_visuals(&self) -> WmResult<()> {
576
        for &window in &self.windows {
577
            let (border_color, border_width) = if self.focused_window == Some(window) {
578
                (self.config.border_focused, self.config.border_width)
579
            } else {
580
                (self.config.border_unfocused, self.config.border_width)
581
            };
582
583
            self.connection.configure_window(
584
                window,
585
                &ConfigureWindowAux::new().border_width(border_width),
586
            )?;
587
588
            self.connection.change_window_attributes(
589
                window,
590
                &ChangeWindowAttributesAux::new().border_pixel(border_color),
591
            )?;
592
        }
593
        self.connection.flush()?;
594
        Ok(())
595
    }
596
597
    fn move_mouse(&mut self, window: Window) -> WmResult<()> {
598
        self.floating_windows.insert(window);
599
600
        let geometry = self.connection.get_geometry(window)?.reply()?;
601
602
        self.connection
603
            .grab_pointer(
604
                false,
605
                self.root,
606
                (EventMask::POINTER_MOTION | EventMask::BUTTON_RELEASE).into(),
607
                GrabMode::ASYNC,
608
                GrabMode::ASYNC,
609
                x11rb::NONE,
610
                x11rb::NONE,
611
                x11rb::CURRENT_TIME,
612
            )?
613
            .reply()?;
614
615
        let pointer = self.connection.query_pointer(self.root)?.reply()?;
616
        let (start_x, start_y) = (pointer.root_x, pointer.root_y);
617
618
        self.connection.configure_window(
619
            window,
620
            &ConfigureWindowAux::new().stack_mode(StackMode::ABOVE),
621
        )?;
622
623
        loop {
624
            let event = self.connection.wait_for_event()?;
625
            match event {
626
                Event::MotionNotify(e) => {
627
                    let new_x = geometry.x + (e.root_x - start_x);
628
                    let new_y = geometry.y + (e.root_y - start_y);
629
                    self.connection.configure_window(
630
                        window,
631
                        &ConfigureWindowAux::new().x(new_x as i32).y(new_y as i32),
632
                    )?;
633
                    self.connection.flush()?;
634
                }
635
                Event::ButtonRelease(_) => break,
636
                _ => {}
637
            }
638
        }
639
640
        self.connection
641
            .ungrab_pointer(x11rb::CURRENT_TIME)?
642
            .check()?;
643
        self.connection
644
            .allow_events(Allow::REPLAY_POINTER, x11rb::CURRENT_TIME)?
645
            .check()?;
646
647
        Ok(())
648
    }
649
650
    fn resize_mouse(&mut self, window: Window) -> WmResult<()> {
651
        self.floating_windows.insert(window);
652
653
        let geometry = self.connection.get_geometry(window)?.reply()?;
654
655
        self.connection.warp_pointer(
656
            x11rb::NONE,
657
            window,
658
            0,
659
            0,
660
            0,
661
            0,
662
            geometry.width as i16,
663
            geometry.height as i16,
664
        )?;
665
666
        self.connection
667
            .grab_pointer(
668
                false,
669
                self.root,
670
                (EventMask::POINTER_MOTION | EventMask::BUTTON_RELEASE).into(),
671
                GrabMode::ASYNC,
672
                GrabMode::ASYNC,
673
                x11rb::NONE,
674
                x11rb::NONE,
675
                x11rb::CURRENT_TIME,
676
            )?
677
            .reply()?;
678
679
        self.connection.configure_window(
680
            window,
681
            &ConfigureWindowAux::new().stack_mode(StackMode::ABOVE),
682
        )?;
683
684
        loop {
685
            let event = self.connection.wait_for_event()?;
686
            match event {
687
                Event::MotionNotify(e) => {
688
                    let new_width = (e.root_x - geometry.x).max(1) as u32;
689
                    let new_height = (e.root_y - geometry.y).max(1) as u32;
690
691
                    self.connection.configure_window(
692
                        window,
693
                        &ConfigureWindowAux::new()
694
                            .width(new_width)
695
                            .height(new_height),
696
                    )?;
697
                    self.connection.flush()?;
698
                }
699
                Event::ButtonRelease(_) => break,
700
                _ => {}
701
            }
702
        }
703
704
        self.connection
705
            .ungrab_pointer(x11rb::CURRENT_TIME)?
706
            .check()?;
707
        self.connection
708
            .allow_events(Allow::REPLAY_POINTER, x11rb::CURRENT_TIME)?
709
            .check()?;
710
711
        Ok(())
712
    }
713
714
    fn handle_event(&mut self, event: Event) -> WmResult<Option<bool>> {
715
        match event {
716
            Event::MapRequest(event) => {
717
                let attrs = match self.connection.get_window_attributes(event.window)?.reply() {
718
                    Ok(attrs) => attrs,
719
                    Err(_) => return Ok(None),
720
                };
721
722
                if attrs.override_redirect {
723
                    return Ok(None);
724
                }
725
726
                if self.windows.contains(&event.window) {
727
                    return Ok(None);
728
                }
729
730
                self.connection.map_window(event.window)?;
731
                self.connection.change_window_attributes(
732
                    event.window,
733
                    &ChangeWindowAttributesAux::new().event_mask(EventMask::ENTER_WINDOW),
734
                )?;
735
736
                self.windows.push(event.window);
737
                self.window_tags.insert(event.window, self.selected_tags);
738
                self.set_wm_state(event.window, 1)?;
739
                let _ = self.save_client_tag(event.window, self.selected_tags);
740
741
                self.apply_layout()?;
742
                self.update_bar()?;
743
                self.set_focus(Some(event.window))?;
744
            }
745
            Event::UnmapNotify(event) => {
746
                if self.windows.contains(&event.window) && self.is_window_visible(event.window) {
747
                    self.remove_window(event.window)?;
748
                }
749
            }
750
            Event::DestroyNotify(event) => {
751
                if self.windows.contains(&event.window) {
752
                    self.remove_window(event.window)?;
753
                }
754
            }
755
            Event::EnterNotify(event) => {
756
                if event.mode != x11rb::protocol::xproto::NotifyMode::NORMAL {
757
                    return Ok(None);
758
                }
759
                if self.windows.contains(&event.event) {
760
                    self.set_focus(Some(event.event))?;
761
                }
762
            }
763
            Event::KeyPress(event) => {
764
                let (action, arg) = keyboard::handle_key_press(event, &self.config.keybindings);
765
                match action {
766
                    KeyAction::Quit => return Ok(Some(false)),
767
                    KeyAction::Restart => return Ok(Some(true)),
768
                    _ => self.handle_key_action(action, &arg)?,
769
                }
770
            }
771
            Event::ButtonPress(event) => {
772
                if event.event == self.bar.window() {
773
                    if let Some(tag_index) = self.bar.handle_click(event.event_x) {
774
                        self.view_tag(tag_index)?;
775
                    }
776
                } else if event.child != x11rb::NONE {
777
                    self.set_focus(Some(event.child))?;
778
779
                    if event.detail == ButtonIndex::M1.into() {
780
                        self.move_mouse(event.child)?;
781
                    } else if event.detail == ButtonIndex::M3.into() {
782
                        self.resize_mouse(event.child)?;
783
                    }
784
                }
785
            }
786
            Event::Expose(event) => {
787
                if event.window == self.bar.window() {
788
                    self.bar.invalidate();
789
                    self.update_bar()?;
790
                }
791
            }
792
            _ => {}
793
        }
794
        Ok(None)
795
    }
796
797
    fn apply_layout(&self) -> WmResult<()> {
798
        if self.fullscreen_window.is_some() {
799
            return Ok(());
800
        }
801
        let screen_width = self.screen.width_in_pixels as u32;
802
        let screen_height = self.screen.height_in_pixels as u32;
803
        let border_width = self.config.border_width;
804
805
        let bar_height = self.bar.height() as u32;
806
        let usable_height = screen_height.saturating_sub(bar_height);
807
808
        let gaps = if self.gaps_enabled {
809
            GapConfig {
810
                inner_horizontal: self.config.gap_inner_horizontal,
811
                inner_vertical: self.config.gap_inner_vertical,
812
                outer_horizontal: self.config.gap_outer_horizontal,
813
                outer_vertical: self.config.gap_outer_vertical,
814
            }
815
        } else {
816
            GapConfig {
817
                inner_horizontal: 0,
818
                inner_vertical: 0,
819
                outer_horizontal: 0,
820
                outer_vertical: 0,
821
            }
822
        };
823
824
        let visible: Vec<Window> = self
825
            .visible_windows()
826
            .into_iter()
827
            .filter(|w| !self.floating_windows.contains(w))
828
            .collect();
829
830
        let geometries = self
831
            .layout
832
            .arrange(&visible, screen_width, usable_height, &gaps);
833
834
        for (window, geometry) in visible.iter().zip(geometries.iter()) {
835
            let adjusted_width = geometry.width.saturating_sub(2 * border_width);
836
            let adjusted_height = geometry.height.saturating_sub(2 * border_width);
837
838
            let adjusted_y = geometry.y_coordinate + bar_height as i32;
839
840
            self.connection.configure_window(
841
                *window,
842
                &ConfigureWindowAux::new()
843
                    .x(geometry.x_coordinate)
844
                    .y(adjusted_y)
845
                    .width(adjusted_width)
846
                    .height(adjusted_height),
847
            )?;
848
        }
849
        self.connection.flush()?;
850
        Ok(())
851
    }
852
853
    fn remove_window(&mut self, window: Window) -> WmResult<()> {
854
        let initial_count = self.windows.len();
855
        self.windows.retain(|&w| w != window);
856
        self.window_tags.remove(&window);
857
        self.floating_windows.remove(&window);
858
859
        if self.fullscreen_window == Some(window) {
860
            self.fullscreen_window = None;
861
            self.connection.map_window(self.bar.window())?;
862
        }
863
864
        if self.windows.len() < initial_count {
865
            if self.focused_window == Some(window) {
866
                let new_focus = if self.windows.is_empty() {
867
                    None
868
                } else {
869
                    Some(self.windows[self.windows.len() - 1])
870
                };
871
                self.set_focus(new_focus)?;
872
            }
873
874
            self.apply_layout()?;
875
            self.update_bar()?;
876
        }
877
        Ok(())
878
    }
879
}