oxwm

https://git.tonybtw.com/oxwm.git git://git.tonybtw.com/oxwm.git
50,904 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::tiling::TilingLayout;
7
use crate::layout::{Layout, LayoutBox, LayoutType, layout_from_str, next_layout};
8
use crate::monitor::{Monitor, detect_monitors};
9
use std::collections::HashSet;
10
use x11rb::cursor::Handle as CursorHandle;
11
12
use x11rb::connection::Connection;
13
use x11rb::protocol::Event;
14
use x11rb::protocol::xproto::*;
15
use x11rb::rust_connection::RustConnection;
16
17
pub type TagMask = u32;
18
pub fn tag_mask(tag: usize) -> TagMask {
19
    1 << tag
20
}
21
22
struct AtomCache {
23
    net_current_desktop: Atom,
24
    net_client_info: Atom,
25
    wm_state: Atom,
26
}
27
28
impl AtomCache {
29
    fn new(connection: &RustConnection) -> WmResult<Self> {
30
        let net_current_desktop = connection
31
            .intern_atom(false, b"_NET_CURRENT_DESKTOP")?
32
            .reply()?
33
            .atom;
34
35
        let net_client_info = connection
36
            .intern_atom(false, b"_NET_CLIENT_INFO")?
37
            .reply()?
38
            .atom;
39
40
        let wm_state = connection.intern_atom(false, b"WM_STATE")?.reply()?.atom;
41
42
        Ok(Self {
43
            net_current_desktop,
44
            net_client_info,
45
            wm_state,
46
        })
47
    }
48
}
49
50
pub struct WindowManager {
51
    config: Config,
52
    connection: RustConnection,
53
    screen_number: usize,
54
    root: Window,
55
    screen: Screen,
56
    windows: Vec<Window>,
57
    layout: LayoutBox,
58
    window_tags: std::collections::HashMap<Window, TagMask>,
59
    window_monitor: std::collections::HashMap<Window, usize>,
60
    window_geometries: std::collections::HashMap<Window, (i16, i16, u16, u16)>,
61
    gaps_enabled: bool,
62
    fullscreen_window: Option<Window>,
63
    floating_windows: HashSet<Window>,
64
    bars: Vec<Bar>,
65
    monitors: Vec<Monitor>,
66
    selected_monitor: usize,
67
    atoms: AtomCache,
68
    previous_focused: Option<Window>,
69
    display: *mut x11::xlib::Display,
70
    font: crate::bar::font::Font,
71
}
72
73
type WmResult<T> = Result<T, WmError>;
74
75
impl WindowManager {
76
    pub fn new(config: Config) -> WmResult<Self> {
77
        let (connection, screen_number) = x11rb::connect(None)?;
78
        let root = connection.setup().roots[screen_number].root;
79
        let screen = connection.setup().roots[screen_number].clone();
80
81
        let normal_cursor = CursorHandle::new(
82
            &connection,
83
            screen_number,
84
            &x11rb::resource_manager::new_from_default(&connection)?,
85
        )?
86
        .reply()?
87
        .load_cursor(&connection, "left_ptr")?;
88
89
        connection
90
            .change_window_attributes(
91
                root,
92
                &ChangeWindowAttributesAux::new()
93
                    .cursor(normal_cursor)
94
                    .event_mask(
95
                        EventMask::SUBSTRUCTURE_REDIRECT
96
                            | EventMask::SUBSTRUCTURE_NOTIFY
97
                            | EventMask::PROPERTY_CHANGE
98
                            | EventMask::KEY_PRESS
99
                            | EventMask::BUTTON_PRESS
100
                            | EventMask::POINTER_MOTION,
101
                    ),
102
            )?
103
            .check()?;
104
105
        connection.grab_button(
106
            false,
107
            root,
108
            EventMask::BUTTON_PRESS | EventMask::BUTTON_RELEASE,
109
            GrabMode::SYNC,
110
            GrabMode::ASYNC,
111
            x11rb::NONE,
112
            x11rb::NONE,
113
            ButtonIndex::M1,
114
            u16::from(config.modkey).into(),
115
        )?;
116
117
        connection.grab_button(
118
            false,
119
            root,
120
            EventMask::BUTTON_PRESS | EventMask::BUTTON_RELEASE,
121
            GrabMode::SYNC,
122
            GrabMode::ASYNC,
123
            x11rb::NONE,
124
            x11rb::NONE,
125
            ButtonIndex::M3,
126
            u16::from(config.modkey).into(),
127
        )?;
128
129
        let monitors = detect_monitors(&connection, &screen, root)?;
130
131
        let display = unsafe { x11::xlib::XOpenDisplay(std::ptr::null()) };
132
        if display.is_null() {
133
            return Err(WmError::X11(crate::errors::X11Error::DisplayOpenFailed));
134
        }
135
136
        let font = crate::bar::font::Font::new(display, screen_number as i32, &config.font)?;
137
138
        let mut bars = Vec::new();
139
        for monitor in monitors.iter() {
140
            let bar = Bar::new(
141
                &connection,
142
                &screen,
143
                screen_number,
144
                &config,
145
                display,
146
                &font,
147
                monitor.x as i16,
148
                monitor.y as i16,
149
                monitor.width as u16,
150
            )?;
151
            bars.push(bar);
152
        }
153
154
        let gaps_enabled = config.gaps_enabled;
155
156
        let atoms = AtomCache::new(&connection)?;
157
158
        let mut window_manager = Self {
159
            config,
160
            connection,
161
            screen_number,
162
            root,
163
            screen,
164
            windows: Vec::new(),
165
            layout: Box::new(TilingLayout),
166
            window_tags: std::collections::HashMap::new(),
167
            window_monitor: std::collections::HashMap::new(),
168
            window_geometries: std::collections::HashMap::new(),
169
            gaps_enabled,
170
            fullscreen_window: None,
171
            floating_windows: HashSet::new(),
172
            bars,
173
            monitors,
174
            selected_monitor: 0,
175
            atoms,
176
            previous_focused: None,
177
            display,
178
            font,
179
        };
180
181
        window_manager.scan_existing_windows()?;
182
        window_manager.update_bar()?;
183
184
        Ok(window_manager)
185
    }
186
187
    /**
188
     *
189
     * This function is deprecated for now, but will potentially be used in the future.
190
     *
191
     */
192
    fn _get_saved_selected_tags(
193
        connection: &RustConnection,
194
        root: Window,
195
        tag_count: usize,
196
    ) -> WmResult<TagMask> {
197
        let net_current_desktop = connection
198
            .intern_atom(false, b"_NET_CURRENT_DESKTOP")?
199
            .reply()?
200
            .atom;
201
202
        match connection
203
            .get_property(false, root, net_current_desktop, AtomEnum::CARDINAL, 0, 1)?
204
            .reply()
205
        {
206
            Ok(prop) if prop.value.len() >= 4 => {
207
                let desktop = u32::from_ne_bytes([
208
                    prop.value[0],
209
                    prop.value[1],
210
                    prop.value[2],
211
                    prop.value[3],
212
                ]);
213
                if desktop < tag_count as u32 {
214
                    let mask = tag_mask(desktop as usize);
215
                    return Ok(mask);
216
                }
217
            }
218
            Ok(_) => {}
219
            Err(e) => {
220
                eprintln!("No _NET_CURRENT_DESKTOP property ({})", e);
221
            }
222
        }
223
        Ok(tag_mask(0))
224
    }
225
226
    fn scan_existing_windows(&mut self) -> WmResult<()> {
227
        let tree = self.connection.query_tree(self.root)?.reply()?;
228
        let net_client_info = self.atoms.net_client_info;
229
        let wm_state_atom = self.atoms.wm_state;
230
231
        for &window in &tree.children {
232
            if self.bars.iter().any(|bar| bar.window() == window) {
233
                continue;
234
            }
235
236
            let Ok(attrs) = self.connection.get_window_attributes(window)?.reply() else {
237
                continue;
238
            };
239
240
            if attrs.override_redirect {
241
                continue;
242
            }
243
244
            if attrs.map_state == MapState::VIEWABLE {
245
                let tag = self.get_saved_tag(window, net_client_info)?;
246
                self.windows.push(window);
247
                self.window_tags.insert(window, tag);
248
                self.window_monitor.insert(window, self.selected_monitor);
249
                continue;
250
            }
251
252
            if attrs.map_state == MapState::UNMAPPED {
253
                let has_wm_state = self
254
                    .connection
255
                    .get_property(false, window, wm_state_atom, AtomEnum::ANY, 0, 2)?
256
                    .reply()
257
                    .is_ok_and(|prop| !prop.value.is_empty());
258
259
                if !has_wm_state {
260
                    continue;
261
                }
262
263
                let has_wm_class = self
264
                    .connection
265
                    .get_property(false, window, AtomEnum::WM_CLASS, AtomEnum::STRING, 0, 1024)?
266
                    .reply()
267
                    .is_ok_and(|prop| !prop.value.is_empty());
268
269
                if has_wm_class {
270
                    let tag = self.get_saved_tag(window, net_client_info)?;
271
                    self.windows.push(window);
272
                    self.window_tags.insert(window, tag);
273
                    self.window_monitor.insert(window, self.selected_monitor);
274
                }
275
            }
276
        }
277
278
        if let Some(&first) = self.windows.first() {
279
            self.set_focus(first)?;
280
        }
281
282
        self.apply_layout()?;
283
        Ok(())
284
    }
285
286
    fn get_saved_tag(&self, window: Window, net_client_info: Atom) -> WmResult<TagMask> {
287
        match self
288
            .connection
289
            .get_property(false, window, net_client_info, AtomEnum::CARDINAL, 0, 2)?
290
            .reply()
291
        {
292
            Ok(prop) if prop.value.len() >= 4 => {
293
                let tags = u32::from_ne_bytes([
294
                    prop.value[0],
295
                    prop.value[1],
296
                    prop.value[2],
297
                    prop.value[3],
298
                ]);
299
300
                if tags != 0 && tags < (1 << self.config.tags.len()) {
301
                    return Ok(tags);
302
                }
303
            }
304
            Ok(_) => {}
305
            Err(e) => {
306
                eprintln!("No _NET_CLIENT_INFO property ({})", e);
307
            }
308
        }
309
310
        Ok(self
311
            .monitors
312
            .get(self.selected_monitor)
313
            .map(|m| m.selected_tags)
314
            .unwrap_or(tag_mask(0)))
315
    }
316
317
    fn save_client_tag(&self, window: Window, tag: TagMask) -> WmResult<()> {
318
        let net_client_info = self.atoms.net_client_info;
319
320
        let bytes = tag.to_ne_bytes().to_vec();
321
322
        self.connection.change_property(
323
            PropMode::REPLACE,
324
            window,
325
            net_client_info,
326
            AtomEnum::CARDINAL,
327
            32,
328
            1,
329
            &bytes,
330
        )?;
331
332
        self.connection.flush()?;
333
        Ok(())
334
    }
335
336
    fn set_wm_state(&self, window: Window, state: u32) -> WmResult<()> {
337
        let wm_state_atom = self.atoms.wm_state;
338
339
        let data = [state, 0u32];
340
        let bytes: Vec<u8> = data.iter().flat_map(|&v| v.to_ne_bytes()).collect();
341
342
        self.connection.change_property(
343
            PropMode::REPLACE,
344
            window,
345
            wm_state_atom,
346
            wm_state_atom,
347
            32,
348
            2,
349
            &bytes,
350
        )?;
351
352
        self.connection.flush()?;
353
        Ok(())
354
    }
355
356
    pub fn run(&mut self) -> WmResult<bool> {
357
        println!("oxwm started on display {}", self.screen_number);
358
359
        keyboard::setup_keybinds(&self.connection, self.root, &self.config.keybindings)?;
360
        self.update_bar()?;
361
362
        loop {
363
            while let Some(event) = self.connection.poll_for_event()? {
364
                if let Some(should_restart) = self.handle_event(event)? {
365
                    return Ok(should_restart);
366
                }
367
            }
368
369
            if let Some(bar) = self.bars.get_mut(self.selected_monitor) {
370
                bar.update_blocks();
371
            }
372
373
            if self.bars.iter().any(|bar| bar.needs_redraw()) {
374
                self.update_bar()?;
375
            }
376
377
            std::thread::sleep(std::time::Duration::from_millis(100));
378
        }
379
    }
380
381
    fn toggle_floating(&mut self) -> WmResult<()> {
382
        if let Some(focused) = self
383
            .monitors
384
            .get(self.selected_monitor)
385
            .and_then(|m| m.focused_window)
386
        {
387
            if self.floating_windows.contains(&focused) {
388
                self.floating_windows.remove(&focused);
389
                self.apply_layout()?;
390
            } else {
391
                let float_width = (self.screen.width_in_pixels / 2) as u32;
392
                let float_height = (self.screen.height_in_pixels / 2) as u32;
393
394
                let border_width = self.config.border_width;
395
396
                let center_width = ((self.screen.width_in_pixels - float_width as u16) / 2) as i32;
397
                let center_height =
398
                    ((self.screen.height_in_pixels - float_height as u16) / 2) as i32;
399
400
                self.connection.configure_window(
401
                    focused,
402
                    &ConfigureWindowAux::new()
403
                        .x(center_width)
404
                        .y(center_height)
405
                        .width(float_width)
406
                        .height(float_height)
407
                        .border_width(border_width)
408
                        .stack_mode(StackMode::ABOVE),
409
                )?;
410
411
                self.floating_windows.insert(focused);
412
                self.apply_layout()?;
413
                self.connection.flush()?;
414
            }
415
        }
416
        Ok(())
417
    }
418
419
    fn smart_move_window(&mut self, direction: i32) -> WmResult<()> {
420
        let focused = match self
421
            .monitors
422
            .get(self.selected_monitor)
423
            .and_then(|m| m.focused_window)
424
        {
425
            Some(win) => win,
426
            None => return Ok(()),
427
        };
428
429
        if self.fullscreen_window == Some(focused) {
430
            return Ok(());
431
        }
432
433
        if !self.floating_windows.contains(&focused) {
434
            let float_width = (self.screen.width_in_pixels / 2) as u32;
435
            let float_height = (self.screen.height_in_pixels / 2) as u32;
436
            let border_width = self.config.border_width;
437
            let center_width = ((self.screen.width_in_pixels - float_width as u16) / 2) as i32;
438
            let center_height = ((self.screen.height_in_pixels - float_height as u16) / 2) as i32;
439
440
            self.connection.configure_window(
441
                focused,
442
                &ConfigureWindowAux::new()
443
                    .x(center_width)
444
                    .y(center_height)
445
                    .width(float_width)
446
                    .height(float_height)
447
                    .border_width(border_width)
448
                    .stack_mode(StackMode::ABOVE),
449
            )?;
450
            self.floating_windows.insert(focused);
451
        }
452
453
        let current_geom = match self.connection.get_geometry(focused)?.reply() {
454
            Ok(geom) => geom,
455
            Err(_) => return Ok(()),
456
        };
457
458
        let c_x = current_geom.x as i32;
459
        let c_y = current_geom.y as i32;
460
        let c_width = current_geom.width as i32;
461
        let c_height = current_geom.height as i32;
462
463
        let monitor = match self.monitors.get(self.selected_monitor) {
464
            Some(m) => m,
465
            None => return Ok(()),
466
        };
467
468
        let (gap_ih, gap_iv, gap_oh, gap_ov) = if self.gaps_enabled {
469
            (
470
                self.config.gap_inner_horizontal as i32,
471
                self.config.gap_inner_vertical as i32,
472
                self.config.gap_outer_horizontal as i32,
473
                self.config.gap_outer_vertical as i32,
474
            )
475
        } else {
476
            (0, 0, 0, 0)
477
        };
478
479
        let (new_x, new_y) = match direction {
480
            0 => {
481
                // UP
482
                let mut target = i32::MIN;
483
                let top = c_y;
484
                let mut ny = c_y - (monitor.height as i32 / 4);
485
486
                for &other_window in &self.windows {
487
                    if other_window == focused {
488
                        continue;
489
                    }
490
                    if !self.floating_windows.contains(&other_window) {
491
                        continue;
492
                    }
493
                    if !self.is_window_visible(other_window) {
494
                        continue;
495
                    }
496
                    let other_mon = self.window_monitor.get(&other_window).copied().unwrap_or(0);
497
                    if other_mon != self.selected_monitor {
498
                        continue;
499
                    }
500
501
                    let other_geom = match self.connection.get_geometry(other_window)?.reply() {
502
                        Ok(geom) => geom,
503
                        Err(_) => continue,
504
                    };
505
506
                    let o_x = other_geom.x as i32;
507
                    let o_y = other_geom.y as i32;
508
                    let o_width = other_geom.width as i32;
509
                    let o_height = other_geom.height as i32;
510
511
                    if c_x + c_width < o_x || c_x > o_x + o_width {
512
                        continue;
513
                    }
514
515
                    let bottom = o_y + o_height + gap_iv;
516
                    if top > bottom && ny < bottom {
517
                        target = target.max(bottom);
518
                    }
519
                }
520
521
                if target != i32::MIN {
522
                    ny = target;
523
                }
524
                ny = ny.max(monitor.y as i32 + gap_ov);
525
                (c_x, ny)
526
            }
527
            1 => {
528
                // DOWN
529
                let mut target = i32::MAX;
530
                let bottom = c_y + c_height;
531
                let mut ny = c_y + (monitor.height as i32 / 4);
532
533
                for &other_window in &self.windows {
534
                    if other_window == focused {
535
                        continue;
536
                    }
537
                    if !self.floating_windows.contains(&other_window) {
538
                        continue;
539
                    }
540
                    if !self.is_window_visible(other_window) {
541
                        continue;
542
                    }
543
                    let other_mon = self.window_monitor.get(&other_window).copied().unwrap_or(0);
544
                    if other_mon != self.selected_monitor {
545
                        continue;
546
                    }
547
548
                    let other_geom = match self.connection.get_geometry(other_window)?.reply() {
549
                        Ok(geom) => geom,
550
                        Err(_) => continue,
551
                    };
552
553
                    let o_x = other_geom.x as i32;
554
                    let o_y = other_geom.y as i32;
555
                    let o_width = other_geom.width as i32;
556
557
                    if c_x + c_width < o_x || c_x > o_x + o_width {
558
                        continue;
559
                    }
560
561
                    let top = o_y - gap_iv;
562
                    if bottom < top && (ny + c_height) > top {
563
                        target = target.min(top - c_height);
564
                    }
565
                }
566
567
                if target != i32::MAX {
568
                    ny = target;
569
                }
570
                ny = ny.min(monitor.y as i32 + monitor.height as i32 - c_height - gap_ov);
571
                (c_x, ny)
572
            }
573
            2 => {
574
                // LEFT
575
                let mut target = i32::MIN;
576
                let left = c_x;
577
                let mut nx = c_x - (monitor.width as i32 / 6);
578
579
                for &other_window in &self.windows {
580
                    if other_window == focused {
581
                        continue;
582
                    }
583
                    if !self.floating_windows.contains(&other_window) {
584
                        continue;
585
                    }
586
                    if !self.is_window_visible(other_window) {
587
                        continue;
588
                    }
589
                    let other_mon = self.window_monitor.get(&other_window).copied().unwrap_or(0);
590
                    if other_mon != self.selected_monitor {
591
                        continue;
592
                    }
593
594
                    let other_geom = match self.connection.get_geometry(other_window)?.reply() {
595
                        Ok(geom) => geom,
596
                        Err(_) => continue,
597
                    };
598
599
                    let o_x = other_geom.x as i32;
600
                    let o_y = other_geom.y as i32;
601
                    let o_width = other_geom.width as i32;
602
                    let o_height = other_geom.height as i32;
603
604
                    if c_y + c_height < o_y || c_y > o_y + o_height {
605
                        continue;
606
                    }
607
608
                    let right = o_x + o_width + gap_ih;
609
                    if left > right && nx < right {
610
                        target = target.max(right);
611
                    }
612
                }
613
614
                if target != i32::MIN {
615
                    nx = target;
616
                }
617
                nx = nx.max(monitor.x as i32 + gap_oh);
618
                (nx, c_y)
619
            }
620
            3 => {
621
                // RIGHT
622
                let mut target = i32::MAX;
623
                let right = c_x + c_width;
624
                let mut nx = c_x + (monitor.width as i32 / 6);
625
626
                for &other_window in &self.windows {
627
                    if other_window == focused {
628
                        continue;
629
                    }
630
                    if !self.floating_windows.contains(&other_window) {
631
                        continue;
632
                    }
633
                    if !self.is_window_visible(other_window) {
634
                        continue;
635
                    }
636
                    let other_mon = self.window_monitor.get(&other_window).copied().unwrap_or(0);
637
                    if other_mon != self.selected_monitor {
638
                        continue;
639
                    }
640
641
                    let other_geom = match self.connection.get_geometry(other_window)?.reply() {
642
                        Ok(geom) => geom,
643
                        Err(_) => continue,
644
                    };
645
646
                    let o_x = other_geom.x as i32;
647
                    let o_y = other_geom.y as i32;
648
                    let o_height = other_geom.height as i32;
649
650
                    if c_y + c_height < o_y || c_y > o_y + o_height {
651
                        continue;
652
                    }
653
654
                    let left = o_x - gap_ih;
655
                    if right < left && (nx + c_width) > left {
656
                        target = target.min(left - c_width);
657
                    }
658
                }
659
660
                if target != i32::MAX {
661
                    nx = target;
662
                }
663
                nx = nx.min(monitor.x as i32 + monitor.width as i32 - c_width - gap_oh);
664
                (nx, c_y)
665
            }
666
            _ => return Ok(()),
667
        };
668
669
        self.connection.configure_window(
670
            focused,
671
            &ConfigureWindowAux::new()
672
                .x(new_x)
673
                .y(new_y)
674
                .stack_mode(StackMode::ABOVE),
675
        )?;
676
677
        self.connection.flush()?;
678
        Ok(())
679
    }
680
681
    fn exchange_client(&mut self, direction: i32) -> WmResult<()> {
682
        let focused = match self
683
            .monitors
684
            .get(self.selected_monitor)
685
            .and_then(|m| m.focused_window)
686
        {
687
            Some(win) => win,
688
            None => return Ok(()),
689
        };
690
691
        if self.fullscreen_window == Some(focused) || self.floating_windows.contains(&focused) {
692
            return Ok(());
693
        }
694
695
        let visible = self.visible_windows();
696
        if visible.len() < 2 {
697
            return Ok(());
698
        }
699
700
        let current_idx = match visible.iter().position(|&w| w == focused) {
701
            Some(idx) => idx,
702
            None => return Ok(()),
703
        };
704
705
        let target_idx = match direction {
706
            0 | 2 => {
707
                // UP or LEFT - previous in stack
708
                if current_idx == 0 {
709
                    visible.len() - 1
710
                } else {
711
                    current_idx - 1
712
                }
713
            }
714
            1 | 3 => {
715
                // DOWN or RIGHT - next in stack
716
                (current_idx + 1) % visible.len()
717
            }
718
            _ => return Ok(()),
719
        };
720
721
        let target = visible[target_idx];
722
723
        let focused_pos = self.windows.iter().position(|&w| w == focused);
724
        let target_pos = self.windows.iter().position(|&w| w == target);
725
726
        if let (Some(f_pos), Some(t_pos)) = (focused_pos, target_pos) {
727
            self.windows.swap(f_pos, t_pos);
728
729
            self.apply_layout()?;
730
731
            self.set_focus(focused)?;
732
733
            if let Ok(geometry) = self.connection.get_geometry(focused)?.reply() {
734
                self.connection.warp_pointer(
735
                    x11rb::NONE,
736
                    focused,
737
                    0,
738
                    0,
739
                    0,
740
                    0,
741
                    geometry.width as i16 / 2,
742
                    geometry.height as i16 / 2,
743
                )?;
744
            }
745
        }
746
747
        Ok(())
748
    }
749
750
    fn toggle_fullscreen(&mut self) -> WmResult<()> {
751
        if let Some(focused) = self
752
            .monitors
753
            .get(self.selected_monitor)
754
            .and_then(|m| m.focused_window)
755
        {
756
            if self.fullscreen_window == Some(focused) {
757
                self.fullscreen_window = None;
758
759
                for bar in &self.bars {
760
                    self.connection.map_window(bar.window())?;
761
                }
762
763
                self.apply_layout()?;
764
            } else {
765
                self.fullscreen_window = Some(focused);
766
767
                if let Some(bar) = self.bars.get(self.selected_monitor) {
768
                    self.connection.unmap_window(bar.window())?;
769
                }
770
771
                let monitor = &self.monitors[self.selected_monitor];
772
                let screen_width = monitor.width;
773
                let screen_height = monitor.height;
774
775
                self.connection.configure_window(
776
                    focused,
777
                    &ConfigureWindowAux::new()
778
                        .x(monitor.x)
779
                        .y(monitor.y)
780
                        .width(screen_width)
781
                        .height(screen_height)
782
                        .border_width(0)
783
                        .stack_mode(StackMode::ABOVE),
784
                )?;
785
786
                self.connection.flush()?;
787
            }
788
        }
789
        Ok(())
790
    }
791
792
    fn get_layout_symbol(&self) -> String {
793
        let layout_name = self.layout.name();
794
        self.config
795
            .layout_symbols
796
            .iter()
797
            .find(|l| l.name == layout_name)
798
            .map(|l| l.symbol.clone())
799
            .unwrap_or_else(|| self.layout.symbol().to_string())
800
    }
801
802
    fn update_bar(&mut self) -> WmResult<()> {
803
        let layout_symbol = self.get_layout_symbol();
804
805
        for (monitor_index, monitor) in self.monitors.iter().enumerate() {
806
            if let Some(bar) = self.bars.get_mut(monitor_index) {
807
                let mut occupied_tags: TagMask = 0;
808
                for (&window, &tags) in &self.window_tags {
809
                    if self.window_monitor.get(&window).copied().unwrap_or(0) == monitor_index {
810
                        occupied_tags |= tags;
811
                    }
812
                }
813
814
                let draw_blocks = monitor_index == self.selected_monitor;
815
                bar.invalidate();
816
                bar.draw(
817
                    &self.connection,
818
                    &self.font,
819
                    self.display,
820
                    monitor.selected_tags,
821
                    occupied_tags,
822
                    draw_blocks,
823
                    &layout_symbol,
824
                )?;
825
            }
826
        }
827
        Ok(())
828
    }
829
830
    fn handle_key_action(&mut self, action: KeyAction, arg: &Arg) -> WmResult<()> {
831
        match action {
832
            KeyAction::Spawn => handlers::handle_spawn_action(action, arg)?,
833
            KeyAction::KillClient => {
834
                if let Some(focused) = self
835
                    .monitors
836
                    .get(self.selected_monitor)
837
                    .and_then(|m| m.focused_window)
838
                {
839
                    match self.connection.kill_client(focused) {
840
                        Ok(_) => {
841
                            self.connection.flush()?;
842
                        }
843
                        Err(e) => {
844
                            eprintln!("Failed to kill window {}: {}", focused, e);
845
                        }
846
                    }
847
                }
848
            }
849
            KeyAction::ToggleFullScreen => {
850
                self.toggle_fullscreen()?;
851
            }
852
            KeyAction::ChangeLayout => {
853
                if let Arg::Str(layout_name) = arg {
854
                    match layout_from_str(layout_name) {
855
                        Ok(layout) => {
856
                            self.layout = layout;
857
                            self.apply_layout()?;
858
                            self.update_bar()?;
859
                        }
860
                        Err(e) => eprintln!("Failed to change layout: {}", e),
861
                    }
862
                }
863
            }
864
            KeyAction::CycleLayout => {
865
                let current_name = self.layout.name();
866
                let next_name = next_layout(current_name);
867
                match layout_from_str(next_name) {
868
                    Ok(layout) => {
869
                        self.layout = layout;
870
                        self.apply_layout()?;
871
                        self.update_bar()?;
872
                    }
873
                    Err(e) => eprintln!("Failed to cycle layout: {}", e),
874
                }
875
            }
876
            KeyAction::ToggleFloating => {
877
                self.toggle_floating()?;
878
            }
879
880
            KeyAction::SmartMoveWin => {
881
                if let Arg::Int(direction) = arg {
882
                    self.smart_move_window(*direction)?;
883
                }
884
            }
885
886
            KeyAction::ExchangeClient => {
887
                if let Arg::Int(direction) = arg {
888
                    self.exchange_client(*direction)?;
889
                }
890
            }
891
892
            KeyAction::FocusStack => {
893
                if let Arg::Int(direction) = arg {
894
                    self.cycle_focus(*direction)?;
895
                }
896
            }
897
            KeyAction::Quit | KeyAction::Restart => {
898
                // Handled in handle_event
899
            }
900
            KeyAction::Recompile => {
901
                match std::process::Command::new("oxwm")
902
                    .arg("--recompile")
903
                    .spawn()
904
                {
905
                    Ok(_) => eprintln!("Recompiling in background"),
906
                    Err(e) => eprintln!("Failed to spawn recompile: {}", e),
907
                }
908
            }
909
            KeyAction::ViewTag => {
910
                if let Arg::Int(tag_index) = arg {
911
                    self.view_tag(*tag_index as usize)?;
912
                }
913
            }
914
            KeyAction::MoveToTag => {
915
                if let Arg::Int(tag_index) = arg {
916
                    self.move_to_tag(*tag_index as usize)?;
917
                }
918
            }
919
            KeyAction::ToggleGaps => {
920
                self.gaps_enabled = !self.gaps_enabled;
921
                self.apply_layout()?;
922
            }
923
            KeyAction::FocusMonitor => {
924
                if let Arg::Int(direction) = arg {
925
                    self.focus_monitor(*direction)?;
926
                }
927
            }
928
            KeyAction::None => {}
929
        }
930
        Ok(())
931
    }
932
933
    fn focus_monitor(&mut self, direction: i32) -> WmResult<()> {
934
        if self.monitors.is_empty() {
935
            return Ok(());
936
        }
937
938
        let new_monitor = if direction > 0 {
939
            (self.selected_monitor + 1) % self.monitors.len()
940
        } else {
941
            (self.selected_monitor + self.monitors.len() - 1) % self.monitors.len()
942
        };
943
944
        if new_monitor == self.selected_monitor {
945
            return Ok(());
946
        }
947
948
        self.selected_monitor = new_monitor;
949
950
        self.update_bar()?;
951
952
        let visible = self.visible_windows_on_monitor(new_monitor);
953
        if let Some(&win) = visible.first() {
954
            self.set_focus(win)?;
955
        }
956
957
        Ok(())
958
    }
959
960
    fn is_window_visible(&self, window: Window) -> bool {
961
        let window_mon = self.window_monitor.get(&window).copied().unwrap_or(0);
962
963
        if let Some(&tags) = self.window_tags.get(&window) {
964
            let monitor = self.monitors.get(window_mon);
965
            let selected_tags = monitor.map(|m| m.selected_tags).unwrap_or(0);
966
            (tags & selected_tags) != 0
967
        } else {
968
            false
969
        }
970
    }
971
972
    fn visible_windows(&self) -> Vec<Window> {
973
        self.windows
974
            .iter()
975
            .filter(|&&w| self.is_window_visible(w))
976
            .copied()
977
            .collect()
978
    }
979
980
    fn visible_windows_on_monitor(&self, monitor_index: usize) -> Vec<Window> {
981
        self.windows
982
            .iter()
983
            .filter(|&&w| {
984
                let window_mon = self.window_monitor.get(&w).copied().unwrap_or(0);
985
                if window_mon != monitor_index {
986
                    return false;
987
                }
988
                if let Some(&tags) = self.window_tags.get(&w) {
989
                    let monitor = self.monitors.get(monitor_index);
990
                    let selected_tags = monitor.map(|m| m.selected_tags).unwrap_or(0);
991
                    (tags & selected_tags) != 0
992
                } else {
993
                    false
994
                }
995
            })
996
            .copied()
997
            .collect()
998
    }
999
1000
    fn get_monitor_at_point(&self, x: i32, y: i32) -> Option<usize> {
1001
        self.monitors
1002
            .iter()
1003
            .position(|mon| mon.contains_point(x, y))
1004
    }
1005
1006
    fn update_window_visibility(&self) -> WmResult<()> {
1007
        for &window in &self.windows {
1008
            if self.is_window_visible(window) {
1009
                self.connection.map_window(window)?;
1010
            } else {
1011
                self.connection.unmap_window(window)?;
1012
            }
1013
        }
1014
        self.connection.flush()?;
1015
        Ok(())
1016
    }
1017
1018
    pub fn view_tag(&mut self, tag_index: usize) -> WmResult<()> {
1019
        if tag_index >= self.config.tags.len() {
1020
            return Ok(());
1021
        }
1022
1023
        if self.fullscreen_window.is_some() {
1024
            self.fullscreen_window = None;
1025
            for bar in &self.bars {
1026
                self.connection.map_window(bar.window())?;
1027
            }
1028
        }
1029
1030
        if let Some(monitor) = self.monitors.get_mut(self.selected_monitor) {
1031
            monitor.selected_tags = tag_mask(tag_index);
1032
        }
1033
1034
        self.save_selected_tags()?;
1035
1036
        self.update_window_visibility()?;
1037
        self.apply_layout()?;
1038
        self.update_bar()?;
1039
1040
        let visible = self.visible_windows_on_monitor(self.selected_monitor);
1041
        if let Some(&win) = visible.first() {
1042
            self.set_focus(win)?;
1043
        }
1044
1045
        Ok(())
1046
    }
1047
1048
    fn save_selected_tags(&self) -> WmResult<()> {
1049
        let net_current_desktop = self.atoms.net_current_desktop;
1050
1051
        let selected_tags = self
1052
            .monitors
1053
            .get(self.selected_monitor)
1054
            .map(|m| m.selected_tags)
1055
            .unwrap_or(tag_mask(0));
1056
        let desktop = selected_tags.trailing_zeros();
1057
1058
        let bytes = (desktop as u32).to_ne_bytes();
1059
        self.connection.change_property(
1060
            PropMode::REPLACE,
1061
            self.root,
1062
            net_current_desktop,
1063
            AtomEnum::CARDINAL,
1064
            32,
1065
            1,
1066
            &bytes,
1067
        )?;
1068
1069
        self.connection.flush()?;
1070
        Ok(())
1071
    }
1072
1073
    pub fn move_to_tag(&mut self, tag_index: usize) -> WmResult<()> {
1074
        if tag_index >= self.config.tags.len() {
1075
            return Ok(());
1076
        }
1077
1078
        if let Some(focused) = self
1079
            .monitors
1080
            .get(self.selected_monitor)
1081
            .and_then(|m| m.focused_window)
1082
        {
1083
            let mask = tag_mask(tag_index);
1084
            self.window_tags.insert(focused, mask);
1085
1086
            let _ = self.save_client_tag(focused, mask);
1087
1088
            self.update_window_visibility()?;
1089
            self.apply_layout()?;
1090
            self.update_bar()?;
1091
        }
1092
1093
        Ok(())
1094
    }
1095
1096
    pub fn cycle_focus(&mut self, direction: i32) -> WmResult<()> {
1097
        let visible = self.visible_windows();
1098
1099
        if visible.is_empty() {
1100
            return Ok(());
1101
        }
1102
1103
        let current = self
1104
            .monitors
1105
            .get(self.selected_monitor)
1106
            .and_then(|m| m.focused_window);
1107
1108
        let next_window = if let Some(current) = current {
1109
            if let Some(current_index) = visible.iter().position(|&w| w == current) {
1110
                let next_index = if direction > 0 {
1111
                    (current_index + 1) % visible.len()
1112
                } else {
1113
                    (current_index + visible.len() - 1) % visible.len()
1114
                };
1115
                visible[next_index]
1116
            } else {
1117
                visible[0]
1118
            }
1119
        } else {
1120
            visible[0]
1121
        };
1122
1123
        self.set_focus(next_window)?;
1124
        Ok(())
1125
    }
1126
1127
    pub fn set_focus(&mut self, window: Window) -> WmResult<()> {
1128
        let old_focused = self.previous_focused;
1129
1130
        if let Some(monitor) = self.monitors.get_mut(self.selected_monitor) {
1131
            monitor.focused_window = Some(window);
1132
        }
1133
1134
        self.connection
1135
            .set_input_focus(InputFocus::POINTER_ROOT, window, x11rb::CURRENT_TIME)?;
1136
        self.connection.flush()?;
1137
1138
        self.update_focus_visuals(old_focused, window)?;
1139
        self.previous_focused = Some(window);
1140
        Ok(())
1141
    }
1142
1143
    fn update_focus_visuals(
1144
        &self,
1145
        old_focused: Option<Window>,
1146
        new_focused: Window,
1147
    ) -> WmResult<()> {
1148
        if let Some(old_win) = old_focused {
1149
            if old_win != new_focused {
1150
                self.connection.configure_window(
1151
                    old_win,
1152
                    &ConfigureWindowAux::new().border_width(self.config.border_width),
1153
                )?;
1154
1155
                self.connection.change_window_attributes(
1156
                    old_win,
1157
                    &ChangeWindowAttributesAux::new().border_pixel(self.config.border_unfocused),
1158
                )?;
1159
            }
1160
        }
1161
1162
        self.connection.configure_window(
1163
            new_focused,
1164
            &ConfigureWindowAux::new().border_width(self.config.border_width),
1165
        )?;
1166
1167
        self.connection.change_window_attributes(
1168
            new_focused,
1169
            &ChangeWindowAttributesAux::new().border_pixel(self.config.border_focused),
1170
        )?;
1171
1172
        self.connection.flush()?;
1173
        Ok(())
1174
    }
1175
1176
    fn move_mouse(&mut self, window: Window) -> WmResult<()> {
1177
        self.floating_windows.insert(window);
1178
1179
        let geometry = self.connection.get_geometry(window)?.reply()?;
1180
1181
        self.connection
1182
            .grab_pointer(
1183
                false,
1184
                self.root,
1185
                (EventMask::POINTER_MOTION | EventMask::BUTTON_RELEASE).into(),
1186
                GrabMode::ASYNC,
1187
                GrabMode::ASYNC,
1188
                x11rb::NONE,
1189
                x11rb::NONE,
1190
                x11rb::CURRENT_TIME,
1191
            )?
1192
            .reply()?;
1193
1194
        let pointer = self.connection.query_pointer(self.root)?.reply()?;
1195
        let (start_x, start_y) = (pointer.root_x, pointer.root_y);
1196
1197
        self.connection.configure_window(
1198
            window,
1199
            &ConfigureWindowAux::new().stack_mode(StackMode::ABOVE),
1200
        )?;
1201
1202
        loop {
1203
            let event = self.connection.wait_for_event()?;
1204
            match event {
1205
                Event::MotionNotify(e) => {
1206
                    let new_x = geometry.x + (e.root_x - start_x);
1207
                    let new_y = geometry.y + (e.root_y - start_y);
1208
                    self.connection.configure_window(
1209
                        window,
1210
                        &ConfigureWindowAux::new().x(new_x as i32).y(new_y as i32),
1211
                    )?;
1212
                    self.connection.flush()?;
1213
                }
1214
                Event::ButtonRelease(_) => break,
1215
                _ => {}
1216
            }
1217
        }
1218
1219
        self.connection
1220
            .ungrab_pointer(x11rb::CURRENT_TIME)?
1221
            .check()?;
1222
        self.connection
1223
            .allow_events(Allow::REPLAY_POINTER, x11rb::CURRENT_TIME)?
1224
            .check()?;
1225
1226
        Ok(())
1227
    }
1228
1229
    fn resize_mouse(&mut self, window: Window) -> WmResult<()> {
1230
        self.floating_windows.insert(window);
1231
1232
        let geometry = self.connection.get_geometry(window)?.reply()?;
1233
1234
        self.connection.warp_pointer(
1235
            x11rb::NONE,
1236
            window,
1237
            0,
1238
            0,
1239
            0,
1240
            0,
1241
            geometry.width as i16,
1242
            geometry.height as i16,
1243
        )?;
1244
1245
        self.connection
1246
            .grab_pointer(
1247
                false,
1248
                self.root,
1249
                (EventMask::POINTER_MOTION | EventMask::BUTTON_RELEASE).into(),
1250
                GrabMode::ASYNC,
1251
                GrabMode::ASYNC,
1252
                x11rb::NONE,
1253
                x11rb::NONE,
1254
                x11rb::CURRENT_TIME,
1255
            )?
1256
            .reply()?;
1257
1258
        self.connection.configure_window(
1259
            window,
1260
            &ConfigureWindowAux::new().stack_mode(StackMode::ABOVE),
1261
        )?;
1262
1263
        loop {
1264
            let event = self.connection.wait_for_event()?;
1265
            match event {
1266
                Event::MotionNotify(e) => {
1267
                    let new_width = (e.root_x - geometry.x).max(1) as u32;
1268
                    let new_height = (e.root_y - geometry.y).max(1) as u32;
1269
1270
                    self.connection.configure_window(
1271
                        window,
1272
                        &ConfigureWindowAux::new()
1273
                            .width(new_width)
1274
                            .height(new_height),
1275
                    )?;
1276
                    self.connection.flush()?;
1277
                }
1278
                Event::ButtonRelease(_) => break,
1279
                _ => {}
1280
            }
1281
        }
1282
1283
        self.connection
1284
            .ungrab_pointer(x11rb::CURRENT_TIME)?
1285
            .check()?;
1286
        self.connection
1287
            .allow_events(Allow::REPLAY_POINTER, x11rb::CURRENT_TIME)?
1288
            .check()?;
1289
1290
        Ok(())
1291
    }
1292
1293
    fn handle_event(&mut self, event: Event) -> WmResult<Option<bool>> {
1294
        match event {
1295
            Event::MapRequest(event) => {
1296
                let attrs = match self.connection.get_window_attributes(event.window)?.reply() {
1297
                    Ok(attrs) => attrs,
1298
                    Err(_) => return Ok(None),
1299
                };
1300
1301
                if attrs.override_redirect {
1302
                    return Ok(None);
1303
                }
1304
1305
                if self.windows.contains(&event.window) {
1306
                    return Ok(None);
1307
                }
1308
1309
                if let Ok(geom) = self.connection.get_geometry(event.window)?.reply() {
1310
                    self.window_geometries
1311
                        .insert(event.window, (geom.x, geom.y, geom.width, geom.height));
1312
                }
1313
1314
                self.connection.map_window(event.window)?;
1315
                self.connection.change_window_attributes(
1316
                    event.window,
1317
                    &ChangeWindowAttributesAux::new().event_mask(EventMask::ENTER_WINDOW),
1318
                )?;
1319
1320
                let selected_tags = self
1321
                    .monitors
1322
                    .get(self.selected_monitor)
1323
                    .map(|m| m.selected_tags)
1324
                    .unwrap_or(tag_mask(0));
1325
1326
                self.windows.push(event.window);
1327
                self.window_tags.insert(event.window, selected_tags);
1328
                self.window_monitor
1329
                    .insert(event.window, self.selected_monitor);
1330
                self.set_wm_state(event.window, 1)?;
1331
                let _ = self.save_client_tag(event.window, selected_tags);
1332
1333
                self.apply_layout()?;
1334
                self.update_bar()?;
1335
                self.set_focus(event.window)?;
1336
            }
1337
            Event::UnmapNotify(event) => {
1338
                if self.windows.contains(&event.window) && self.is_window_visible(event.window) {
1339
                    self.remove_window(event.window)?;
1340
                }
1341
            }
1342
            Event::DestroyNotify(event) => {
1343
                if self.windows.contains(&event.window) {
1344
                    self.remove_window(event.window)?;
1345
                }
1346
            }
1347
            Event::EnterNotify(event) => {
1348
                if event.mode != x11rb::protocol::xproto::NotifyMode::NORMAL {
1349
                    return Ok(None);
1350
                }
1351
                if self.windows.contains(&event.event) {
1352
                    self.set_focus(event.event)?;
1353
                }
1354
            }
1355
            Event::MotionNotify(event) => {
1356
                if event.event != self.root {
1357
                    return Ok(None);
1358
                }
1359
1360
                if let Some(monitor_index) =
1361
                    self.get_monitor_at_point(event.root_x as i32, event.root_y as i32)
1362
                {
1363
                    if monitor_index != self.selected_monitor {
1364
                        self.selected_monitor = monitor_index;
1365
                        self.update_bar()?;
1366
1367
                        let visible = self.visible_windows_on_monitor(monitor_index);
1368
                        if let Some(&win) = visible.first() {
1369
                            self.set_focus(win)?;
1370
                        }
1371
                    }
1372
                }
1373
            }
1374
            Event::KeyPress(event) => {
1375
                let (action, arg) = keyboard::handle_key_press(event, &self.config.keybindings);
1376
                match action {
1377
                    KeyAction::Quit => return Ok(Some(false)),
1378
                    KeyAction::Restart => return Ok(Some(true)),
1379
                    _ => self.handle_key_action(action, &arg)?,
1380
                }
1381
            }
1382
            Event::ButtonPress(event) => {
1383
                let is_bar_click = self
1384
                    .bars
1385
                    .iter()
1386
                    .enumerate()
1387
                    .find(|(_, bar)| bar.window() == event.event);
1388
1389
                if let Some((monitor_index, bar)) = is_bar_click {
1390
                    if let Some(tag_index) = bar.handle_click(event.event_x) {
1391
                        if monitor_index != self.selected_monitor {
1392
                            self.selected_monitor = monitor_index;
1393
                        }
1394
                        self.view_tag(tag_index)?;
1395
                    }
1396
                } else if event.child != x11rb::NONE {
1397
                    self.set_focus(event.child)?;
1398
1399
                    if event.detail == ButtonIndex::M1.into() {
1400
                        self.move_mouse(event.child)?;
1401
                    } else if event.detail == ButtonIndex::M3.into() {
1402
                        self.resize_mouse(event.child)?;
1403
                    }
1404
                }
1405
            }
1406
            Event::Expose(event) => {
1407
                for bar in &mut self.bars {
1408
                    if event.window == bar.window() {
1409
                        bar.invalidate();
1410
                        self.update_bar()?;
1411
                        break;
1412
                    }
1413
                }
1414
            }
1415
            _ => {}
1416
        }
1417
        Ok(None)
1418
    }
1419
1420
    fn apply_layout(&self) -> WmResult<()> {
1421
        if self.fullscreen_window.is_some() {
1422
            return Ok(());
1423
        }
1424
1425
        if self.layout.name() == LayoutType::Normie.as_str() {
1426
            return Ok(());
1427
        }
1428
1429
        let border_width = self.config.border_width;
1430
1431
        let gaps = if self.gaps_enabled {
1432
            GapConfig {
1433
                inner_horizontal: self.config.gap_inner_horizontal,
1434
                inner_vertical: self.config.gap_inner_vertical,
1435
                outer_horizontal: self.config.gap_outer_horizontal,
1436
                outer_vertical: self.config.gap_outer_vertical,
1437
            }
1438
        } else {
1439
            GapConfig {
1440
                inner_horizontal: 0,
1441
                inner_vertical: 0,
1442
                outer_horizontal: 0,
1443
                outer_vertical: 0,
1444
            }
1445
        };
1446
1447
        for (monitor_index, monitor) in self.monitors.iter().enumerate() {
1448
            let visible: Vec<Window> = self
1449
                .windows
1450
                .iter()
1451
                .filter(|&&w| {
1452
                    let window_mon = self.window_monitor.get(&w).copied().unwrap_or(0);
1453
                    if window_mon != monitor_index {
1454
                        return false;
1455
                    }
1456
                    if self.floating_windows.contains(&w) {
1457
                        return false;
1458
                    }
1459
                    if let Some(&tags) = self.window_tags.get(&w) {
1460
                        (tags & monitor.selected_tags) != 0
1461
                    } else {
1462
                        false
1463
                    }
1464
                })
1465
                .copied()
1466
                .collect();
1467
1468
            let bar_height = self
1469
                .bars
1470
                .get(monitor_index)
1471
                .map(|b| b.height() as u32)
1472
                .unwrap_or(0);
1473
            let usable_height = monitor.height.saturating_sub(bar_height);
1474
1475
            let geometries = self
1476
                .layout
1477
                .arrange(&visible, monitor.width, usable_height, &gaps);
1478
1479
            for (window, geometry) in visible.iter().zip(geometries.iter()) {
1480
                let adjusted_width = geometry.width.saturating_sub(2 * border_width);
1481
                let adjusted_height = geometry.height.saturating_sub(2 * border_width);
1482
1483
                let adjusted_x = geometry.x_coordinate + monitor.x;
1484
                let adjusted_y = geometry.y_coordinate + monitor.y + bar_height as i32;
1485
1486
                self.connection.configure_window(
1487
                    *window,
1488
                    &ConfigureWindowAux::new()
1489
                        .x(adjusted_x)
1490
                        .y(adjusted_y)
1491
                        .width(adjusted_width)
1492
                        .height(adjusted_height),
1493
                )?;
1494
            }
1495
        }
1496
1497
        self.connection.flush()?;
1498
        Ok(())
1499
    }
1500
1501
    pub fn change_layout<L: Layout + 'static>(&mut self, new_layout: L) -> WmResult<()> {
1502
        self.layout = Box::new(new_layout);
1503
        self.apply_layout()?;
1504
        Ok(())
1505
    }
1506
1507
    fn remove_window(&mut self, window: Window) -> WmResult<()> {
1508
        let initial_count = self.windows.len();
1509
        self.windows.retain(|&w| w != window);
1510
        self.window_tags.remove(&window);
1511
        self.window_monitor.remove(&window);
1512
        self.window_geometries.remove(&window);
1513
        self.floating_windows.remove(&window);
1514
1515
        if self.fullscreen_window == Some(window) {
1516
            self.fullscreen_window = None;
1517
            for bar in &self.bars {
1518
                self.connection.map_window(bar.window())?;
1519
            }
1520
        }
1521
1522
        if self.windows.len() < initial_count {
1523
            let focused = self
1524
                .monitors
1525
                .get(self.selected_monitor)
1526
                .and_then(|m| m.focused_window);
1527
            if focused == Some(window) {
1528
                let visible = self.visible_windows_on_monitor(self.selected_monitor);
1529
                if let Some(&new_win) = visible.last() {
1530
                    self.set_focus(new_win)?;
1531
                } else if let Some(monitor) = self.monitors.get_mut(self.selected_monitor) {
1532
                    monitor.focused_window = None;
1533
                }
1534
            }
1535
1536
            self.apply_layout()?;
1537
            self.update_bar()?;
1538
        }
1539
        Ok(())
1540
    }
1541
}