oxwm

https://git.tonybtw.com/oxwm.git git://git.tonybtw.com/oxwm.git
153,745 bytes raw
1
use crate::Config;
2
use crate::bar::Bar;
3
use crate::client::{Client, TagMask};
4
use crate::errors::{ConfigError, WmError};
5
use crate::keyboard::{self, Arg, KeyAction, handlers};
6
use crate::layout::GapConfig;
7
use crate::layout::tiling::TilingLayout;
8
use crate::layout::{Layout, LayoutBox, LayoutType, layout_from_str, next_layout};
9
use crate::monitor::{Monitor, detect_monitors};
10
use crate::overlay::{ErrorOverlay, KeybindOverlay, Overlay};
11
use std::collections::{HashMap, HashSet};
12
use x11rb::cursor::Handle as CursorHandle;
13
14
use x11rb::connection::Connection;
15
use x11rb::protocol::Event;
16
use x11rb::protocol::xproto::*;
17
use x11rb::rust_connection::RustConnection;
18
19
enum Control {
20
    Continue,
21
    Quit,
22
}
23
24
pub fn tag_mask(tag: usize) -> TagMask {
25
    1 << tag
26
}
27
28
pub fn unmask_tag(mask: TagMask) -> usize {
29
    mask.trailing_zeros() as usize
30
}
31
32
struct AtomCache {
33
    net_supported: Atom,
34
    net_supporting_wm_check: Atom,
35
    net_current_desktop: Atom,
36
    net_client_info: Atom,
37
    wm_state: Atom,
38
    wm_protocols: Atom,
39
    wm_delete_window: Atom,
40
    net_wm_state: Atom,
41
    net_wm_state_fullscreen: Atom,
42
    net_wm_window_type: Atom,
43
    net_wm_window_type_dialog: Atom,
44
    wm_name: Atom,
45
    net_wm_name: Atom,
46
    utf8_string: Atom,
47
    net_active_window: Atom,
48
    wm_take_focus: Atom,
49
    net_client_list: Atom,
50
}
51
52
impl AtomCache {
53
    fn new(connection: &RustConnection) -> WmResult<Self> {
54
        let net_supported = connection
55
            .intern_atom(false, b"_NET_SUPPORTED")?
56
            .reply()?
57
            .atom;
58
59
        let net_supporting_wm_check = connection
60
            .intern_atom(false, b"_NET_SUPPORTING_WM_CHECK")?
61
            .reply()?
62
            .atom;
63
64
        let net_current_desktop = connection
65
            .intern_atom(false, b"_NET_CURRENT_DESKTOP")?
66
            .reply()?
67
            .atom;
68
69
        let net_client_info = connection
70
            .intern_atom(false, b"_NET_CLIENT_INFO")?
71
            .reply()?
72
            .atom;
73
74
        let wm_state = connection.intern_atom(false, b"WM_STATE")?.reply()?.atom;
75
76
        let wm_protocols = connection
77
            .intern_atom(false, b"WM_PROTOCOLS")?
78
            .reply()?
79
            .atom;
80
81
        let wm_delete_window = connection
82
            .intern_atom(false, b"WM_DELETE_WINDOW")?
83
            .reply()?
84
            .atom;
85
86
        let net_wm_state = connection
87
            .intern_atom(false, b"_NET_WM_STATE")?
88
            .reply()?
89
            .atom;
90
91
        let net_wm_state_fullscreen = connection
92
            .intern_atom(false, b"_NET_WM_STATE_FULLSCREEN")?
93
            .reply()?
94
            .atom;
95
96
        let net_wm_window_type = connection
97
            .intern_atom(false, b"_NET_WM_WINDOW_TYPE")?
98
            .reply()?
99
            .atom;
100
101
        let net_wm_window_type_dialog = connection
102
            .intern_atom(false, b"_NET_WM_WINDOW_TYPE_DIALOG")?
103
            .reply()?
104
            .atom;
105
106
        let wm_name = AtomEnum::WM_NAME.into();
107
        let net_wm_name = connection
108
            .intern_atom(false, b"_NET_WM_NAME")?
109
            .reply()?
110
            .atom;
111
        let utf8_string = connection.intern_atom(false, b"UTF8_STRING")?.reply()?.atom;
112
        let net_active_window = connection
113
            .intern_atom(false, b"_NET_ACTIVE_WINDOW")?
114
            .reply()?
115
            .atom;
116
117
        let wm_take_focus = connection
118
            .intern_atom(false, b"WM_TAKE_FOCUS")?
119
            .reply()?
120
            .atom;
121
122
        let net_client_list = connection
123
            .intern_atom(false, b"_NET_CLIENT_LIST")?
124
            .reply()?
125
            .atom;
126
127
        Ok(Self {
128
            net_supported,
129
            net_supporting_wm_check,
130
            net_current_desktop,
131
            net_client_info,
132
            wm_state,
133
            wm_protocols,
134
            wm_delete_window,
135
            net_wm_state,
136
            net_wm_state_fullscreen,
137
            net_wm_window_type,
138
            net_wm_window_type_dialog,
139
            wm_name,
140
            net_wm_name,
141
            utf8_string,
142
            net_active_window,
143
            wm_take_focus,
144
            net_client_list,
145
        })
146
    }
147
}
148
149
pub struct WindowManager {
150
    config: Config,
151
    connection: RustConnection,
152
    screen_number: usize,
153
    root: Window,
154
    _wm_check_window: Window,
155
    screen: Screen,
156
    windows: Vec<Window>,
157
    clients: HashMap<Window, Client>,
158
    layout: LayoutBox,
159
    gaps_enabled: bool,
160
    floating_windows: HashSet<Window>,
161
    fullscreen_windows: HashSet<Window>,
162
    bars: Vec<Bar>,
163
    tab_bars: Vec<crate::tab_bar::TabBar>,
164
    show_bar: bool,
165
    monitors: Vec<Monitor>,
166
    selected_monitor: usize,
167
    atoms: AtomCache,
168
    previous_focused: Option<Window>,
169
    display: *mut x11::xlib::Display,
170
    font: crate::bar::font::Font,
171
    keychord_state: keyboard::handlers::KeychordState,
172
    current_key: usize,
173
    keyboard_mapping: Option<keyboard::KeyboardMapping>,
174
    error_message: Option<String>,
175
    overlay: ErrorOverlay,
176
    keybind_overlay: KeybindOverlay,
177
}
178
179
type WmResult<T> = Result<T, WmError>;
180
181
impl WindowManager {
182
    pub fn new(config: Config) -> WmResult<Self> {
183
        let (connection, screen_number) = x11rb::connect(None)?;
184
        let root = connection.setup().roots[screen_number].root;
185
        let screen = connection.setup().roots[screen_number].clone();
186
187
        let normal_cursor = CursorHandle::new(
188
            &connection,
189
            screen_number,
190
            &x11rb::resource_manager::new_from_default(&connection)?,
191
        )?
192
        .reply()?
193
        .load_cursor(&connection, "left_ptr")?;
194
195
        connection
196
            .change_window_attributes(
197
                root,
198
                &ChangeWindowAttributesAux::new()
199
                    .cursor(normal_cursor)
200
                    .event_mask(
201
                        EventMask::SUBSTRUCTURE_REDIRECT
202
                            | EventMask::SUBSTRUCTURE_NOTIFY
203
                            | EventMask::PROPERTY_CHANGE
204
                            | EventMask::KEY_PRESS
205
                            | EventMask::BUTTON_PRESS
206
                            | EventMask::POINTER_MOTION,
207
                    ),
208
            )?
209
            .check()?;
210
211
        let ignore_modifiers = [
212
            0,
213
            u16::from(ModMask::LOCK),
214
            u16::from(ModMask::M2),
215
            u16::from(ModMask::LOCK | ModMask::M2),
216
        ];
217
218
        for &ignore_mask in &ignore_modifiers {
219
            let grab_mask = u16::from(config.modkey) | ignore_mask;
220
221
            connection.grab_button(
222
                false,
223
                root,
224
                EventMask::BUTTON_PRESS | EventMask::BUTTON_RELEASE,
225
                GrabMode::SYNC,
226
                GrabMode::ASYNC,
227
                x11rb::NONE,
228
                x11rb::NONE,
229
                ButtonIndex::M1,
230
                grab_mask.into(),
231
            )?;
232
233
            connection.grab_button(
234
                false,
235
                root,
236
                EventMask::BUTTON_PRESS | EventMask::BUTTON_RELEASE,
237
                GrabMode::SYNC,
238
                GrabMode::ASYNC,
239
                x11rb::NONE,
240
                x11rb::NONE,
241
                ButtonIndex::M3,
242
                grab_mask.into(),
243
            )?;
244
        }
245
246
        let monitors = detect_monitors(&connection, &screen, root)?;
247
248
        let display = unsafe { x11::xlib::XOpenDisplay(std::ptr::null()) };
249
        if display.is_null() {
250
            return Err(WmError::X11(crate::errors::X11Error::DisplayOpenFailed));
251
        }
252
253
        let font = crate::bar::font::Font::new(display, screen_number as i32, &config.font)?;
254
255
        let mut bars = Vec::new();
256
        for monitor in monitors.iter() {
257
            let bar = Bar::new(
258
                &connection,
259
                &screen,
260
                screen_number,
261
                &config,
262
                display,
263
                &font,
264
                monitor.screen_x as i16,
265
                monitor.screen_y as i16,
266
                monitor.screen_width as u16,
267
            )?;
268
            bars.push(bar);
269
        }
270
271
        let bar_height = font.height() as f32 * 1.4;
272
        let mut tab_bars = Vec::new();
273
        for monitor in monitors.iter() {
274
            let tab_bar = crate::tab_bar::TabBar::new(
275
                &connection,
276
                &screen,
277
                screen_number,
278
                display,
279
                &font,
280
                (monitor.screen_x + config.gap_outer_horizontal as i32) as i16,
281
                (monitor.screen_y as f32 + bar_height + config.gap_outer_vertical as f32) as i16,
282
                monitor
283
                    .screen_width
284
                    .saturating_sub(2 * config.gap_outer_horizontal as i32) as u16,
285
                config.scheme_occupied,
286
                config.scheme_selected,
287
            )?;
288
            tab_bars.push(tab_bar);
289
        }
290
291
        let gaps_enabled = config.gaps_enabled;
292
293
        let atoms = AtomCache::new(&connection)?;
294
295
        let supported_atoms: Vec<Atom> = vec![
296
            atoms.net_supported,
297
            atoms.net_supporting_wm_check,
298
            atoms.net_wm_state,
299
            atoms.net_wm_state_fullscreen,
300
            atoms.net_wm_window_type,
301
            atoms.net_wm_window_type_dialog,
302
            atoms.net_active_window,
303
            atoms.net_wm_name,
304
            atoms.net_current_desktop,
305
            atoms.net_client_info,
306
            atoms.net_client_list,
307
        ];
308
        let supported_bytes: Vec<u8> = supported_atoms
309
            .iter()
310
            .flat_map(|a| a.to_ne_bytes())
311
            .collect();
312
        connection.change_property(
313
            PropMode::REPLACE,
314
            root,
315
            atoms.net_supported,
316
            AtomEnum::ATOM,
317
            32,
318
            supported_atoms.len() as u32,
319
            &supported_bytes,
320
        )?;
321
322
        let wm_check_window = connection.generate_id()?;
323
        connection.create_window(
324
            screen.root_depth,
325
            wm_check_window,
326
            root,
327
            0,
328
            0,
329
            1,
330
            1,
331
            0,
332
            WindowClass::INPUT_OUTPUT,
333
            0,
334
            &CreateWindowAux::new(),
335
        )?;
336
337
        connection.change_property(
338
            PropMode::REPLACE,
339
            wm_check_window,
340
            atoms.net_supporting_wm_check,
341
            AtomEnum::WINDOW,
342
            32,
343
            1,
344
            &wm_check_window.to_ne_bytes(),
345
        )?;
346
347
        connection.change_property(
348
            PropMode::REPLACE,
349
            wm_check_window,
350
            atoms.net_wm_name,
351
            atoms.utf8_string,
352
            8,
353
            4,
354
            b"oxwm",
355
        )?;
356
357
        connection.change_property(
358
            PropMode::REPLACE,
359
            root,
360
            atoms.net_supporting_wm_check,
361
            AtomEnum::WINDOW,
362
            32,
363
            1,
364
            &wm_check_window.to_ne_bytes(),
365
        )?;
366
367
        let overlay = ErrorOverlay::new(
368
            &connection,
369
            &screen,
370
            screen_number,
371
            display,
372
            &font,
373
            screen.width_in_pixels,
374
        )?;
375
376
        let keybind_overlay =
377
            KeybindOverlay::new(&connection, &screen, screen_number, display, config.modkey)?;
378
379
        let mut window_manager = Self {
380
            config,
381
            connection,
382
            screen_number,
383
            root,
384
            _wm_check_window: wm_check_window,
385
            screen,
386
            windows: Vec::new(),
387
            clients: HashMap::new(),
388
            layout: Box::new(TilingLayout),
389
            gaps_enabled,
390
            floating_windows: HashSet::new(),
391
            fullscreen_windows: HashSet::new(),
392
            bars,
393
            tab_bars,
394
            show_bar: true,
395
            monitors,
396
            selected_monitor: 0,
397
            atoms,
398
            previous_focused: None,
399
            display,
400
            font,
401
            keychord_state: keyboard::handlers::KeychordState::Idle,
402
            current_key: 0,
403
            keyboard_mapping: None,
404
            error_message: None,
405
            overlay,
406
            keybind_overlay,
407
        };
408
409
        for tab_bar in &window_manager.tab_bars {
410
            tab_bar.hide(&window_manager.connection)?;
411
        }
412
413
        window_manager.scan_existing_windows()?;
414
        window_manager.update_bar()?;
415
        window_manager.run_autostart_commands();
416
417
        Ok(window_manager)
418
    }
419
420
    pub fn show_startup_config_error(&mut self, error: ConfigError) {
421
        let monitor = &self.monitors[self.selected_monitor];
422
        let monitor_x = monitor.screen_x as i16;
423
        let monitor_y = monitor.screen_y as i16;
424
        let screen_width = monitor.screen_width as u16;
425
        let screen_height = monitor.screen_height as u16;
426
427
        if let Err(e) = self.overlay.show_error(
428
            &self.connection,
429
            &self.font,
430
            error,
431
            monitor_x,
432
            monitor_y,
433
            screen_width,
434
            screen_height,
435
        ) {
436
            eprintln!("Failed to show config error overlay: {:?}", e);
437
        }
438
    }
439
440
    fn try_reload_config(&mut self) -> Result<(), ConfigError> {
441
        let lua_path = self
442
            .config
443
            .path
444
            .as_ref()
445
            .ok_or(ConfigError::NoConfigPathSet)?;
446
447
        if !lua_path.exists() {
448
            return Err(ConfigError::NoConfigAtPath);
449
        }
450
451
        let config_str =
452
            std::fs::read_to_string(lua_path).map_err(|e| ConfigError::CouldNotReadConfig(e))?;
453
454
        let config_dir = lua_path.parent();
455
456
        let new_config = crate::config::parse_lua_config(&config_str, config_dir)?;
457
458
        let lua_path = self.config.path.take();
459
460
        self.config = new_config;
461
        self.config.path = lua_path;
462
        self.error_message = None;
463
464
        for bar in &mut self.bars {
465
            bar.update_from_config(&self.config);
466
        }
467
468
        Ok(())
469
    }
470
471
    fn scan_existing_windows(&mut self) -> WmResult<()> {
472
        let tree = self.connection.query_tree(self.root)?.reply()?;
473
        let net_client_info = self.atoms.net_client_info;
474
        let wm_state_atom = self.atoms.wm_state;
475
476
        for &window in &tree.children {
477
            if self.bars.iter().any(|bar| bar.window() == window) {
478
                continue;
479
            }
480
481
            let Ok(attrs) = self.connection.get_window_attributes(window)?.reply() else {
482
                continue;
483
            };
484
485
            if attrs.override_redirect {
486
                continue;
487
            }
488
489
            if attrs.map_state == MapState::VIEWABLE {
490
                let _tag = self.get_saved_tag(window, net_client_info)?;
491
                self.windows.push(window);
492
                continue;
493
            }
494
495
            if attrs.map_state == MapState::UNMAPPED {
496
                let has_wm_state = self
497
                    .connection
498
                    .get_property(false, window, wm_state_atom, AtomEnum::ANY, 0, 2)?
499
                    .reply()
500
                    .is_ok_and(|prop| !prop.value.is_empty());
501
502
                if !has_wm_state {
503
                    continue;
504
                }
505
506
                let has_wm_class = self
507
                    .connection
508
                    .get_property(false, window, AtomEnum::WM_CLASS, AtomEnum::STRING, 0, 1024)?
509
                    .reply()
510
                    .is_ok_and(|prop| !prop.value.is_empty());
511
512
                if has_wm_class {
513
                    let _tag = self.get_saved_tag(window, net_client_info)?;
514
                    self.connection.map_window(window)?;
515
                    self.windows.push(window);
516
                }
517
            }
518
        }
519
520
        if let Some(&first) = self.windows.first() {
521
            self.focus(Some(first))?;
522
        }
523
524
        self.apply_layout()?;
525
        Ok(())
526
    }
527
528
    fn get_saved_tag(&self, window: Window, net_client_info: Atom) -> WmResult<TagMask> {
529
        match self
530
            .connection
531
            .get_property(false, window, net_client_info, AtomEnum::CARDINAL, 0, 2)?
532
            .reply()
533
        {
534
            Ok(prop) if prop.value.len() >= 4 => {
535
                let tags = u32::from_ne_bytes([
536
                    prop.value[0],
537
                    prop.value[1],
538
                    prop.value[2],
539
                    prop.value[3],
540
                ]);
541
542
                if tags != 0 && tags < (1 << self.config.tags.len()) {
543
                    return Ok(tags);
544
                }
545
            }
546
            Ok(_) => {}
547
            Err(e) => {
548
                eprintln!("No _NET_CLIENT_INFO property ({})", e);
549
            }
550
        }
551
552
        Ok(self
553
            .monitors
554
            .get(self.selected_monitor)
555
            .map(|m| m.tagset[m.selected_tags_index])
556
            .unwrap_or(tag_mask(0)))
557
    }
558
559
    fn save_client_tag(&self, window: Window, tag: TagMask) -> WmResult<()> {
560
        let net_client_info = self.atoms.net_client_info;
561
562
        let bytes = tag.to_ne_bytes().to_vec();
563
564
        self.connection.change_property(
565
            PropMode::REPLACE,
566
            window,
567
            net_client_info,
568
            AtomEnum::CARDINAL,
569
            32,
570
            1,
571
            &bytes,
572
        )?;
573
574
        self.connection.flush()?;
575
        Ok(())
576
    }
577
578
    fn set_wm_state(&self, window: Window, state: u32) -> WmResult<()> {
579
        let wm_state_atom = self.atoms.wm_state;
580
581
        let data = [state, 0u32];
582
        let bytes: Vec<u8> = data.iter().flat_map(|&v| v.to_ne_bytes()).collect();
583
584
        self.connection.change_property(
585
            PropMode::REPLACE,
586
            window,
587
            wm_state_atom,
588
            wm_state_atom,
589
            32,
590
            2,
591
            &bytes,
592
        )?;
593
594
        self.connection.flush()?;
595
        Ok(())
596
    }
597
598
    fn update_client_list(&self) -> WmResult<()> {
599
        let window_bytes: Vec<u8> = self
600
            .windows
601
            .iter()
602
            .flat_map(|window| window.to_ne_bytes())
603
            .collect();
604
605
        self.connection.change_property(
606
            PropMode::REPLACE,
607
            self.root,
608
            self.atoms.net_client_list,
609
            AtomEnum::WINDOW,
610
            32,
611
            self.windows.len() as u32,
612
            &window_bytes,
613
        )?;
614
615
        Ok(())
616
    }
617
618
    pub fn run(&mut self) -> WmResult<()> {
619
        println!("oxwm started on display {}", self.screen_number);
620
621
        self.grab_keys()?;
622
        self.update_bar()?;
623
624
        let mut last_bar_update = std::time::Instant::now();
625
        const BAR_UPDATE_INTERVAL_MS: u64 = 100;
626
627
        loop {
628
            match self.connection.poll_for_event_with_sequence()? {
629
                Some((event, _sequence)) => {
630
                    if matches!(self.handle_event(event)?, Control::Quit) {
631
                        return Ok(());
632
                    }
633
                }
634
                None => {
635
                    if last_bar_update.elapsed().as_millis() >= BAR_UPDATE_INTERVAL_MS as u128 {
636
                        if let Some(bar) = self.bars.get_mut(self.selected_monitor) {
637
                            bar.update_blocks();
638
                        }
639
                        if self.bars.iter().any(|bar| bar.needs_redraw()) {
640
                            self.update_bar()?;
641
                        }
642
                        last_bar_update = std::time::Instant::now();
643
                    }
644
645
                    self.connection.flush()?;
646
                    std::thread::sleep(std::time::Duration::from_millis(16));
647
                }
648
            }
649
        }
650
    }
651
652
    fn toggle_floating(&mut self) -> WmResult<()> {
653
        let focused = self
654
            .monitors
655
            .get(self.selected_monitor)
656
            .and_then(|m| m.selected_client);
657
658
        if focused.is_none() {
659
            return Ok(());
660
        }
661
        let focused = focused.unwrap();
662
663
        if let Some(client) = self.clients.get(&focused)
664
            && client.is_fullscreen
665
        {
666
            return Ok(());
667
        }
668
669
        let (is_fixed, x, y, w, h) = if let Some(client) = self.clients.get(&focused) {
670
            (
671
                client.is_fixed,
672
                client.x_position as i32,
673
                client.y_position as i32,
674
                client.width as u32,
675
                client.height as u32,
676
            )
677
        } else {
678
            return Ok(());
679
        };
680
681
        let was_floating = self.floating_windows.contains(&focused);
682
683
        if was_floating {
684
            self.floating_windows.remove(&focused);
685
            if let Some(client) = self.clients.get_mut(&focused) {
686
                client.is_floating = false;
687
            }
688
        } else {
689
            self.floating_windows.insert(focused);
690
            if let Some(client) = self.clients.get_mut(&focused) {
691
                client.is_floating = is_fixed || !client.is_floating;
692
            }
693
694
            self.connection.configure_window(
695
                focused,
696
                &ConfigureWindowAux::new()
697
                    .x(x)
698
                    .y(y)
699
                    .width(w)
700
                    .height(h)
701
                    .stack_mode(StackMode::ABOVE),
702
            )?;
703
        }
704
705
        self.apply_layout()?;
706
        Ok(())
707
    }
708
709
    fn set_master_factor(&mut self, delta: f32) -> WmResult<()> {
710
        if let Some(monitor) = self.monitors.get_mut(self.selected_monitor) {
711
            let new_mfact = (monitor.master_factor + delta).clamp(0.05, 0.95);
712
            monitor.master_factor = new_mfact;
713
            self.apply_layout()?;
714
        }
715
        Ok(())
716
    }
717
718
    fn inc_num_master(&mut self, delta: i32) -> WmResult<()> {
719
        if let Some(monitor) = self.monitors.get_mut(self.selected_monitor) {
720
            let new_nmaster = (monitor.num_master + delta).max(0);
721
            monitor.num_master = new_nmaster;
722
            self.apply_layout()?;
723
        }
724
        Ok(())
725
    }
726
727
    fn get_layout_symbol(&self) -> String {
728
        let layout_name = self.layout.name();
729
        self.config
730
            .layout_symbols
731
            .iter()
732
            .find(|l| l.name == layout_name)
733
            .map(|l| l.symbol.clone())
734
            .unwrap_or_else(|| self.layout.symbol().to_string())
735
    }
736
737
    fn get_keychord_indicator(&self) -> Option<String> {
738
        match &self.keychord_state {
739
            keyboard::handlers::KeychordState::Idle => None,
740
            keyboard::handlers::KeychordState::InProgress {
741
                candidates,
742
                keys_pressed,
743
            } => {
744
                if candidates.is_empty() {
745
                    return None;
746
                }
747
748
                let binding = &self.config.keybindings[candidates[0]];
749
                let mut indicator = String::new();
750
751
                for (i, key_press) in binding.keys.iter().take(*keys_pressed).enumerate() {
752
                    if i > 0 {
753
                        indicator.push(' ');
754
                    }
755
756
                    for modifier in &key_press.modifiers {
757
                        indicator.push_str(Self::format_modifier(*modifier));
758
                        indicator.push('+');
759
                    }
760
761
                    indicator.push_str(&keyboard::keysyms::format_keysym(key_press.keysym));
762
                }
763
764
                indicator.push('-');
765
                Some(indicator)
766
            }
767
        }
768
    }
769
770
    fn format_modifier(modifier: KeyButMask) -> &'static str {
771
        match modifier {
772
            KeyButMask::MOD1 => "Alt",
773
            KeyButMask::MOD4 => "Super",
774
            KeyButMask::SHIFT => "Shift",
775
            KeyButMask::CONTROL => "Ctrl",
776
            _ => "Mod",
777
        }
778
    }
779
780
    fn update_bar(&mut self) -> WmResult<()> {
781
        let layout_symbol = self.get_layout_symbol();
782
        let keychord_indicator = self.get_keychord_indicator();
783
784
        for (monitor_index, monitor) in self.monitors.iter().enumerate() {
785
            if let Some(bar) = self.bars.get_mut(monitor_index) {
786
                let mut occupied_tags: TagMask = 0;
787
                let mut urgent_tags: TagMask = 0;
788
                for client in self.clients.values() {
789
                    if client.monitor_index == monitor_index {
790
                        occupied_tags |= client.tags;
791
                        if client.is_urgent {
792
                            urgent_tags |= client.tags;
793
                        }
794
                    }
795
                }
796
797
                let draw_blocks = monitor_index == self.selected_monitor;
798
                bar.invalidate();
799
                bar.draw(
800
                    &self.connection,
801
                    &self.font,
802
                    self.display,
803
                    monitor.tagset[monitor.selected_tags_index],
804
                    occupied_tags,
805
                    urgent_tags,
806
                    draw_blocks,
807
                    &layout_symbol,
808
                    keychord_indicator.as_deref(),
809
                )?;
810
            }
811
        }
812
        Ok(())
813
    }
814
815
    fn update_tab_bars(&mut self) -> WmResult<()> {
816
        for (monitor_index, monitor) in self.monitors.iter().enumerate() {
817
            if let Some(tab_bar) = self.tab_bars.get_mut(monitor_index) {
818
                let visible_windows: Vec<(Window, String)> = self
819
                    .windows
820
                    .iter()
821
                    .filter_map(|&window| {
822
                        if let Some(client) = self.clients.get(&window) {
823
                            if client.monitor_index != monitor_index
824
                                || self.floating_windows.contains(&window)
825
                                || self.fullscreen_windows.contains(&window)
826
                            {
827
                                return None;
828
                            }
829
                            if (client.tags & monitor.tagset[monitor.selected_tags_index]) != 0 {
830
                                return Some((window, client.name.clone()));
831
                            }
832
                        }
833
                        None
834
                    })
835
                    .collect();
836
837
                let focused_window = monitor.selected_client;
838
839
                tab_bar.draw(
840
                    &self.connection,
841
                    &self.font,
842
                    &visible_windows,
843
                    focused_window,
844
                )?;
845
            }
846
        }
847
        Ok(())
848
    }
849
850
    fn handle_key_action(&mut self, action: KeyAction, arg: &Arg) -> WmResult<()> {
851
        match action {
852
            KeyAction::Spawn => handlers::handle_spawn_action(action, arg, self.selected_monitor)?,
853
            KeyAction::SpawnTerminal => {
854
                crate::signal::spawn_detached(&self.config.terminal);
855
            }
856
            KeyAction::KillClient => {
857
                if let Some(focused) = self
858
                    .monitors
859
                    .get(self.selected_monitor)
860
                    .and_then(|m| m.selected_client)
861
                {
862
                    self.kill_client(focused)?;
863
                }
864
            }
865
            KeyAction::ToggleFullScreen => {
866
                self.fullscreen()?;
867
                self.restack()?;
868
            }
869
            KeyAction::ChangeLayout => {
870
                if let Arg::Str(layout_name) = arg {
871
                    match layout_from_str(layout_name) {
872
                        Ok(layout) => {
873
                            self.layout = layout;
874
                            if layout_name != "normie" && layout_name != "floating" {
875
                                self.floating_windows.clear();
876
                            }
877
                            self.apply_layout()?;
878
                            self.update_bar()?;
879
                            self.restack()?;
880
                        }
881
                        Err(e) => eprintln!("Failed to change layout: {}", e),
882
                    }
883
                }
884
            }
885
            KeyAction::CycleLayout => {
886
                let current_name = self.layout.name();
887
                let next_name = next_layout(current_name);
888
                match layout_from_str(next_name) {
889
                    Ok(layout) => {
890
                        self.layout = layout;
891
                        if next_name != "normie" && next_name != "floating" {
892
                            self.floating_windows.clear();
893
                        }
894
                        self.apply_layout()?;
895
                        self.update_bar()?;
896
                        self.restack()?;
897
                    }
898
                    Err(e) => eprintln!("Failed to cycle layout: {}", e),
899
                }
900
            }
901
            KeyAction::ToggleFloating => {
902
                self.toggle_floating()?;
903
                self.restack()?;
904
            }
905
906
            KeyAction::FocusStack => {
907
                if let Arg::Int(direction) = arg {
908
                    self.focusstack(*direction)?;
909
                    self.restack()?;
910
                }
911
            }
912
            KeyAction::MoveStack => {
913
                if let Arg::Int(direction) = arg {
914
                    self.move_stack(*direction)?;
915
                    self.restack()?;
916
                }
917
            }
918
            KeyAction::Quit | KeyAction::Restart => {}
919
            KeyAction::ViewTag => {
920
                if let Arg::Int(tag_index) = arg {
921
                    self.view_tag(*tag_index as usize)?;
922
                }
923
            }
924
            KeyAction::ViewNextTag => {
925
                let monitor = self.get_selected_monitor();
926
                let current_tag_index = unmask_tag(monitor.get_selected_tag()) as i32;
927
                let len = self.config.tags.len() as i32;
928
                self.view_tag((current_tag_index + 1).rem_euclid(len) as usize)?;
929
            }
930
            KeyAction::ViewPreviousTag => {
931
                let monitor = self.get_selected_monitor();
932
                let current_tag_index = unmask_tag(monitor.get_selected_tag()) as i32;
933
                let len = self.config.tags.len() as i32;
934
                self.view_tag((current_tag_index - 1).rem_euclid(len) as usize)?;
935
            }
936
            KeyAction::ViewNextNonEmptyTag => {
937
                let monitor = self.get_selected_monitor();
938
                let current = unmask_tag(monitor.get_selected_tag()) as i32;
939
                let len = self.config.tags.len() as i32;
940
                let mon_num = monitor.monitor_number;
941
942
                for offset in 1..len {
943
                    let next = (current + offset).rem_euclid(len) as usize;
944
                    if self.has_windows_on_tag(mon_num, next) {
945
                        self.view_tag(next)?;
946
                        break;
947
                    }
948
                }
949
            }
950
            KeyAction::ViewPreviousNonEmptyTag => {
951
                let monitor = self.get_selected_monitor();
952
                let current = unmask_tag(monitor.get_selected_tag()) as i32;
953
                let len = self.config.tags.len() as i32;
954
                let mon_num = monitor.monitor_number;
955
956
                for offset in 1..len {
957
                    let prev = (current - offset).rem_euclid(len) as usize;
958
                    if self.has_windows_on_tag(mon_num, prev) {
959
                        self.view_tag(prev)?;
960
                        break;
961
                    }
962
                }
963
            }
964
            KeyAction::ToggleView => {
965
                if let Arg::Int(tag_index) = arg {
966
                    self.toggleview(*tag_index as usize)?;
967
                }
968
            }
969
            KeyAction::MoveToTag => {
970
                if let Arg::Int(tag_index) = arg {
971
                    self.move_to_tag(*tag_index as usize)?;
972
                }
973
            }
974
            KeyAction::ToggleTag => {
975
                if let Arg::Int(tag_index) = arg {
976
                    self.toggletag(*tag_index as usize)?;
977
                }
978
            }
979
            KeyAction::ToggleGaps => {
980
                self.gaps_enabled = !self.gaps_enabled;
981
                self.apply_layout()?;
982
                self.restack()?;
983
            }
984
            KeyAction::FocusMonitor => {
985
                if let Arg::Int(direction) = arg {
986
                    self.focus_monitor(*direction)?;
987
                }
988
            }
989
            KeyAction::TagMonitor => {
990
                if let Arg::Int(direction) = arg {
991
                    self.send_window_to_adjacent_monitor(*direction)?;
992
                }
993
            }
994
            KeyAction::ShowKeybindOverlay => {
995
                let monitor = &self.monitors[self.selected_monitor];
996
                self.keybind_overlay.toggle(
997
                    &self.connection,
998
                    &self.font,
999
                    &self.config.keybindings,
1000
                    monitor.screen_x as i16,
1001
                    monitor.screen_y as i16,
1002
                    monitor.screen_width as u16,
1003
                    monitor.screen_height as u16,
1004
                )?;
1005
            }
1006
            KeyAction::SetMasterFactor => {
1007
                if let Arg::Int(delta) = arg {
1008
                    self.set_master_factor(*delta as f32 / 100.0)?;
1009
                }
1010
            }
1011
            KeyAction::IncNumMaster => {
1012
                if let Arg::Int(delta) = arg {
1013
                    self.inc_num_master(*delta)?;
1014
                }
1015
            }
1016
            KeyAction::None => {}
1017
        }
1018
        Ok(())
1019
    }
1020
1021
    fn is_window_visible(&self, window: Window) -> bool {
1022
        if let Some(client) = self.clients.get(&window) {
1023
            let monitor = self.monitors.get(client.monitor_index);
1024
            let selected_tags = monitor
1025
                .map(|m| m.tagset[m.selected_tags_index])
1026
                .unwrap_or(0);
1027
            (client.tags & selected_tags) != 0
1028
        } else {
1029
            false
1030
        }
1031
    }
1032
1033
    fn visible_windows(&self) -> Vec<Window> {
1034
        let mut result = Vec::new();
1035
        for monitor in &self.monitors {
1036
            let mut current = monitor.clients_head;
1037
            while let Some(window) = current {
1038
                if let Some(client) = self.clients.get(&window) {
1039
                    let visible_tags = client.tags & monitor.tagset[monitor.selected_tags_index];
1040
                    if visible_tags != 0 {
1041
                        result.push(window);
1042
                    }
1043
                    current = client.next;
1044
                } else {
1045
                    break;
1046
                }
1047
            }
1048
        }
1049
        result
1050
    }
1051
1052
    fn visible_windows_on_monitor(&self, monitor_index: usize) -> Vec<Window> {
1053
        let mut result = Vec::new();
1054
        if let Some(monitor) = self.monitors.get(monitor_index) {
1055
            let mut current = monitor.clients_head;
1056
            while let Some(window) = current {
1057
                if let Some(client) = self.clients.get(&window) {
1058
                    let visible_tags = client.tags & monitor.tagset[monitor.selected_tags_index];
1059
                    if visible_tags != 0 {
1060
                        result.push(window);
1061
                    }
1062
                    current = client.next;
1063
                } else {
1064
                    break;
1065
                }
1066
            }
1067
        }
1068
        result
1069
    }
1070
1071
    fn get_monitor_at_point(&self, x: i32, y: i32) -> Option<usize> {
1072
        self.monitors
1073
            .iter()
1074
            .position(|mon| mon.contains_point(x, y))
1075
    }
1076
1077
    fn get_monitor_for_rect(&self, x: i32, y: i32, w: i32, h: i32) -> usize {
1078
        let mut best_monitor = self.selected_monitor;
1079
        let mut max_area = 0;
1080
1081
        for (idx, monitor) in self.monitors.iter().enumerate() {
1082
            let intersect_width = 0.max(
1083
                (x + w).min(monitor.window_area_x + monitor.window_area_width)
1084
                    - x.max(monitor.window_area_x),
1085
            );
1086
            let intersect_height = 0.max(
1087
                (y + h).min(monitor.window_area_y + monitor.window_area_height)
1088
                    - y.max(monitor.window_area_y),
1089
            );
1090
            let area = intersect_width * intersect_height;
1091
1092
            if area > max_area {
1093
                max_area = area;
1094
                best_monitor = idx;
1095
            }
1096
        }
1097
1098
        best_monitor
1099
    }
1100
1101
    fn move_window_to_monitor(
1102
        &mut self,
1103
        window: Window,
1104
        target_monitor_index: usize,
1105
    ) -> WmResult<()> {
1106
        let current_monitor_index = self.clients.get(&window).map(|c| c.monitor_index);
1107
1108
        if let Some(current_idx) = current_monitor_index
1109
            && current_idx == target_monitor_index
1110
        {
1111
            return Ok(());
1112
        }
1113
1114
        self.unfocus(window, false)?;
1115
        self.detach(window);
1116
        self.detach_stack(window);
1117
1118
        if let Some(client) = self.clients.get_mut(&window) {
1119
            client.monitor_index = target_monitor_index;
1120
            if let Some(target_monitor) = self.monitors.get(target_monitor_index) {
1121
                client.tags = target_monitor.tagset[target_monitor.selected_tags_index];
1122
            }
1123
        }
1124
1125
        self.attach_aside(window, target_monitor_index);
1126
        self.attach_stack(window, target_monitor_index);
1127
1128
        self.focus(None)?;
1129
        self.apply_layout()?;
1130
1131
        Ok(())
1132
    }
1133
1134
    fn get_adjacent_monitor(&self, direction: i32) -> Option<usize> {
1135
        if self.monitors.len() <= 1 {
1136
            return None;
1137
        }
1138
1139
        if direction > 0 {
1140
            if self.selected_monitor + 1 < self.monitors.len() {
1141
                Some(self.selected_monitor + 1)
1142
            } else {
1143
                Some(0)
1144
            }
1145
        } else if self.selected_monitor == 0 {
1146
            Some(self.monitors.len() - 1)
1147
        } else {
1148
            Some(self.selected_monitor - 1)
1149
        }
1150
    }
1151
1152
    fn is_visible(&self, window: Window) -> bool {
1153
        let Some(client) = self.clients.get(&window) else {
1154
            return false;
1155
        };
1156
1157
        let Some(monitor) = self.monitors.get(client.monitor_index) else {
1158
            return false;
1159
        };
1160
1161
        (client.tags & monitor.tagset[monitor.selected_tags_index]) != 0
1162
    }
1163
1164
    fn showhide(&mut self, window: Option<Window>) -> WmResult<()> {
1165
        let Some(window) = window else {
1166
            return Ok(());
1167
        };
1168
1169
        let Some(client) = self.clients.get(&window).cloned() else {
1170
            return Ok(());
1171
        };
1172
1173
        let monitor = match self.monitors.get(client.monitor_index) {
1174
            Some(m) => m,
1175
            None => return Ok(()),
1176
        };
1177
1178
        let is_visible = (client.tags & monitor.tagset[monitor.selected_tags_index]) != 0;
1179
1180
        if is_visible {
1181
            self.connection.configure_window(
1182
                window,
1183
                &ConfigureWindowAux::new()
1184
                    .x(client.x_position as i32)
1185
                    .y(client.y_position as i32),
1186
            )?;
1187
1188
            let is_floating = client.is_floating;
1189
            let is_fullscreen = client.is_fullscreen;
1190
            let has_no_layout = self.layout.name() == LayoutType::Normie.as_str();
1191
1192
            if (has_no_layout || is_floating) && !is_fullscreen {
1193
                let (x, y, w, h, changed) = self.apply_size_hints(
1194
                    window,
1195
                    client.x_position as i32,
1196
                    client.y_position as i32,
1197
                    client.width as i32,
1198
                    client.height as i32,
1199
                );
1200
                if changed {
1201
                    if let Some(c) = self.clients.get_mut(&window) {
1202
                        c.old_x_position = c.x_position;
1203
                        c.old_y_position = c.y_position;
1204
                        c.old_width = c.width;
1205
                        c.old_height = c.height;
1206
                        c.x_position = x as i16;
1207
                        c.y_position = y as i16;
1208
                        c.width = w as u16;
1209
                        c.height = h as u16;
1210
                    }
1211
                    self.connection.configure_window(
1212
                        window,
1213
                        &ConfigureWindowAux::new()
1214
                            .x(x)
1215
                            .y(y)
1216
                            .width(w as u32)
1217
                            .height(h as u32)
1218
                            .border_width(self.config.border_width),
1219
                    )?;
1220
                    self.send_configure_notify(window)?;
1221
                    self.connection.flush()?;
1222
                }
1223
            }
1224
1225
            self.showhide(client.stack_next)?;
1226
        } else {
1227
            self.showhide(client.stack_next)?;
1228
1229
            let width = client.width_with_border() as i32;
1230
            self.connection.configure_window(
1231
                window,
1232
                &ConfigureWindowAux::new()
1233
                    .x(width * -2)
1234
                    .y(client.y_position as i32),
1235
            )?;
1236
        }
1237
1238
        Ok(())
1239
    }
1240
1241
    pub fn view_tag(&mut self, tag_index: usize) -> WmResult<()> {
1242
        if tag_index >= self.config.tags.len() {
1243
            return Ok(());
1244
        }
1245
1246
        let monitor = match self.monitors.get_mut(self.selected_monitor) {
1247
            Some(m) => m,
1248
            None => return Ok(()),
1249
        };
1250
1251
        let new_tagset = tag_mask(tag_index);
1252
1253
        if new_tagset == monitor.tagset[monitor.selected_tags_index] {
1254
            if !self.config.tag_back_and_forth {
1255
                return Ok(());
1256
            }
1257
            monitor.tagset.swap(0, 1);
1258
        } else {
1259
            monitor.selected_tags_index ^= 1;
1260
            monitor.tagset[monitor.selected_tags_index] = new_tagset;
1261
        }
1262
1263
        self.save_selected_tags()?;
1264
        self.focus(None)?;
1265
        self.apply_layout()?;
1266
        self.update_bar()?;
1267
1268
        Ok(())
1269
    }
1270
1271
    pub fn toggleview(&mut self, tag_index: usize) -> WmResult<()> {
1272
        if tag_index >= self.config.tags.len() {
1273
            return Ok(());
1274
        }
1275
1276
        let monitor = match self.monitors.get_mut(self.selected_monitor) {
1277
            Some(m) => m,
1278
            None => return Ok(()),
1279
        };
1280
1281
        let mask = tag_mask(tag_index);
1282
        let new_tagset = monitor.tagset[monitor.selected_tags_index] ^ mask;
1283
1284
        if new_tagset == 0 {
1285
            return Ok(());
1286
        }
1287
1288
        monitor.tagset[monitor.selected_tags_index] = new_tagset;
1289
1290
        self.save_selected_tags()?;
1291
        self.focus(None)?;
1292
        self.apply_layout()?;
1293
        self.update_bar()?;
1294
1295
        Ok(())
1296
    }
1297
1298
    fn save_selected_tags(&self) -> WmResult<()> {
1299
        let net_current_desktop = self.atoms.net_current_desktop;
1300
1301
        let selected_tags = self
1302
            .monitors
1303
            .get(self.selected_monitor)
1304
            .map(|m| m.tagset[m.selected_tags_index])
1305
            .unwrap_or(tag_mask(0));
1306
        let desktop = selected_tags.trailing_zeros();
1307
1308
        let bytes = (desktop as u32).to_ne_bytes();
1309
        self.connection.change_property(
1310
            PropMode::REPLACE,
1311
            self.root,
1312
            net_current_desktop,
1313
            AtomEnum::CARDINAL,
1314
            32,
1315
            1,
1316
            &bytes,
1317
        )?;
1318
1319
        self.connection.flush()?;
1320
        Ok(())
1321
    }
1322
1323
    pub fn move_to_tag(&mut self, tag_index: usize) -> WmResult<()> {
1324
        if tag_index >= self.config.tags.len() {
1325
            return Ok(());
1326
        }
1327
1328
        let focused = match self
1329
            .monitors
1330
            .get(self.selected_monitor)
1331
            .and_then(|m| m.selected_client)
1332
        {
1333
            Some(win) => win,
1334
            None => return Ok(()),
1335
        };
1336
1337
        let mask = tag_mask(tag_index);
1338
1339
        if let Some(client) = self.clients.get_mut(&focused) {
1340
            client.tags = mask;
1341
        }
1342
1343
        if let Err(error) = self.save_client_tag(focused, mask) {
1344
            eprintln!("Failed to save client tag: {:?}", error);
1345
        }
1346
1347
        self.focus(None)?;
1348
        self.apply_layout()?;
1349
        self.update_bar()?;
1350
1351
        Ok(())
1352
    }
1353
1354
    pub fn toggletag(&mut self, tag_index: usize) -> WmResult<()> {
1355
        if tag_index >= self.config.tags.len() {
1356
            return Ok(());
1357
        }
1358
1359
        let focused = match self
1360
            .monitors
1361
            .get(self.selected_monitor)
1362
            .and_then(|m| m.selected_client)
1363
        {
1364
            Some(win) => win,
1365
            None => return Ok(()),
1366
        };
1367
1368
        let mask = tag_mask(tag_index);
1369
        let current_tags = self.clients.get(&focused).map(|c| c.tags).unwrap_or(0);
1370
        let new_tags = current_tags ^ mask;
1371
1372
        if new_tags == 0 {
1373
            return Ok(());
1374
        }
1375
1376
        if let Some(client) = self.clients.get_mut(&focused) {
1377
            client.tags = new_tags;
1378
        }
1379
1380
        if let Err(error) = self.save_client_tag(focused, new_tags) {
1381
            eprintln!("Failed to save client tag: {:?}", error);
1382
        }
1383
1384
        self.focus(None)?;
1385
        self.apply_layout()?;
1386
        self.update_bar()?;
1387
1388
        Ok(())
1389
    }
1390
1391
    pub fn cycle_focus(&mut self, direction: i32) -> WmResult<()> {
1392
        let visible = self.visible_windows();
1393
1394
        if visible.is_empty() {
1395
            return Ok(());
1396
        }
1397
1398
        let current = self
1399
            .monitors
1400
            .get(self.selected_monitor)
1401
            .and_then(|m| m.selected_client);
1402
1403
        let next_window = if let Some(current) = current {
1404
            if let Some(current_index) = visible.iter().position(|&w| w == current) {
1405
                let next_index = if direction > 0 {
1406
                    (current_index + 1) % visible.len()
1407
                } else {
1408
                    (current_index + visible.len() - 1) % visible.len()
1409
                };
1410
                visible[next_index]
1411
            } else {
1412
                visible[0]
1413
            }
1414
        } else {
1415
            visible[0]
1416
        };
1417
1418
        let is_tabbed = self.layout.name() == "tabbed";
1419
        if is_tabbed {
1420
            self.connection.configure_window(
1421
                next_window,
1422
                &ConfigureWindowAux::new().stack_mode(StackMode::ABOVE),
1423
            )?;
1424
        }
1425
1426
        self.focus(Some(next_window))?;
1427
1428
        if is_tabbed {
1429
            self.update_tab_bars()?;
1430
        }
1431
1432
        Ok(())
1433
    }
1434
1435
    fn grab_keys(&mut self) -> WmResult<()> {
1436
        self.keyboard_mapping = Some(keyboard::grab_keys(
1437
            &self.connection,
1438
            self.root,
1439
            &self.config.keybindings,
1440
            self.current_key,
1441
        )?);
1442
        Ok(())
1443
    }
1444
1445
    fn kill_client(&self, window: Window) -> WmResult<()> {
1446
        if self.send_event(window, self.atoms.wm_delete_window)? {
1447
            self.connection.flush()?;
1448
        } else {
1449
            eprintln!(
1450
                "Window {} doesn't support WM_DELETE_WINDOW, killing forcefully",
1451
                window
1452
            );
1453
            self.connection.kill_client(window)?;
1454
            self.connection.flush()?;
1455
        }
1456
        Ok(())
1457
    }
1458
1459
    fn send_event(&self, window: Window, protocol: Atom) -> WmResult<bool> {
1460
        let protocols_reply = self
1461
            .connection
1462
            .get_property(
1463
                false,
1464
                window,
1465
                self.atoms.wm_protocols,
1466
                AtomEnum::ATOM,
1467
                0,
1468
                100,
1469
            )?
1470
            .reply();
1471
1472
        let protocols_reply = match protocols_reply {
1473
            Ok(reply) => reply,
1474
            Err(_) => return Ok(false),
1475
        };
1476
1477
        let protocols: Vec<Atom> = protocols_reply
1478
            .value
1479
            .chunks_exact(4)
1480
            .map(|chunk| u32::from_ne_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]))
1481
            .collect();
1482
1483
        if !protocols.contains(&protocol) {
1484
            return Ok(false);
1485
        }
1486
1487
        let event = x11rb::protocol::xproto::ClientMessageEvent {
1488
            response_type: x11rb::protocol::xproto::CLIENT_MESSAGE_EVENT,
1489
            format: 32,
1490
            sequence: 0,
1491
            window,
1492
            type_: self.atoms.wm_protocols,
1493
            data: x11rb::protocol::xproto::ClientMessageData::from([
1494
                protocol,
1495
                x11rb::CURRENT_TIME,
1496
                0,
1497
                0,
1498
                0,
1499
            ]),
1500
        };
1501
1502
        self.connection
1503
            .send_event(false, window, EventMask::NO_EVENT, event)?;
1504
        self.connection.flush()?;
1505
        Ok(true)
1506
    }
1507
1508
    fn set_urgent(&mut self, window: Window, urgent: bool) -> WmResult<()> {
1509
        if let Some(client) = self.clients.get_mut(&window) {
1510
            client.is_urgent = urgent;
1511
        }
1512
1513
        let hints_reply = self
1514
            .connection
1515
            .get_property(false, window, AtomEnum::WM_HINTS, AtomEnum::WM_HINTS, 0, 9)?
1516
            .reply();
1517
1518
        if let Ok(hints) = hints_reply
1519
            && hints.value.len() >= 4
1520
        {
1521
            let mut flags = u32::from_ne_bytes([
1522
                hints.value[0],
1523
                hints.value[1],
1524
                hints.value[2],
1525
                hints.value[3],
1526
            ]);
1527
1528
            if urgent {
1529
                flags |= 256;
1530
            } else {
1531
                flags &= !256;
1532
            }
1533
1534
            let mut new_hints = hints.value.clone();
1535
            new_hints[0..4].copy_from_slice(&flags.to_ne_bytes());
1536
1537
            self.connection.change_property(
1538
                PropMode::REPLACE,
1539
                window,
1540
                AtomEnum::WM_HINTS,
1541
                AtomEnum::WM_HINTS,
1542
                32,
1543
                new_hints.len() as u32 / 4,
1544
                &new_hints,
1545
            )?;
1546
        }
1547
1548
        Ok(())
1549
    }
1550
1551
    fn get_window_atom_property(&self, window: Window, property: Atom) -> WmResult<Option<Atom>> {
1552
        let reply = self
1553
            .connection
1554
            .get_property(false, window, property, AtomEnum::ATOM, 0, 1)?
1555
            .reply();
1556
1557
        match reply {
1558
            Ok(prop) if !prop.value.is_empty() && prop.value.len() >= 4 => {
1559
                let atom = u32::from_ne_bytes([
1560
                    prop.value[0],
1561
                    prop.value[1],
1562
                    prop.value[2],
1563
                    prop.value[3],
1564
                ]);
1565
                Ok(Some(atom))
1566
            }
1567
            _ => Ok(None),
1568
        }
1569
    }
1570
1571
    fn get_window_atom_list_property(&self, window: Window, property: Atom) -> WmResult<Vec<Atom>> {
1572
        let reply = self
1573
            .connection
1574
            .get_property(false, window, property, AtomEnum::ATOM, 0, 32)?
1575
            .reply();
1576
1577
        match reply {
1578
            Ok(prop) if !prop.value.is_empty() => {
1579
                let atoms: Vec<Atom> = prop
1580
                    .value
1581
                    .chunks_exact(4)
1582
                    .map(|chunk| u32::from_ne_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]))
1583
                    .collect();
1584
                Ok(atoms)
1585
            }
1586
            _ => Ok(Vec::new()),
1587
        }
1588
    }
1589
1590
    fn fullscreen(&mut self) -> WmResult<()> {
1591
        let Some(focused_window) = self
1592
            .monitors
1593
            .get(self.selected_monitor)
1594
            .and_then(|m| m.selected_client)
1595
        else {
1596
            return Ok(());
1597
        };
1598
1599
        let is_fullscreen = self.fullscreen_windows.contains(&focused_window);
1600
        self.set_window_fullscreen(focused_window, !is_fullscreen)?;
1601
        Ok(())
1602
    }
1603
1604
    fn set_window_fullscreen(&mut self, window: Window, fullscreen: bool) -> WmResult<()> {
1605
        let monitor_idx = self
1606
            .clients
1607
            .get(&window)
1608
            .map(|c| c.monitor_index)
1609
            .unwrap_or(self.selected_monitor);
1610
        let monitor = &self.monitors[monitor_idx];
1611
1612
        if fullscreen && !self.fullscreen_windows.contains(&window) {
1613
            let bytes = self.atoms.net_wm_state_fullscreen.to_ne_bytes().to_vec();
1614
            self.connection.change_property(
1615
                PropMode::REPLACE,
1616
                window,
1617
                self.atoms.net_wm_state,
1618
                AtomEnum::ATOM,
1619
                32,
1620
                1,
1621
                &bytes,
1622
            )?;
1623
1624
            if let Some(client) = self.clients.get_mut(&window) {
1625
                client.is_fullscreen = true;
1626
                client.old_state = client.is_floating;
1627
                client.old_border_width = client.border_width;
1628
                client.old_x_position = client.x_position;
1629
                client.old_y_position = client.y_position;
1630
                client.old_width = client.width;
1631
                client.old_height = client.height;
1632
                client.border_width = 0;
1633
                client.is_floating = true;
1634
            }
1635
1636
            self.fullscreen_windows.insert(window);
1637
            self.floating_windows.insert(window);
1638
1639
            self.connection.configure_window(
1640
                window,
1641
                &x11rb::protocol::xproto::ConfigureWindowAux::new()
1642
                    .border_width(0)
1643
                    .x(monitor.screen_x)
1644
                    .y(monitor.screen_y)
1645
                    .width(monitor.screen_width as u32)
1646
                    .height(monitor.screen_height as u32)
1647
                    .stack_mode(x11rb::protocol::xproto::StackMode::ABOVE),
1648
            )?;
1649
1650
            self.connection.flush()?;
1651
        } else if !fullscreen && self.fullscreen_windows.contains(&window) {
1652
            self.connection.change_property(
1653
                PropMode::REPLACE,
1654
                window,
1655
                self.atoms.net_wm_state,
1656
                AtomEnum::ATOM,
1657
                32,
1658
                0,
1659
                &[],
1660
            )?;
1661
1662
            self.fullscreen_windows.remove(&window);
1663
1664
            let (was_floating, restored_x, restored_y, restored_width, restored_height, restored_border) = self
1665
                .clients
1666
                .get(&window)
1667
                .map(|client| {
1668
                    (
1669
                        client.old_state,
1670
                        client.old_x_position,
1671
                        client.old_y_position,
1672
                        client.old_width,
1673
                        client.old_height,
1674
                        client.old_border_width,
1675
                    )
1676
                })
1677
                .unwrap_or((false, 0, 0, 100, 100, 0));
1678
1679
            if !was_floating {
1680
                self.floating_windows.remove(&window);
1681
            }
1682
1683
            if let Some(client) = self.clients.get_mut(&window) {
1684
                client.is_fullscreen = false;
1685
                client.is_floating = client.old_state;
1686
                client.border_width = client.old_border_width;
1687
                client.x_position = client.old_x_position;
1688
                client.y_position = client.old_y_position;
1689
                client.width = client.old_width;
1690
                client.height = client.old_height;
1691
            }
1692
1693
            self.connection.configure_window(
1694
                window,
1695
                &ConfigureWindowAux::new()
1696
                    .x(restored_x as i32)
1697
                    .y(restored_y as i32)
1698
                    .width(restored_width as u32)
1699
                    .height(restored_height as u32)
1700
                    .border_width(restored_border as u32),
1701
            )?;
1702
1703
            self.apply_layout()?;
1704
        }
1705
1706
        Ok(())
1707
    }
1708
1709
    fn get_transient_parent(&self, window: Window) -> Option<Window> {
1710
        self.connection
1711
            .get_property(
1712
                false,
1713
                window,
1714
                AtomEnum::WM_TRANSIENT_FOR,
1715
                AtomEnum::WINDOW,
1716
                0,
1717
                1,
1718
            )
1719
            .ok()
1720
            .and_then(|cookie| cookie.reply().ok())
1721
            .filter(|reply| !reply.value.is_empty())
1722
            .and_then(|reply| {
1723
                if reply.value.len() >= 4 {
1724
                    let parent_window = u32::from_ne_bytes([
1725
                        reply.value[0],
1726
                        reply.value[1],
1727
                        reply.value[2],
1728
                        reply.value[3],
1729
                    ]);
1730
                    Some(parent_window)
1731
                } else {
1732
                    None
1733
                }
1734
            })
1735
    }
1736
1737
    fn get_window_class_instance(&self, window: Window) -> (String, String) {
1738
        let reply = self
1739
            .connection
1740
            .get_property(false, window, AtomEnum::WM_CLASS, AtomEnum::STRING, 0, 1024)
1741
            .ok()
1742
            .and_then(|cookie| cookie.reply().ok());
1743
1744
        if let Some(reply) = reply
1745
            && !reply.value.is_empty()
1746
            && let Ok(text) = std::str::from_utf8(&reply.value)
1747
        {
1748
            let parts: Vec<&str> = text.split('\0').collect();
1749
            let instance = parts.first().unwrap_or(&"").to_string();
1750
            let class = parts.get(1).unwrap_or(&"").to_string();
1751
            return (instance, class);
1752
        }
1753
1754
        (String::new(), String::new())
1755
    }
1756
1757
    fn apply_rules(&mut self, window: Window) -> WmResult<()> {
1758
        let (instance, class) = self.get_window_class_instance(window);
1759
        let title = self
1760
            .clients
1761
            .get(&window)
1762
            .map(|c| c.name.clone())
1763
            .unwrap_or_default();
1764
1765
        let mut rule_tags: Option<u32> = None;
1766
        let mut rule_floating: Option<bool> = None;
1767
        let mut rule_monitor: Option<usize> = None;
1768
        let mut rule_focus = false;
1769
1770
        for rule in &self.config.window_rules {
1771
            if rule.matches(&class, &instance, &title) {
1772
                if rule.tags.is_some() {
1773
                    rule_tags = rule.tags;
1774
                }
1775
                if rule.is_floating.is_some() {
1776
                    rule_floating = rule.is_floating;
1777
                }
1778
                if rule.monitor.is_some() {
1779
                    rule_monitor = rule.monitor;
1780
                }
1781
                rule_focus = rule.focus.unwrap_or(false);
1782
            }
1783
        }
1784
1785
        if let Some(client) = self.clients.get_mut(&window) {
1786
            if let Some(is_floating) = rule_floating {
1787
                client.is_floating = is_floating;
1788
                if is_floating {
1789
                    self.floating_windows.insert(window);
1790
                } else {
1791
                    self.floating_windows.remove(&window);
1792
                }
1793
            }
1794
1795
            if let Some(monitor_index) = rule_monitor
1796
                && monitor_index < self.monitors.len()
1797
            {
1798
                client.monitor_index = monitor_index;
1799
            }
1800
1801
            if let Some(tags) = rule_tags {
1802
                client.tags = tags;
1803
1804
                if rule_focus {
1805
                    let tag_index = unmask_tag(tags);
1806
                    let monitor_tagset = self
1807
                        .monitors
1808
                        .get(client.monitor_index)
1809
                        .map(|monitor| monitor.get_selected_tag())
1810
                        .unwrap_or(tag_mask(0));
1811
                    let is_tag_focused = monitor_tagset & tags == tags;
1812
1813
                    if !is_tag_focused {
1814
                        self.view_tag(tag_index)?;
1815
                    }
1816
                }
1817
            }
1818
        }
1819
1820
        Ok(())
1821
    }
1822
1823
    fn manage_window(&mut self, window: Window) -> WmResult<()> {
1824
        let geometry = self.connection.get_geometry(window)?.reply()?;
1825
        let border_width = self.config.border_width;
1826
1827
        let transient_parent = self.get_transient_parent(window);
1828
        let is_transient = transient_parent.is_some();
1829
1830
        let (monitor_index, tags) = if let Some(parent) = transient_parent {
1831
            if let Some(parent_client) = self.clients.get(&parent) {
1832
                (parent_client.monitor_index, parent_client.tags)
1833
            } else {
1834
                let tags = self
1835
                    .monitors
1836
                    .get(self.selected_monitor)
1837
                    .map(|monitor| monitor.tagset[monitor.selected_tags_index])
1838
                    .unwrap_or(tag_mask(0));
1839
                (self.selected_monitor, tags)
1840
            }
1841
        } else {
1842
            let tags = self
1843
                .monitors
1844
                .get(self.selected_monitor)
1845
                .map(|monitor| monitor.tagset[monitor.selected_tags_index])
1846
                .unwrap_or(tag_mask(0));
1847
            (self.selected_monitor, tags)
1848
        };
1849
1850
        let mut client = Client::new(window, monitor_index, tags);
1851
        client.x_position = geometry.x;
1852
        client.y_position = geometry.y;
1853
        client.width = geometry.width;
1854
        client.height = geometry.height;
1855
        client.old_x_position = geometry.x;
1856
        client.old_y_position = geometry.y;
1857
        client.old_width = geometry.width;
1858
        client.old_height = geometry.height;
1859
        client.old_border_width = geometry.border_width;
1860
        client.border_width = border_width as u16;
1861
1862
        self.clients.insert(window, client);
1863
        self.update_window_title(window)?;
1864
1865
        if !is_transient {
1866
            self.apply_rules(window)?;
1867
        }
1868
1869
        let client_monitor = self
1870
            .clients
1871
            .get(&window)
1872
            .map(|c| c.monitor_index)
1873
            .unwrap_or(monitor_index);
1874
        let monitor = &self.monitors[client_monitor];
1875
1876
        let mut x = self
1877
            .clients
1878
            .get(&window)
1879
            .map(|c| c.x_position as i32)
1880
            .unwrap_or(0);
1881
        let mut y = self
1882
            .clients
1883
            .get(&window)
1884
            .map(|c| c.y_position as i32)
1885
            .unwrap_or(0);
1886
        let w = self
1887
            .clients
1888
            .get(&window)
1889
            .map(|c| c.width as i32)
1890
            .unwrap_or(1);
1891
        let h = self
1892
            .clients
1893
            .get(&window)
1894
            .map(|c| c.height as i32)
1895
            .unwrap_or(1);
1896
        let bw = border_width as i32;
1897
1898
        if x + w + 2 * bw > monitor.window_area_x + monitor.window_area_width {
1899
            x = monitor.window_area_x + monitor.window_area_width - w - 2 * bw;
1900
        }
1901
        if y + h + 2 * bw > monitor.window_area_y + monitor.window_area_height {
1902
            y = monitor.window_area_y + monitor.window_area_height - h - 2 * bw;
1903
        }
1904
        x = x.max(monitor.window_area_x);
1905
        y = y.max(monitor.window_area_y);
1906
1907
        if let Some(c) = self.clients.get_mut(&window) {
1908
            c.x_position = x as i16;
1909
            c.y_position = y as i16;
1910
        }
1911
1912
        self.connection.configure_window(
1913
            window,
1914
            &ConfigureWindowAux::new().border_width(border_width),
1915
        )?;
1916
        self.connection.change_window_attributes(
1917
            window,
1918
            &ChangeWindowAttributesAux::new().border_pixel(self.config.border_unfocused),
1919
        )?;
1920
        self.send_configure_notify(window)?;
1921
        self.update_window_type(window)?;
1922
        self.update_size_hints(window)?;
1923
        self.update_window_hints(window)?;
1924
1925
        self.connection.change_window_attributes(
1926
            window,
1927
            &ChangeWindowAttributesAux::new().event_mask(
1928
                EventMask::ENTER_WINDOW
1929
                    | EventMask::FOCUS_CHANGE
1930
                    | EventMask::PROPERTY_CHANGE
1931
                    | EventMask::STRUCTURE_NOTIFY,
1932
            ),
1933
        )?;
1934
1935
        let is_fixed = self
1936
            .clients
1937
            .get(&window)
1938
            .map(|c| c.is_fixed)
1939
            .unwrap_or(false);
1940
        if let Some(c) = self.clients.get_mut(&window)
1941
            && !c.is_floating
1942
        {
1943
            c.is_floating = is_transient || is_fixed;
1944
            c.old_state = c.is_floating;
1945
        }
1946
1947
        if self
1948
            .clients
1949
            .get(&window)
1950
            .map(|c| c.is_floating)
1951
            .unwrap_or(false)
1952
        {
1953
            self.floating_windows.insert(window);
1954
            self.connection.configure_window(
1955
                window,
1956
                &ConfigureWindowAux::new().stack_mode(StackMode::ABOVE),
1957
            )?;
1958
        }
1959
1960
        self.attach_aside(window, client_monitor);
1961
        self.attach_stack(window, client_monitor);
1962
        self.windows.push(window);
1963
1964
        let off_screen_x = x + 2 * self.screen.width_in_pixels as i32;
1965
        self.connection.configure_window(
1966
            window,
1967
            &ConfigureWindowAux::new()
1968
                .x(off_screen_x)
1969
                .y(y)
1970
                .width(w as u32)
1971
                .height(h as u32),
1972
        )?;
1973
1974
        self.set_wm_state(window, 1)?;
1975
        self.update_client_list()?;
1976
1977
        let final_tags = self.clients.get(&window).map(|c| c.tags).unwrap_or(tags);
1978
        let _ = self.save_client_tag(window, final_tags);
1979
1980
        self.apply_layout()?;
1981
        self.connection.map_window(window)?;
1982
        self.focus(None)?;
1983
        self.update_bar()?;
1984
1985
        if self.layout.name() == "tabbed" {
1986
            self.update_tab_bars()?;
1987
        }
1988
1989
        Ok(())
1990
    }
1991
1992
    pub fn set_focus(&mut self, window: Window) -> WmResult<()> {
1993
        let never_focus = self
1994
            .clients
1995
            .get(&window)
1996
            .map(|c| c.never_focus)
1997
            .unwrap_or(false);
1998
1999
        if !never_focus {
2000
            self.connection
2001
                .set_input_focus(InputFocus::POINTER_ROOT, window, x11rb::CURRENT_TIME)?;
2002
2003
            self.connection.change_property(
2004
                PropMode::REPLACE,
2005
                self.root,
2006
                self.atoms.net_active_window,
2007
                AtomEnum::WINDOW,
2008
                32,
2009
                1,
2010
                &window.to_ne_bytes(),
2011
            )?;
2012
        }
2013
2014
        let _ = self.send_event(window, self.atoms.wm_take_focus);
2015
        self.connection.flush()?;
2016
2017
        Ok(())
2018
    }
2019
2020
    fn grabbuttons(&self, window: Window, focused: bool) -> WmResult<()> {
2021
        self.connection.ungrab_button(ButtonIndex::ANY, window, ModMask::ANY)?;
2022
2023
        if !focused {
2024
            self.connection.grab_button(
2025
                false,
2026
                window,
2027
                EventMask::BUTTON_PRESS | EventMask::BUTTON_RELEASE,
2028
                GrabMode::SYNC,
2029
                GrabMode::SYNC,
2030
                x11rb::NONE,
2031
                x11rb::NONE,
2032
                ButtonIndex::ANY,
2033
                ModMask::ANY,
2034
            )?;
2035
        }
2036
2037
        let ignore_modifiers = [
2038
            0u16,
2039
            u16::from(ModMask::LOCK),
2040
            u16::from(ModMask::M2),
2041
            u16::from(ModMask::LOCK | ModMask::M2),
2042
        ];
2043
2044
        for &ignore_mask in &ignore_modifiers {
2045
            let grab_mask = u16::from(self.config.modkey) | ignore_mask;
2046
2047
            self.connection.grab_button(
2048
                false,
2049
                window,
2050
                EventMask::BUTTON_PRESS | EventMask::BUTTON_RELEASE,
2051
                GrabMode::ASYNC,
2052
                GrabMode::SYNC,
2053
                x11rb::NONE,
2054
                x11rb::NONE,
2055
                ButtonIndex::M1,
2056
                grab_mask.into(),
2057
            )?;
2058
2059
            self.connection.grab_button(
2060
                false,
2061
                window,
2062
                EventMask::BUTTON_PRESS | EventMask::BUTTON_RELEASE,
2063
                GrabMode::ASYNC,
2064
                GrabMode::SYNC,
2065
                x11rb::NONE,
2066
                x11rb::NONE,
2067
                ButtonIndex::M3,
2068
                grab_mask.into(),
2069
            )?;
2070
        }
2071
2072
        Ok(())
2073
    }
2074
2075
    fn unfocus(&self, window: Window, reset_input_focus: bool) -> WmResult<()> {
2076
        if !self.windows.contains(&window) {
2077
            return Ok(());
2078
        }
2079
2080
        self.grabbuttons(window, false)?;
2081
2082
        self.connection.change_window_attributes(
2083
            window,
2084
            &ChangeWindowAttributesAux::new().border_pixel(self.config.border_unfocused),
2085
        )?;
2086
2087
        if reset_input_focus {
2088
            self.connection.set_input_focus(
2089
                InputFocus::POINTER_ROOT,
2090
                self.root,
2091
                x11rb::CURRENT_TIME,
2092
            )?;
2093
            self.connection.delete_property(self.root, self.atoms.net_active_window)?;
2094
        }
2095
2096
        Ok(())
2097
    }
2098
2099
    fn focus(&mut self, window: Option<Window>) -> WmResult<()> {
2100
        let old_selected = self
2101
            .monitors
2102
            .get(self.selected_monitor)
2103
            .and_then(|m| m.selected_client);
2104
2105
        let mut focus_client = window;
2106
        if focus_client.is_none()
2107
            || focus_client.is_some_and(|w| !self.is_visible(w))
2108
        {
2109
            let mut current = self
2110
                .monitors
2111
                .get(self.selected_monitor)
2112
                .and_then(|m| m.stack_head);
2113
2114
            focus_client = None;
2115
            while let Some(w) = current {
2116
                if self.is_visible(w) {
2117
                    focus_client = Some(w);
2118
                    break;
2119
                }
2120
                current = self.clients.get(&w).and_then(|c| c.stack_next);
2121
            }
2122
        }
2123
2124
        if old_selected != focus_client {
2125
            if let Some(old_win) = old_selected {
2126
                self.unfocus(old_win, false)?;
2127
            }
2128
        }
2129
2130
        if let Some(win) = focus_client {
2131
            let monitor_idx = self
2132
                .clients
2133
                .get(&win)
2134
                .map(|c| c.monitor_index)
2135
                .unwrap_or(self.selected_monitor);
2136
2137
            if monitor_idx != self.selected_monitor {
2138
                self.selected_monitor = monitor_idx;
2139
            }
2140
2141
            if self.clients.get(&win).is_some_and(|c| c.is_urgent) {
2142
                self.set_urgent(win, false)?;
2143
            }
2144
2145
            self.detach_stack(win);
2146
            self.attach_stack(win, monitor_idx);
2147
2148
            self.grabbuttons(win, true)?;
2149
2150
            self.connection.change_window_attributes(
2151
                win,
2152
                &ChangeWindowAttributesAux::new().border_pixel(self.config.border_focused),
2153
            )?;
2154
2155
            let never_focus = self
2156
                .clients
2157
                .get(&win)
2158
                .map(|client| client.never_focus)
2159
                .unwrap_or(false);
2160
2161
            if !never_focus {
2162
                self.connection
2163
                    .set_input_focus(InputFocus::POINTER_ROOT, win, x11rb::CURRENT_TIME)?;
2164
2165
                self.connection.change_property(
2166
                    PropMode::REPLACE,
2167
                    self.root,
2168
                    self.atoms.net_active_window,
2169
                    AtomEnum::WINDOW,
2170
                    32,
2171
                    1,
2172
                    &win.to_ne_bytes(),
2173
                )?;
2174
            }
2175
2176
            let _ = self.send_event(win, self.atoms.wm_take_focus);
2177
2178
            if let Some(monitor) = self.monitors.get_mut(self.selected_monitor) {
2179
                monitor.selected_client = Some(win);
2180
            }
2181
2182
            self.previous_focused = Some(win);
2183
        } else {
2184
            self.connection.set_input_focus(
2185
                InputFocus::POINTER_ROOT,
2186
                self.root,
2187
                x11rb::CURRENT_TIME,
2188
            )?;
2189
2190
            self.connection
2191
                .delete_property(self.root, self.atoms.net_active_window)?;
2192
2193
            if let Some(monitor) = self.monitors.get_mut(self.selected_monitor) {
2194
                monitor.selected_client = None;
2195
            }
2196
        }
2197
2198
        self.connection.flush()?;
2199
2200
        Ok(())
2201
    }
2202
2203
    fn restack(&mut self) -> WmResult<()> {
2204
        let monitor = match self.monitors.get(self.selected_monitor) {
2205
            Some(m) => m,
2206
            None => return Ok(()),
2207
        };
2208
2209
        let mut windows_to_restack: Vec<Window> = Vec::new();
2210
2211
        if let Some(selected) = monitor.selected_client
2212
            && self.floating_windows.contains(&selected)
2213
        {
2214
            windows_to_restack.push(selected);
2215
        }
2216
2217
        let mut current = monitor.stack_head;
2218
        while let Some(win) = current {
2219
            if self.windows.contains(&win)
2220
                && self.floating_windows.contains(&win)
2221
                && Some(win) != monitor.selected_client
2222
            {
2223
                windows_to_restack.push(win);
2224
            }
2225
            current = self.clients.get(&win).and_then(|c| c.stack_next);
2226
        }
2227
2228
        current = monitor.stack_head;
2229
        while let Some(win) = current {
2230
            if self.windows.contains(&win) && !self.floating_windows.contains(&win) {
2231
                windows_to_restack.push(win);
2232
            }
2233
            current = self.clients.get(&win).and_then(|c| c.stack_next);
2234
        }
2235
2236
        for (i, &win) in windows_to_restack.iter().enumerate() {
2237
            if i == 0 {
2238
                self.connection.configure_window(
2239
                    win,
2240
                    &ConfigureWindowAux::new().stack_mode(StackMode::ABOVE),
2241
                )?;
2242
            } else {
2243
                self.connection.configure_window(
2244
                    win,
2245
                    &ConfigureWindowAux::new()
2246
                        .sibling(windows_to_restack[i - 1])
2247
                        .stack_mode(StackMode::BELOW),
2248
                )?;
2249
            }
2250
        }
2251
2252
        Ok(())
2253
    }
2254
2255
    fn focusstack(&mut self, direction: i32) -> WmResult<()> {
2256
        let monitor = match self.monitors.get(self.selected_monitor) {
2257
            Some(monitor) => monitor,
2258
            None => return Ok(()),
2259
        };
2260
2261
        let selected_window = match monitor.selected_client {
2262
            Some(window) => window,
2263
            None => return Ok(()),
2264
        };
2265
2266
        let selected_is_fullscreen = self
2267
            .clients
2268
            .get(&selected_window)
2269
            .map(|client| client.is_fullscreen)
2270
            .unwrap_or(false);
2271
2272
        if selected_is_fullscreen {
2273
            return Ok(());
2274
        }
2275
2276
        let selected_tags = monitor.tagset[monitor.selected_tags_index];
2277
2278
        let mut stack_windows: Vec<Window> = Vec::new();
2279
        let mut current_window = monitor.clients_head;
2280
        while let Some(window) = current_window {
2281
            if let Some(client) = self.clients.get(&window) {
2282
                if client.tags & selected_tags != 0 && !client.is_floating {
2283
                    stack_windows.push(window);
2284
                }
2285
                current_window = client.next;
2286
            } else {
2287
                break;
2288
            }
2289
        }
2290
2291
        if stack_windows.is_empty() {
2292
            return Ok(());
2293
        }
2294
2295
        let current_index = stack_windows
2296
            .iter()
2297
            .position(|&window| window == selected_window);
2298
2299
        let next_window = if let Some(index) = current_index {
2300
            if direction > 0 {
2301
                if index + 1 < stack_windows.len() {
2302
                    stack_windows[index + 1]
2303
                } else {
2304
                    stack_windows[0]
2305
                }
2306
            } else if index > 0 {
2307
                stack_windows[index - 1]
2308
            } else {
2309
                stack_windows[stack_windows.len() - 1]
2310
            }
2311
        } else {
2312
            return Ok(());
2313
        };
2314
2315
        self.focus(Some(next_window))?;
2316
        self.restack()?;
2317
        self.update_tab_bars()?;
2318
2319
        Ok(())
2320
    }
2321
2322
    pub fn move_stack(&mut self, direction: i32) -> WmResult<()> {
2323
        let monitor_index = self.selected_monitor;
2324
        let monitor = match self.monitors.get(monitor_index) {
2325
            Some(m) => m.clone(),
2326
            None => return Ok(()),
2327
        };
2328
2329
        let selected = match monitor.selected_client {
2330
            Some(win) => win,
2331
            None => return Ok(()),
2332
        };
2333
2334
        let selected_client = match self.clients.get(&selected) {
2335
            Some(c) => c,
2336
            None => return Ok(()),
2337
        };
2338
2339
        let target = if direction > 0 {
2340
            let next = self.next_tiled(selected_client.next, &monitor);
2341
            if next.is_some() {
2342
                next
2343
            } else {
2344
                self.next_tiled(monitor.clients_head, &monitor)
2345
            }
2346
        } else {
2347
            let mut previous = None;
2348
            let mut current = monitor.clients_head;
2349
            while let Some(window) = current {
2350
                if window == selected {
2351
                    break;
2352
                }
2353
                if let Some(client) = self.clients.get(&window) {
2354
                    let visible_tags = client.tags & monitor.tagset[monitor.selected_tags_index];
2355
                    if visible_tags != 0 && !client.is_floating {
2356
                        previous = Some(window);
2357
                    }
2358
                    current = client.next;
2359
                } else {
2360
                    break;
2361
                }
2362
            }
2363
            if previous.is_none() {
2364
                let mut last = None;
2365
                let mut current = monitor.clients_head;
2366
                while let Some(window) = current {
2367
                    if let Some(client) = self.clients.get(&window) {
2368
                        let visible_tags =
2369
                            client.tags & monitor.tagset[monitor.selected_tags_index];
2370
                        if visible_tags != 0 && !client.is_floating {
2371
                            last = Some(window);
2372
                        }
2373
                        current = client.next;
2374
                    } else {
2375
                        break;
2376
                    }
2377
                }
2378
                last
2379
            } else {
2380
                previous
2381
            }
2382
        };
2383
2384
        let target = match target {
2385
            Some(t) if t != selected => t,
2386
            _ => return Ok(()),
2387
        };
2388
2389
        let mut prev_selected = None;
2390
        let mut prev_target = None;
2391
        let mut current = monitor.clients_head;
2392
2393
        while let Some(window) = current {
2394
            if let Some(client) = self.clients.get(&window) {
2395
                if client.next == Some(selected) {
2396
                    prev_selected = Some(window);
2397
                }
2398
                if client.next == Some(target) {
2399
                    prev_target = Some(window);
2400
                }
2401
                current = client.next;
2402
            } else {
2403
                break;
2404
            }
2405
        }
2406
2407
        let selected_next = self.clients.get(&selected).and_then(|c| c.next);
2408
        let target_next = self.clients.get(&target).and_then(|c| c.next);
2409
2410
        let temp = if selected_next == Some(target) {
2411
            Some(selected)
2412
        } else {
2413
            selected_next
2414
        };
2415
2416
        if let Some(client) = self.clients.get_mut(&selected) {
2417
            client.next = if target_next == Some(selected) {
2418
                Some(target)
2419
            } else {
2420
                target_next
2421
            };
2422
        }
2423
2424
        if let Some(client) = self.clients.get_mut(&target) {
2425
            client.next = temp;
2426
        }
2427
2428
        if let Some(prev) = prev_selected
2429
            && prev != target
2430
            && let Some(client) = self.clients.get_mut(&prev)
2431
        {
2432
            client.next = Some(target);
2433
        }
2434
2435
        if let Some(prev) = prev_target
2436
            && prev != selected
2437
            && let Some(client) = self.clients.get_mut(&prev)
2438
        {
2439
            client.next = Some(selected);
2440
        }
2441
2442
        if let Some(monitor) = self.monitors.get_mut(monitor_index) {
2443
            if monitor.clients_head == Some(selected) {
2444
                monitor.clients_head = Some(target);
2445
            } else if monitor.clients_head == Some(target) {
2446
                monitor.clients_head = Some(selected);
2447
            }
2448
        }
2449
2450
        self.apply_layout()?;
2451
        Ok(())
2452
    }
2453
2454
    pub fn focus_monitor(&mut self, direction: i32) -> WmResult<()> {
2455
        if self.monitors.len() <= 1 {
2456
            return Ok(());
2457
        }
2458
2459
        let target_monitor = match self.get_adjacent_monitor(direction) {
2460
            Some(idx) if idx != self.selected_monitor => idx,
2461
            _ => return Ok(()),
2462
        };
2463
2464
        let old_selected = self
2465
            .monitors
2466
            .get(self.selected_monitor)
2467
            .and_then(|m| m.selected_client);
2468
2469
        if let Some(win) = old_selected {
2470
            self.unfocus(win, true)?;
2471
        }
2472
2473
        self.selected_monitor = target_monitor;
2474
        self.focus(None)?;
2475
2476
        Ok(())
2477
    }
2478
2479
    pub fn send_window_to_adjacent_monitor(&mut self, direction: i32) -> WmResult<()> {
2480
        if self.monitors.len() <= 1 {
2481
            return Ok(());
2482
        }
2483
2484
        let selected_window = self
2485
            .monitors
2486
            .get(self.selected_monitor)
2487
            .and_then(|m| m.selected_client);
2488
2489
        let window = match selected_window {
2490
            Some(win) => win,
2491
            None => return Ok(()),
2492
        };
2493
2494
        let target_monitor = match self.get_adjacent_monitor(direction) {
2495
            Some(idx) => idx,
2496
            None => return Ok(()),
2497
        };
2498
2499
        self.move_window_to_monitor(window, target_monitor)?;
2500
2501
        Ok(())
2502
    }
2503
2504
    fn drag_window(&mut self, window: Window) -> WmResult<()> {
2505
        let is_fullscreen = self
2506
            .clients
2507
            .get(&window)
2508
            .map(|c| c.is_fullscreen)
2509
            .unwrap_or(false);
2510
2511
        if is_fullscreen {
2512
            return Ok(());
2513
        }
2514
2515
        let client_info = self.clients.get(&window).map(|c| {
2516
            (
2517
                c.x_position,
2518
                c.y_position,
2519
                c.width,
2520
                c.height,
2521
                c.is_floating,
2522
                c.monitor_index,
2523
            )
2524
        });
2525
2526
        let Some((orig_x, orig_y, width, height, was_floating, monitor_idx)) = client_info else {
2527
            return Ok(());
2528
        };
2529
2530
        let monitor = self.monitors.get(monitor_idx).cloned();
2531
        let Some(monitor) = monitor else {
2532
            return Ok(());
2533
        };
2534
2535
        let snap = 32;
2536
        let is_normie = self.layout.name() == "normie";
2537
2538
        if !was_floating && !is_normie {
2539
            self.toggle_floating()?;
2540
        }
2541
2542
        self.connection
2543
            .grab_pointer(
2544
                false,
2545
                self.root,
2546
                EventMask::POINTER_MOTION | EventMask::BUTTON_RELEASE | EventMask::BUTTON_PRESS,
2547
                GrabMode::ASYNC,
2548
                GrabMode::ASYNC,
2549
                x11rb::NONE,
2550
                x11rb::NONE,
2551
                x11rb::CURRENT_TIME,
2552
            )?
2553
            .reply()?;
2554
2555
        let pointer = self.connection.query_pointer(self.root)?.reply()?;
2556
        let (start_x, start_y) = (pointer.root_x as i32, pointer.root_y as i32);
2557
2558
        let mut last_time = 0u32;
2559
2560
        loop {
2561
            let event = self.connection.wait_for_event()?;
2562
            match event {
2563
                Event::ConfigureRequest(_) | Event::MapRequest(_) | Event::Expose(_) => {}
2564
                Event::MotionNotify(e) => {
2565
                    if e.time.wrapping_sub(last_time) <= 16 {
2566
                        continue;
2567
                    }
2568
                    last_time = e.time;
2569
2570
                    let mut new_x = orig_x as i32 + (e.root_x as i32 - start_x);
2571
                    let mut new_y = orig_y as i32 + (e.root_y as i32 - start_y);
2572
2573
                    if (monitor.window_area_x - new_x).abs() < snap {
2574
                        new_x = monitor.window_area_x;
2575
                    } else if ((monitor.window_area_x + monitor.window_area_width)
2576
                        - (new_x + width as i32))
2577
                        .abs()
2578
                        < snap
2579
                    {
2580
                        new_x = monitor.window_area_x + monitor.window_area_width - width as i32;
2581
                    }
2582
2583
                    if (monitor.window_area_y - new_y).abs() < snap {
2584
                        new_y = monitor.window_area_y;
2585
                    } else if ((monitor.window_area_y + monitor.window_area_height)
2586
                        - (new_y + height as i32))
2587
                        .abs()
2588
                        < snap
2589
                    {
2590
                        new_y = monitor.window_area_y + monitor.window_area_height - height as i32;
2591
                    }
2592
2593
                    let should_resize = is_normie
2594
                        || self
2595
                            .clients
2596
                            .get(&window)
2597
                            .map(|c| c.is_floating)
2598
                            .unwrap_or(false);
2599
2600
                    if should_resize {
2601
                        if let Some(client) = self.clients.get_mut(&window) {
2602
                            client.x_position = new_x as i16;
2603
                            client.y_position = new_y as i16;
2604
                        }
2605
2606
                        self.connection.configure_window(
2607
                            window,
2608
                            &ConfigureWindowAux::new().x(new_x).y(new_y),
2609
                        )?;
2610
                        self.connection.flush()?;
2611
                    }
2612
                }
2613
                Event::ButtonRelease(_) => break,
2614
                _ => {}
2615
            }
2616
        }
2617
2618
        self.connection
2619
            .ungrab_pointer(x11rb::CURRENT_TIME)?
2620
            .check()?;
2621
2622
        let final_client = self
2623
            .clients
2624
            .get(&window)
2625
            .map(|c| (c.x_position, c.y_position, c.width, c.height));
2626
2627
        if let Some((x, y, w, h)) = final_client {
2628
            let new_monitor = self.get_monitor_for_rect(x as i32, y as i32, w as i32, h as i32);
2629
            if new_monitor != monitor_idx {
2630
                self.move_window_to_monitor(window, new_monitor)?;
2631
                self.selected_monitor = new_monitor;
2632
                self.focus(None)?;
2633
            }
2634
        }
2635
2636
        if self.config.auto_tile && !was_floating && !is_normie {
2637
            let drop_monitor_idx = self
2638
                .clients
2639
                .get(&window)
2640
                .map(|c| c.monitor_index)
2641
                .unwrap_or(monitor_idx);
2642
2643
            if let Some((x, y, w, h)) = final_client {
2644
                let center = (x as i32 + w as i32 / 2, y as i32 + h as i32 / 2);
2645
                if let Some(target) = self.tiled_window_at(window, drop_monitor_idx, center) {
2646
                    self.detach(window);
2647
                    self.insert_before(window, target, drop_monitor_idx);
2648
                }
2649
            }
2650
2651
            self.floating_windows.remove(&window);
2652
            if let Some(client) = self.clients.get_mut(&window) {
2653
                client.is_floating = false;
2654
            }
2655
            self.apply_layout()?;
2656
        }
2657
2658
        Ok(())
2659
    }
2660
2661
    fn tiled_window_at(
2662
        &self,
2663
        exclude: Window,
2664
        monitor_idx: usize,
2665
        (px, py): (i32, i32),
2666
    ) -> Option<Window> {
2667
        let monitor = self.monitors.get(monitor_idx)?;
2668
        let tags = monitor.tagset[monitor.selected_tags_index];
2669
        let mut current = monitor.clients_head;
2670
2671
        while let Some(win) = current {
2672
            let c = self.clients.get(&win)?;
2673
            current = c.next;
2674
2675
            if win == exclude || c.is_floating || (c.tags & tags) == 0 {
2676
                continue;
2677
            }
2678
2679
            let (x, y) = (c.x_position as i32, c.y_position as i32);
2680
            let (w, h) = (
2681
                c.width as i32 + c.border_width as i32 * 2,
2682
                c.height as i32 + c.border_width as i32 * 2,
2683
            );
2684
2685
            if px >= x && px < x + w && py >= y && py < y + h {
2686
                return Some(win);
2687
            }
2688
        }
2689
        None
2690
    }
2691
2692
    fn insert_before(&mut self, window: Window, target: Window, monitor_idx: usize) {
2693
        let Some(monitor) = self.monitors.get_mut(monitor_idx) else {
2694
            return;
2695
        };
2696
2697
        if monitor.clients_head == Some(target) {
2698
            if let Some(c) = self.clients.get_mut(&window) {
2699
                c.next = Some(target);
2700
            }
2701
            monitor.clients_head = Some(window);
2702
            return;
2703
        }
2704
2705
        let mut current = monitor.clients_head;
2706
        while let Some(w) = current {
2707
            let Some(c) = self.clients.get(&w) else { break };
2708
            if c.next != Some(target) {
2709
                current = c.next;
2710
                continue;
2711
            }
2712
            if let Some(prev) = self.clients.get_mut(&w) {
2713
                prev.next = Some(window);
2714
            }
2715
            if let Some(inserted) = self.clients.get_mut(&window) {
2716
                inserted.next = Some(target);
2717
            }
2718
            break;
2719
        }
2720
    }
2721
2722
    fn resize_window_with_mouse(&mut self, window: Window) -> WmResult<()> {
2723
        let is_fullscreen = self
2724
            .clients
2725
            .get(&window)
2726
            .map(|c| c.is_fullscreen)
2727
            .unwrap_or(false);
2728
2729
        if is_fullscreen {
2730
            return Ok(());
2731
        }
2732
2733
        let client_info = self.clients.get(&window).map(|c| {
2734
            (
2735
                c.x_position,
2736
                c.y_position,
2737
                c.width,
2738
                c.height,
2739
                c.border_width,
2740
                c.is_floating,
2741
                c.monitor_index,
2742
            )
2743
        });
2744
2745
        let Some((
2746
            orig_x,
2747
            orig_y,
2748
            orig_width,
2749
            orig_height,
2750
            border_width,
2751
            was_floating,
2752
            monitor_idx,
2753
        )) = client_info
2754
        else {
2755
            return Ok(());
2756
        };
2757
2758
        let monitor = match self.monitors.get(monitor_idx) {
2759
            Some(m) => m,
2760
            None => return Ok(()),
2761
        };
2762
2763
        let is_normie = self.layout.name() == "normie";
2764
2765
        if self.config.auto_tile && !was_floating && !is_normie {
2766
            let mut tiled_count = 0;
2767
            let mut current = monitor.clients_head;
2768
            while let Some(w) = current {
2769
                if let Some(c) = self.clients.get(&w) {
2770
                    let visible = (c.tags & monitor.tagset[monitor.selected_tags_index]) != 0;
2771
                    if visible && !c.is_floating {
2772
                        tiled_count += 1;
2773
                    }
2774
                    current = c.next;
2775
                } else {
2776
                    break;
2777
                }
2778
            }
2779
            if tiled_count <= 1 {
2780
                return Ok(());
2781
            }
2782
        }
2783
2784
        if !was_floating && !is_normie {
2785
            self.toggle_floating()?;
2786
        }
2787
2788
        self.connection.warp_pointer(
2789
            x11rb::NONE,
2790
            window,
2791
            0,
2792
            0,
2793
            0,
2794
            0,
2795
            (orig_width + border_width - 1) as i16,
2796
            (orig_height + border_width - 1) as i16,
2797
        )?;
2798
2799
        self.connection
2800
            .grab_pointer(
2801
                false,
2802
                self.root,
2803
                EventMask::POINTER_MOTION | EventMask::BUTTON_RELEASE | EventMask::BUTTON_PRESS,
2804
                GrabMode::ASYNC,
2805
                GrabMode::ASYNC,
2806
                x11rb::NONE,
2807
                x11rb::NONE,
2808
                x11rb::CURRENT_TIME,
2809
            )?
2810
            .reply()?;
2811
2812
        let mut last_time = 0u32;
2813
2814
        loop {
2815
            let event = self.connection.wait_for_event()?;
2816
            match event {
2817
                Event::ConfigureRequest(_) | Event::MapRequest(_) | Event::Expose(_) => {}
2818
                Event::MotionNotify(e) => {
2819
                    if e.time.wrapping_sub(last_time) <= 16 {
2820
                        continue;
2821
                    }
2822
                    last_time = e.time;
2823
2824
                    let new_width = ((e.root_x as i32 - orig_x as i32 - 2 * border_width as i32
2825
                        + 1)
2826
                    .max(1)) as u32;
2827
                    let new_height = ((e.root_y as i32 - orig_y as i32 - 2 * border_width as i32
2828
                        + 1)
2829
                    .max(1)) as u32;
2830
2831
                    let should_resize = is_normie
2832
                        || self
2833
                            .clients
2834
                            .get(&window)
2835
                            .map(|c| c.is_floating)
2836
                            .unwrap_or(false);
2837
2838
                    if should_resize && let Some(client) = self.clients.get(&window).cloned() {
2839
                        let (_, _, hint_width, hint_height, _) = self.apply_size_hints(
2840
                            window,
2841
                            client.x_position as i32,
2842
                            client.y_position as i32,
2843
                            new_width as i32,
2844
                            new_height as i32,
2845
                        );
2846
2847
                        if let Some(client_mut) = self.clients.get_mut(&window) {
2848
                            client_mut.width = hint_width as u16;
2849
                            client_mut.height = hint_height as u16;
2850
                        }
2851
2852
                        self.connection.configure_window(
2853
                            window,
2854
                            &ConfigureWindowAux::new()
2855
                                .width(hint_width as u32)
2856
                                .height(hint_height as u32),
2857
                        )?;
2858
                        self.connection.flush()?;
2859
                    }
2860
                }
2861
                Event::ButtonRelease(_) => break,
2862
                _ => {}
2863
            }
2864
        }
2865
2866
        let final_client = self.clients.get(&window).map(|c| (c.width, c.border_width));
2867
2868
        if let Some((w, bw)) = final_client {
2869
            self.connection.warp_pointer(
2870
                x11rb::NONE,
2871
                window,
2872
                0,
2873
                0,
2874
                0,
2875
                0,
2876
                (w + bw - 1) as i16,
2877
                (w + bw - 1) as i16,
2878
            )?;
2879
        }
2880
2881
        self.connection
2882
            .ungrab_pointer(x11rb::CURRENT_TIME)?
2883
            .check()?;
2884
2885
        let final_client_pos = self
2886
            .clients
2887
            .get(&window)
2888
            .map(|c| (c.x_position, c.y_position, c.width, c.height));
2889
2890
        if let Some((x, y, w, h)) = final_client_pos {
2891
            let new_monitor = self.get_monitor_for_rect(x as i32, y as i32, w as i32, h as i32);
2892
            if new_monitor != monitor_idx {
2893
                self.move_window_to_monitor(window, new_monitor)?;
2894
                self.selected_monitor = new_monitor;
2895
                self.focus(None)?;
2896
            }
2897
        }
2898
2899
        if self.config.auto_tile && !was_floating && !is_normie {
2900
            self.floating_windows.remove(&window);
2901
            if let Some(client) = self.clients.get_mut(&window) {
2902
                client.is_floating = false;
2903
            }
2904
            self.apply_layout()?;
2905
        }
2906
2907
        Ok(())
2908
    }
2909
2910
    fn handle_event(&mut self, event: Event) -> WmResult<Control> {
2911
        match event {
2912
            Event::KeyPress(ref key_event) if key_event.event == self.overlay.window() => {
2913
                if self.overlay.is_visible()
2914
                    && let Err(error) = self.overlay.hide(&self.connection)
2915
                {
2916
                    eprintln!("Failed to hide overlay: {:?}", error);
2917
                }
2918
                return Ok(Control::Continue);
2919
            }
2920
            Event::ButtonPress(ref button_event) if button_event.event == self.overlay.window() => {
2921
                if self.overlay.is_visible()
2922
                    && let Err(error) = self.overlay.hide(&self.connection)
2923
                {
2924
                    eprintln!("Failed to hide overlay: {:?}", error);
2925
                }
2926
                return Ok(Control::Continue);
2927
            }
2928
            Event::Expose(ref expose_event) if expose_event.window == self.overlay.window() => {
2929
                if self.overlay.is_visible()
2930
                    && let Err(error) = self.overlay.draw(&self.connection, &self.font)
2931
                {
2932
                    eprintln!("Failed to draw overlay: {:?}", error);
2933
                }
2934
                return Ok(Control::Continue);
2935
            }
2936
            Event::KeyPress(ref e) if e.event == self.keybind_overlay.window() => {
2937
                if self.keybind_overlay.is_visible()
2938
                    && !self.keybind_overlay.should_suppress_input()
2939
                {
2940
                    use crate::keyboard::keysyms;
2941
                    if let Some(mapping) = &self.keyboard_mapping {
2942
                        let keysym = mapping.keycode_to_keysym(e.detail);
2943
                        let is_escape = keysym == keysyms::XK_ESCAPE;
2944
                        let is_q = keysym == keysyms::XK_Q || keysym == 0x0051;
2945
                        if (is_escape || is_q)
2946
                            && let Err(error) = self.keybind_overlay.hide(&self.connection)
2947
                        {
2948
                            eprintln!("Failed to hide keybind overlay: {:?}", error);
2949
                        }
2950
                    }
2951
                }
2952
                return Ok(Control::Continue);
2953
            }
2954
            Event::ButtonPress(ref e) if e.event == self.keybind_overlay.window() => {
2955
                self.connection
2956
                    .allow_events(Allow::REPLAY_POINTER, e.time)?;
2957
                return Ok(Control::Continue);
2958
            }
2959
            Event::Expose(ref expose_event)
2960
                if expose_event.window == self.keybind_overlay.window() =>
2961
            {
2962
                if self.keybind_overlay.is_visible()
2963
                    && let Err(error) = self.keybind_overlay.draw(&self.connection, &self.font)
2964
                {
2965
                    eprintln!("Failed to draw keybind overlay: {:?}", error);
2966
                }
2967
                return Ok(Control::Continue);
2968
            }
2969
            Event::MapRequest(event) => {
2970
                let attrs = match self.connection.get_window_attributes(event.window)?.reply() {
2971
                    Ok(attrs) => attrs,
2972
                    Err(_) => return Ok(Control::Continue),
2973
                };
2974
2975
                if attrs.override_redirect {
2976
                    return Ok(Control::Continue);
2977
                }
2978
2979
                if !self.windows.contains(&event.window) {
2980
                    self.manage_window(event.window)?;
2981
                }
2982
            }
2983
            Event::UnmapNotify(event) => {
2984
                if self.windows.contains(&event.window) && self.is_window_visible(event.window) {
2985
                    self.remove_window(event.window, false)?;
2986
                }
2987
            }
2988
            Event::DestroyNotify(event) => {
2989
                if self.windows.contains(&event.window) {
2990
                    self.remove_window(event.window, true)?;
2991
                }
2992
            }
2993
            Event::PropertyNotify(event) => {
2994
                if event.state == Property::DELETE {
2995
                    return Ok(Control::Continue);
2996
                }
2997
2998
                if !self.clients.contains_key(&event.window) {
2999
                    return Ok(Control::Continue);
3000
                }
3001
3002
                if event.atom == AtomEnum::WM_TRANSIENT_FOR.into() {
3003
                    let is_floating = self
3004
                        .clients
3005
                        .get(&event.window)
3006
                        .map(|c| c.is_floating)
3007
                        .unwrap_or(false);
3008
                    if !is_floating
3009
                        && let Some(parent) = self.get_transient_parent(event.window)
3010
                        && self.clients.contains_key(&parent)
3011
                    {
3012
                        if let Some(c) = self.clients.get_mut(&event.window) {
3013
                            c.is_floating = true;
3014
                        }
3015
                        self.floating_windows.insert(event.window);
3016
                        self.apply_layout()?;
3017
                    }
3018
                } else if event.atom == AtomEnum::WM_NORMAL_HINTS.into() {
3019
                    if let Some(c) = self.clients.get_mut(&event.window) {
3020
                        c.hints_valid = false;
3021
                    }
3022
                } else if event.atom == AtomEnum::WM_HINTS.into() {
3023
                    self.update_window_hints(event.window)?;
3024
                    self.update_bar()?;
3025
                }
3026
3027
                if event.atom == self.atoms.wm_name || event.atom == self.atoms.net_wm_name {
3028
                    let _ = self.update_window_title(event.window);
3029
                    if self.layout.name() == "tabbed" {
3030
                        self.update_tab_bars()?;
3031
                    }
3032
                }
3033
3034
                if event.atom == self.atoms.net_wm_window_type {
3035
                    self.update_window_type(event.window)?;
3036
                }
3037
            }
3038
            Event::EnterNotify(event) => {
3039
                if event.mode != x11rb::protocol::xproto::NotifyMode::NORMAL
3040
                    || event.detail == x11rb::protocol::xproto::NotifyDetail::INFERIOR
3041
                {
3042
                    return Ok(Control::Continue);
3043
                }
3044
                if self.windows.contains(&event.event) {
3045
                    if let Some(client) = self.clients.get(&event.event)
3046
                        && client.monitor_index != self.selected_monitor
3047
                    {
3048
                        if let Some(old_selected) = self
3049
                            .monitors
3050
                            .get(self.selected_monitor)
3051
                            .and_then(|monitor| monitor.selected_client)
3052
                        {
3053
                            self.unfocus(old_selected, false)?;
3054
                        }
3055
                        self.selected_monitor = client.monitor_index;
3056
                        self.update_bar()?;
3057
                    }
3058
                    self.focus(Some(event.event))?;
3059
                    self.update_tab_bars()?;
3060
                }
3061
            }
3062
            Event::MotionNotify(event) => {
3063
                if event.event != self.root {
3064
                    return Ok(Control::Continue);
3065
                }
3066
3067
                if let Some(monitor_index) =
3068
                    self.get_monitor_at_point(event.root_x as i32, event.root_y as i32)
3069
                    && monitor_index != self.selected_monitor
3070
                {
3071
                    if let Some(old_selected) = self
3072
                        .monitors
3073
                        .get(self.selected_monitor)
3074
                        .and_then(|monitor| monitor.selected_client)
3075
                    {
3076
                        self.unfocus(old_selected, true)?;
3077
                    }
3078
3079
                    self.selected_monitor = monitor_index;
3080
                    self.focus(None)?;
3081
                    self.update_bar()?;
3082
                    self.update_tab_bars()?;
3083
                }
3084
            }
3085
            Event::KeyPress(event) => {
3086
                let Some(mapping) = &self.keyboard_mapping else {
3087
                    return Ok(Control::Continue);
3088
                };
3089
3090
                let result = keyboard::handle_key_press(
3091
                    event,
3092
                    &self.config.keybindings,
3093
                    &self.keychord_state,
3094
                    mapping,
3095
                );
3096
3097
                match result {
3098
                    keyboard::handlers::KeychordResult::Completed(action, arg) => {
3099
                        self.keychord_state = keyboard::handlers::KeychordState::Idle;
3100
                        self.current_key = 0;
3101
                        self.grab_keys()?;
3102
                        self.update_bar()?;
3103
3104
                        match action {
3105
                            KeyAction::Quit => return Ok(Control::Quit),
3106
                            KeyAction::Restart => match self.try_reload_config() {
3107
                                Ok(()) => {
3108
                                    self.gaps_enabled = self.config.gaps_enabled;
3109
                                    self.error_message = None;
3110
                                    if let Err(error) = self.overlay.hide(&self.connection) {
3111
                                        eprintln!(
3112
                                            "Failed to hide overlay after config reload: {:?}",
3113
                                            error
3114
                                        );
3115
                                    }
3116
                                    self.apply_layout()?;
3117
                                    self.update_bar()?;
3118
                                }
3119
                                Err(err) => {
3120
                                    eprintln!("Config reload error: {}", err);
3121
                                    self.error_message = Some(err.to_string());
3122
                                    let monitor = &self.monitors[self.selected_monitor];
3123
                                    let monitor_x = monitor.screen_x as i16;
3124
                                    let monitor_y = monitor.screen_y as i16;
3125
                                    let screen_width = monitor.screen_width as u16;
3126
                                    let screen_height = monitor.screen_height as u16;
3127
                                    match self.overlay.show_error(
3128
                                        &self.connection,
3129
                                        &self.font,
3130
                                        err,
3131
                                        monitor_x,
3132
                                        monitor_y,
3133
                                        screen_width,
3134
                                        screen_height,
3135
                                    ) {
3136
                                        Ok(()) => eprintln!("Error modal displayed"),
3137
                                        Err(e) => eprintln!("Failed to show error modal: {:?}", e),
3138
                                    }
3139
                                }
3140
                            },
3141
                            _ => self.handle_key_action(action, &arg)?,
3142
                        }
3143
                    }
3144
                    keyboard::handlers::KeychordResult::InProgress(candidates) => {
3145
                        self.current_key += 1;
3146
                        self.keychord_state = keyboard::handlers::KeychordState::InProgress {
3147
                            candidates: candidates.clone(),
3148
                            keys_pressed: self.current_key,
3149
                        };
3150
                        self.grab_keys()?;
3151
                        self.update_bar()?;
3152
                    }
3153
                    keyboard::handlers::KeychordResult::Cancelled
3154
                    | keyboard::handlers::KeychordResult::None => {
3155
                        self.keychord_state = keyboard::handlers::KeychordState::Idle;
3156
                        self.current_key = 0;
3157
                        self.grab_keys()?;
3158
                        self.update_bar()?;
3159
                    }
3160
                }
3161
            }
3162
            Event::ButtonPress(event) => {
3163
                if self.keybind_overlay.is_visible()
3164
                    && event.event != self.keybind_overlay.window()
3165
                    && let Err(error) = self.keybind_overlay.hide(&self.connection)
3166
                {
3167
                    eprintln!("Failed to hide keybind overlay: {:?}", error);
3168
                }
3169
3170
                let is_bar_click = self
3171
                    .bars
3172
                    .iter()
3173
                    .enumerate()
3174
                    .find(|(_, bar)| bar.window() == event.event);
3175
3176
                if let Some((monitor_index, bar)) = is_bar_click {
3177
                    if let Some(tag_index) = bar.handle_click(event.event_x) {
3178
                        if monitor_index != self.selected_monitor {
3179
                            self.selected_monitor = monitor_index;
3180
                        }
3181
                        self.view_tag(tag_index)?;
3182
                    }
3183
                } else {
3184
                    let is_tab_bar_click = self
3185
                        .tab_bars
3186
                        .iter()
3187
                        .enumerate()
3188
                        .find(|(_, tab_bar)| tab_bar.window() == event.event);
3189
3190
                    if let Some((monitor_index, tab_bar)) = is_tab_bar_click {
3191
                        if monitor_index != self.selected_monitor {
3192
                            self.selected_monitor = monitor_index;
3193
                        }
3194
3195
                        let visible_windows: Vec<(Window, String)> = self
3196
                            .windows
3197
                            .iter()
3198
                            .filter_map(|&window| {
3199
                                if let Some(client) = self.clients.get(&window) {
3200
                                    if client.monitor_index != monitor_index
3201
                                        || self.floating_windows.contains(&window)
3202
                                        || self.fullscreen_windows.contains(&window)
3203
                                    {
3204
                                        return None;
3205
                                    }
3206
                                    let monitor_tags = self
3207
                                        .monitors
3208
                                        .get(monitor_index)
3209
                                        .map(|m| m.tagset[m.selected_tags_index])
3210
                                        .unwrap_or(0);
3211
                                    if (client.tags & monitor_tags) != 0 {
3212
                                        return Some((window, client.name.clone()));
3213
                                    }
3214
                                }
3215
                                None
3216
                            })
3217
                            .collect();
3218
3219
                        if let Some(clicked_window) =
3220
                            tab_bar.get_clicked_window(&visible_windows, event.event_x)
3221
                        {
3222
                            self.connection.configure_window(
3223
                                clicked_window,
3224
                                &ConfigureWindowAux::new().stack_mode(StackMode::ABOVE),
3225
                            )?;
3226
                            self.focus(Some(clicked_window))?;
3227
                            self.update_tab_bars()?;
3228
                        }
3229
                    } else if event.child != x11rb::NONE {
3230
                        self.focus(Some(event.child))?;
3231
                        self.restack()?;
3232
                        self.update_tab_bars()?;
3233
3234
                        let state_clean = u16::from(event.state)
3235
                            & !(u16::from(ModMask::LOCK) | u16::from(ModMask::M2));
3236
                        let modkey_held = state_clean & u16::from(self.config.modkey) != 0;
3237
3238
                        if modkey_held && event.detail == ButtonIndex::M1.into() {
3239
                            if self.clients.contains_key(&event.child) {
3240
                                self.drag_window(event.child)?;
3241
                            }
3242
                            self.connection
3243
                                .allow_events(Allow::REPLAY_POINTER, event.time)?;
3244
                        } else if modkey_held && event.detail == ButtonIndex::M3.into() {
3245
                            if self.clients.contains_key(&event.child) {
3246
                                self.resize_window_with_mouse(event.child)?;
3247
                            }
3248
                            self.connection
3249
                                .allow_events(Allow::REPLAY_POINTER, event.time)?;
3250
                        } else {
3251
                            self.connection
3252
                                .allow_events(Allow::REPLAY_POINTER, event.time)?;
3253
                        }
3254
                    } else if self.windows.contains(&event.event) {
3255
                        self.focus(Some(event.event))?;
3256
                        self.restack()?;
3257
                        self.update_tab_bars()?;
3258
3259
                        let state_clean = u16::from(event.state)
3260
                            & !(u16::from(ModMask::LOCK) | u16::from(ModMask::M2));
3261
                        let modkey_held = state_clean & u16::from(self.config.modkey) != 0;
3262
3263
                        if modkey_held && event.detail == ButtonIndex::M1.into() {
3264
                            self.drag_window(event.event)?;
3265
                            self.connection
3266
                                .allow_events(Allow::REPLAY_POINTER, event.time)?;
3267
                        } else if modkey_held && event.detail == ButtonIndex::M3.into() {
3268
                            self.resize_window_with_mouse(event.event)?;
3269
                            self.connection
3270
                                .allow_events(Allow::REPLAY_POINTER, event.time)?;
3271
                        } else {
3272
                            self.connection
3273
                                .allow_events(Allow::REPLAY_POINTER, event.time)?;
3274
                        }
3275
                    } else {
3276
                        self.connection
3277
                            .allow_events(Allow::REPLAY_POINTER, event.time)?;
3278
                    }
3279
                }
3280
            }
3281
            Event::Expose(event) => {
3282
                for bar in &mut self.bars {
3283
                    if event.window == bar.window() {
3284
                        bar.invalidate();
3285
                        self.update_bar()?;
3286
                        break;
3287
                    }
3288
                }
3289
                for _tab_bar in &self.tab_bars {
3290
                    if event.window == _tab_bar.window() {
3291
                        self.update_tab_bars()?;
3292
                        break;
3293
                    }
3294
                }
3295
            }
3296
            Event::ConfigureRequest(event) => {
3297
                if let Some(client) = self.clients.get(&event.window) {
3298
                    let monitor = &self.monitors[client.monitor_index];
3299
                    let is_floating = client.is_floating;
3300
                    let is_fullscreen = client.is_fullscreen;
3301
                    let has_layout = self.layout.name() != "normie";
3302
3303
                    if event.value_mask.contains(ConfigWindow::BORDER_WIDTH) {
3304
                        if let Some(c) = self.clients.get_mut(&event.window) {
3305
                            c.border_width = event.border_width;
3306
                        }
3307
                    } else if is_fullscreen {
3308
                        self.send_configure_notify(event.window)?;
3309
                    } else if is_floating || !has_layout {
3310
                        let mut x = client.x_position as i32;
3311
                        let mut y = client.y_position as i32;
3312
                        let mut w = client.width as i32;
3313
                        let mut h = client.height as i32;
3314
3315
                        if event.value_mask.contains(ConfigWindow::X) {
3316
                            if let Some(c) = self.clients.get_mut(&event.window) {
3317
                                c.old_x_position = c.x_position;
3318
                            }
3319
                            x = monitor.screen_x + event.x as i32;
3320
                        }
3321
                        if event.value_mask.contains(ConfigWindow::Y) {
3322
                            if let Some(c) = self.clients.get_mut(&event.window) {
3323
                                c.old_y_position = c.y_position;
3324
                            }
3325
                            y = monitor.screen_y + event.y as i32;
3326
                        }
3327
                        if event.value_mask.contains(ConfigWindow::WIDTH) {
3328
                            if let Some(c) = self.clients.get_mut(&event.window) {
3329
                                c.old_width = c.width;
3330
                            }
3331
                            w = event.width as i32;
3332
                        }
3333
                        if event.value_mask.contains(ConfigWindow::HEIGHT) {
3334
                            if let Some(c) = self.clients.get_mut(&event.window) {
3335
                                c.old_height = c.height;
3336
                            }
3337
                            h = event.height as i32;
3338
                        }
3339
3340
                        let bw = self.config.border_width as i32;
3341
                        let width_with_border = w + 2 * bw;
3342
                        let height_with_border = h + 2 * bw;
3343
3344
                        if (x + w) > monitor.screen_x + monitor.screen_width && is_floating {
3345
                            x = monitor.screen_x
3346
                                + (monitor.screen_width / 2 - width_with_border / 2);
3347
                        }
3348
                        if (y + h) > monitor.screen_y + monitor.screen_height && is_floating {
3349
                            y = monitor.screen_y
3350
                                + (monitor.screen_height / 2 - height_with_border / 2);
3351
                        }
3352
3353
                        if let Some(c) = self.clients.get_mut(&event.window) {
3354
                            c.x_position = x as i16;
3355
                            c.y_position = y as i16;
3356
                            c.width = w as u16;
3357
                            c.height = h as u16;
3358
                        }
3359
3360
                        let only_position_change = event.value_mask.contains(ConfigWindow::X)
3361
                            || event.value_mask.contains(ConfigWindow::Y);
3362
                        let no_size_change = !event.value_mask.contains(ConfigWindow::WIDTH)
3363
                            && !event.value_mask.contains(ConfigWindow::HEIGHT);
3364
                        if only_position_change && no_size_change {
3365
                            self.send_configure_notify(event.window)?;
3366
                        }
3367
3368
                        if self.is_visible(event.window) {
3369
                            self.connection.configure_window(
3370
                                event.window,
3371
                                &ConfigureWindowAux::new()
3372
                                    .x(x)
3373
                                    .y(y)
3374
                                    .width(w as u32)
3375
                                    .height(h as u32),
3376
                            )?;
3377
                        }
3378
                    } else {
3379
                        self.send_configure_notify(event.window)?;
3380
                    }
3381
                } else {
3382
                    let mut aux = ConfigureWindowAux::new();
3383
                    if event.value_mask.contains(ConfigWindow::X) {
3384
                        aux = aux.x(event.x as i32);
3385
                    }
3386
                    if event.value_mask.contains(ConfigWindow::Y) {
3387
                        aux = aux.y(event.y as i32);
3388
                    }
3389
                    if event.value_mask.contains(ConfigWindow::WIDTH) {
3390
                        aux = aux.width(event.width as u32);
3391
                    }
3392
                    if event.value_mask.contains(ConfigWindow::HEIGHT) {
3393
                        aux = aux.height(event.height as u32);
3394
                    }
3395
                    if event.value_mask.contains(ConfigWindow::BORDER_WIDTH) {
3396
                        aux = aux.border_width(event.border_width as u32);
3397
                    }
3398
                    if event.value_mask.contains(ConfigWindow::SIBLING) {
3399
                        aux = aux.sibling(event.sibling);
3400
                    }
3401
                    if event.value_mask.contains(ConfigWindow::STACK_MODE) {
3402
                        aux = aux.stack_mode(event.stack_mode);
3403
                    }
3404
                    self.connection.configure_window(event.window, &aux)?;
3405
                }
3406
                self.connection.flush()?;
3407
            }
3408
            Event::ClientMessage(event) => {
3409
                if !self.clients.contains_key(&event.window) {
3410
                    return Ok(Control::Continue);
3411
                }
3412
3413
                if event.type_ == self.atoms.net_wm_state {
3414
                    let data = event.data.as_data32();
3415
                    let atom1 = data.get(1).copied().unwrap_or(0);
3416
                    let atom2 = data.get(2).copied().unwrap_or(0);
3417
3418
                    if atom1 == self.atoms.net_wm_state_fullscreen
3419
                        || atom2 == self.atoms.net_wm_state_fullscreen
3420
                    {
3421
                        let action = data[0];
3422
                        let fullscreen = match action {
3423
                            1 => true,
3424
                            0 => false,
3425
                            2 => !self.fullscreen_windows.contains(&event.window),
3426
                            _ => return Ok(Control::Continue),
3427
                        };
3428
                        self.set_window_fullscreen(event.window, fullscreen)?;
3429
                        self.restack()?;
3430
                    }
3431
                } else if event.type_ == self.atoms.net_active_window {
3432
                    let selected_window = self
3433
                        .monitors
3434
                        .get(self.selected_monitor)
3435
                        .and_then(|m| m.selected_client);
3436
3437
                    let is_urgent = self
3438
                        .clients
3439
                        .get(&event.window)
3440
                        .map(|c| c.is_urgent)
3441
                        .unwrap_or(false);
3442
3443
                    if Some(event.window) != selected_window && !is_urgent {
3444
                        self.set_urgent(event.window, true)?;
3445
                    }
3446
                }
3447
            }
3448
            Event::FocusIn(event) => {
3449
                if !self.windows.contains(&event.event) {
3450
                    return Ok(Control::Continue);
3451
                }
3452
3453
                let event_window_visible = self.is_visible(event.event);
3454
3455
                if !event_window_visible {
3456
                    self.focus(None)?;
3457
                } else {
3458
                    let selected_window = self
3459
                        .monitors
3460
                        .get(self.selected_monitor)
3461
                        .and_then(|monitor| monitor.selected_client);
3462
3463
                    if let Some(focused_window) = selected_window
3464
                        && event.event != focused_window
3465
                    {
3466
                        self.set_focus(focused_window)?;
3467
                    }
3468
                }
3469
            }
3470
            Event::MappingNotify(event) => {
3471
                if event.request == x11rb::protocol::xproto::Mapping::KEYBOARD {
3472
                    self.grab_keys()?;
3473
                }
3474
            }
3475
            Event::ConfigureNotify(event) => {
3476
                if event.window == self.root {
3477
                    let old_width = self.screen.width_in_pixels;
3478
                    let old_height = self.screen.height_in_pixels;
3479
3480
                    if event.width != old_width || event.height != old_height {
3481
                        self.screen = self.connection.setup().roots[self.screen_number].clone();
3482
3483
                        for monitor_index in 0..self.monitors.len() {
3484
                            let monitor = &self.monitors[monitor_index];
3485
                            let monitor_x = monitor.screen_x;
3486
                            let monitor_y = monitor.screen_y;
3487
                            let monitor_width = monitor.screen_width as u32;
3488
                            let monitor_height = monitor.screen_height as u32;
3489
3490
                            let fullscreen_on_monitor: Vec<Window> = self
3491
                                .fullscreen_windows
3492
                                .iter()
3493
                                .filter(|&&window| {
3494
                                    self.clients
3495
                                        .get(&window)
3496
                                        .map(|client| client.monitor_index == monitor_index)
3497
                                        .unwrap_or(false)
3498
                                })
3499
                                .copied()
3500
                                .collect();
3501
3502
                            for window in fullscreen_on_monitor {
3503
                                self.connection.configure_window(
3504
                                    window,
3505
                                    &ConfigureWindowAux::new()
3506
                                        .x(monitor_x)
3507
                                        .y(monitor_y)
3508
                                        .width(monitor_width)
3509
                                        .height(monitor_height),
3510
                                )?;
3511
                            }
3512
                        }
3513
3514
                        self.apply_layout()?;
3515
                    }
3516
                }
3517
            }
3518
            _ => {}
3519
        }
3520
        Ok(Control::Continue)
3521
    }
3522
3523
    fn apply_layout(&mut self) -> WmResult<()> {
3524
        for monitor_index in 0..self.monitors.len() {
3525
            let stack_head = self.monitors.get(monitor_index).and_then(|m| m.stack_head);
3526
            self.showhide(stack_head)?;
3527
        }
3528
3529
        let is_normie = self.layout.name() == LayoutType::Normie.as_str();
3530
3531
        if !is_normie {
3532
            let monitor_count = self.monitors.len();
3533
            for monitor_index in 0..monitor_count {
3534
                let monitor = &self.monitors[monitor_index];
3535
                let border_width = self.config.border_width;
3536
3537
                let gaps = if self.gaps_enabled {
3538
                    GapConfig {
3539
                        inner_horizontal: self.config.gap_inner_horizontal,
3540
                        inner_vertical: self.config.gap_inner_vertical,
3541
                        outer_horizontal: self.config.gap_outer_horizontal,
3542
                        outer_vertical: self.config.gap_outer_vertical,
3543
                    }
3544
                } else {
3545
                    GapConfig {
3546
                        inner_horizontal: 0,
3547
                        inner_vertical: 0,
3548
                        outer_horizontal: 0,
3549
                        outer_vertical: 0,
3550
                    }
3551
                };
3552
3553
                let monitor_x = monitor.screen_x;
3554
                let monitor_y = monitor.screen_y;
3555
                let monitor_width = monitor.screen_width;
3556
                let monitor_height = monitor.screen_height;
3557
3558
                let mut visible: Vec<Window> = Vec::new();
3559
                let mut current = self.next_tiled(monitor.clients_head, monitor);
3560
                while let Some(window) = current {
3561
                    visible.push(window);
3562
                    if let Some(client) = self.clients.get(&window) {
3563
                        current = self.next_tiled(client.next, monitor);
3564
                    } else {
3565
                        break;
3566
                    }
3567
                }
3568
3569
                let bar_height = if self.show_bar {
3570
                    self.bars
3571
                        .get(monitor_index)
3572
                        .map(|bar| bar.height() as u32)
3573
                        .unwrap_or(0)
3574
                } else {
3575
                    0
3576
                };
3577
                let usable_height = monitor_height.saturating_sub(bar_height as i32);
3578
                let master_factor = monitor.master_factor;
3579
                let num_master = monitor.num_master;
3580
                let smartgaps_enabled = self.config.smartgaps_enabled;
3581
3582
                let geometries = self.layout.arrange(
3583
                    &visible,
3584
                    monitor_width as u32,
3585
                    usable_height as u32,
3586
                    &gaps,
3587
                    master_factor,
3588
                    num_master,
3589
                    smartgaps_enabled,
3590
                );
3591
3592
                for (window, geometry) in visible.iter().zip(geometries.iter()) {
3593
                    let mut adjusted_width = geometry.width.saturating_sub(2 * border_width);
3594
                    let mut adjusted_height = geometry.height.saturating_sub(2 * border_width);
3595
3596
                    if let Some(client) = self.clients.get(window).cloned()
3597
                        && !client.is_floating
3598
                    {
3599
                        let (_, _, hint_width, hint_height, _) = self.apply_size_hints(
3600
                            *window,
3601
                            geometry.x_coordinate,
3602
                            geometry.y_coordinate,
3603
                            adjusted_width as i32,
3604
                            adjusted_height as i32,
3605
                        );
3606
                        adjusted_width = hint_width as u32;
3607
                        adjusted_height = hint_height as u32;
3608
                    }
3609
3610
                    let adjusted_x = geometry.x_coordinate + monitor_x;
3611
                    let adjusted_y = geometry.y_coordinate + monitor_y + bar_height as i32;
3612
3613
                    if let Some(client) = self.clients.get_mut(window) {
3614
                        client.x_position = adjusted_x as i16;
3615
                        client.y_position = adjusted_y as i16;
3616
                        client.width = adjusted_width as u16;
3617
                        client.height = adjusted_height as u16;
3618
                    }
3619
3620
                    self.connection.configure_window(
3621
                        *window,
3622
                        &ConfigureWindowAux::new()
3623
                            .x(adjusted_x)
3624
                            .y(adjusted_y)
3625
                            .width(adjusted_width)
3626
                            .height(adjusted_height)
3627
                            .border_width(border_width),
3628
                    )?;
3629
3630
                    if let Some(c) = self.clients.get_mut(window) {
3631
                        c.x_position = adjusted_x as i16;
3632
                        c.y_position = adjusted_y as i16;
3633
                        c.width = adjusted_width as u16;
3634
                        c.height = adjusted_height as u16;
3635
                        c.border_width = border_width as u16;
3636
                    }
3637
                }
3638
            }
3639
        }
3640
3641
        for monitor_index in 0..self.monitors.len() {
3642
            let stack_head = self.monitors[monitor_index].stack_head;
3643
            self.showhide(stack_head)?;
3644
        }
3645
3646
        for monitor_index in 0..self.monitors.len() {
3647
            let monitor = &self.monitors[monitor_index];
3648
            let tags = monitor.tagset[monitor.selected_tags_index];
3649
3650
            let has_visible_fullscreen = self.fullscreen_windows.iter().any(|&w| {
3651
                self.clients.get(&w).map_or(false, |c| {
3652
                    c.monitor_index == monitor_index && (c.tags & tags) != 0
3653
                })
3654
            });
3655
3656
            if has_visible_fullscreen {
3657
                if let Some(bar) = self.bars.get(monitor_index) {
3658
                    self.connection.unmap_window(bar.window())?;
3659
                }
3660
3661
                for &window in &self.fullscreen_windows {
3662
                    if let Some(client) = self.clients.get(&window) {
3663
                        if client.monitor_index == monitor_index && (client.tags & tags) != 0 {
3664
                            self.connection.configure_window(
3665
                                window,
3666
                                &ConfigureWindowAux::new()
3667
                                    .border_width(0)
3668
                                    .x(monitor.screen_x)
3669
                                    .y(monitor.screen_y)
3670
                                    .width(monitor.screen_width as u32)
3671
                                    .height(monitor.screen_height as u32)
3672
                                    .stack_mode(StackMode::ABOVE),
3673
                            )?;
3674
                        }
3675
                    }
3676
                }
3677
            } else if self.show_bar {
3678
                if let Some(bar) = self.bars.get(monitor_index) {
3679
                    self.connection.map_window(bar.window())?;
3680
                }
3681
            }
3682
        }
3683
3684
        self.connection.flush()?;
3685
3686
        let is_tabbed = self.layout.name() == LayoutType::Tabbed.as_str();
3687
3688
        if is_tabbed {
3689
            let outer_horizontal = if self.gaps_enabled {
3690
                self.config.gap_outer_horizontal
3691
            } else {
3692
                0
3693
            };
3694
            let outer_vertical = if self.gaps_enabled {
3695
                self.config.gap_outer_vertical
3696
            } else {
3697
                0
3698
            };
3699
3700
            for monitor_index in 0..self.tab_bars.len() {
3701
                if let Some(monitor) = self.monitors.get(monitor_index) {
3702
                    let bar_height = if self.show_bar {
3703
                        self.bars
3704
                            .get(monitor_index)
3705
                            .map(|bar| bar.height() as f32)
3706
                            .unwrap_or(0.0)
3707
                    } else {
3708
                        0.0
3709
                    };
3710
3711
                    let tab_bar_x = (monitor.screen_x + outer_horizontal as i32) as i16;
3712
                    let tab_bar_y =
3713
                        (monitor.screen_y as f32 + bar_height + outer_vertical as f32) as i16;
3714
                    let tab_bar_width = monitor
3715
                        .screen_width
3716
                        .saturating_sub(2 * outer_horizontal as i32)
3717
                        as u16;
3718
3719
                    if let Err(e) = self.tab_bars[monitor_index].reposition(
3720
                        &self.connection,
3721
                        tab_bar_x,
3722
                        tab_bar_y,
3723
                        tab_bar_width,
3724
                    ) {
3725
                        eprintln!("Failed to reposition tab bar: {:?}", e);
3726
                    }
3727
                }
3728
            }
3729
        }
3730
3731
        for monitor_index in 0..self.tab_bars.len() {
3732
            let has_visible_windows = self.windows.iter().any(|&window| {
3733
                if let Some(client) = self.clients.get(&window) {
3734
                    if client.monitor_index != monitor_index
3735
                        || self.floating_windows.contains(&window)
3736
                        || self.fullscreen_windows.contains(&window)
3737
                    {
3738
                        return false;
3739
                    }
3740
                    if let Some(monitor) = self.monitors.get(monitor_index) {
3741
                        return (client.tags & monitor.tagset[monitor.selected_tags_index]) != 0;
3742
                    }
3743
                }
3744
                false
3745
            });
3746
3747
            if is_tabbed && has_visible_windows {
3748
                if let Err(e) = self.tab_bars[monitor_index].show(&self.connection) {
3749
                    eprintln!("Failed to show tab bar: {:?}", e);
3750
                }
3751
            } else if let Err(e) = self.tab_bars[monitor_index].hide(&self.connection) {
3752
                eprintln!("Failed to hide tab bar: {:?}", e);
3753
            }
3754
        }
3755
3756
        if is_tabbed {
3757
            self.update_tab_bars()?;
3758
        }
3759
3760
        Ok(())
3761
    }
3762
3763
    pub fn change_layout<L: Layout + 'static>(&mut self, new_layout: L) -> WmResult<()> {
3764
        self.layout = Box::new(new_layout);
3765
        self.apply_layout()?;
3766
        Ok(())
3767
    }
3768
3769
    fn send_configure_notify(&self, window: Window) -> WmResult<()> {
3770
        let client = self.clients.get(&window);
3771
        let (x, y, w, h, bw) = if let Some(c) = client {
3772
            (
3773
                c.x_position,
3774
                c.y_position,
3775
                c.width,
3776
                c.height,
3777
                c.border_width,
3778
            )
3779
        } else {
3780
            let geom = self.connection.get_geometry(window)?.reply()?;
3781
            (geom.x, geom.y, geom.width, geom.height, geom.border_width)
3782
        };
3783
3784
        let event = ConfigureNotifyEvent {
3785
            response_type: CONFIGURE_NOTIFY_EVENT,
3786
            sequence: 0,
3787
            event: window,
3788
            window,
3789
            above_sibling: x11rb::NONE,
3790
            x,
3791
            y,
3792
            width: w,
3793
            height: h,
3794
            border_width: bw,
3795
            override_redirect: false,
3796
        };
3797
3798
        self.connection
3799
            .send_event(false, window, EventMask::STRUCTURE_NOTIFY, event)?;
3800
3801
        Ok(())
3802
    }
3803
3804
    fn update_size_hints(&mut self, window: Window) -> WmResult<()> {
3805
        let size_hints = self
3806
            .connection
3807
            .get_property(
3808
                false,
3809
                window,
3810
                x11rb::protocol::xproto::AtomEnum::WM_NORMAL_HINTS,
3811
                x11rb::protocol::xproto::AtomEnum::WM_SIZE_HINTS,
3812
                0,
3813
                18,
3814
            )?
3815
            .reply()?;
3816
3817
        if size_hints.value.is_empty() {
3818
            if let Some(client) = self.clients.get_mut(&window) {
3819
                client.hints_valid = false;
3820
            }
3821
            return Ok(());
3822
        }
3823
3824
        if size_hints.value.len() < 18 * 4 {
3825
            if let Some(client) = self.clients.get_mut(&window) {
3826
                client.hints_valid = false;
3827
            }
3828
            return Ok(());
3829
        }
3830
3831
        use crate::size_hints::{flags::*, offset::*};
3832
3833
        let read_u32 = |offset: usize| -> u32 {
3834
            let bytes = &size_hints.value[offset * 4..(offset + 1) * 4];
3835
            u32::from_ne_bytes([bytes[0], bytes[1], bytes[2], bytes[3]])
3836
        };
3837
3838
        let flags = read_u32(FLAGS);
3839
3840
        if let Some(client) = self.clients.get_mut(&window) {
3841
            if flags & P_BASE_SIZE != 0 {
3842
                client.base_width = read_u32(BASE_WIDTH) as i32;
3843
                client.base_height = read_u32(BASE_HEIGHT) as i32;
3844
            } else if flags & P_MIN_SIZE != 0 {
3845
                client.base_width = read_u32(MIN_WIDTH) as i32;
3846
                client.base_height = read_u32(MIN_HEIGHT) as i32;
3847
            } else {
3848
                client.base_width = 0;
3849
                client.base_height = 0;
3850
            }
3851
3852
            if flags & P_RESIZE_INC != 0 {
3853
                client.increment_width = read_u32(WIDTH_INC) as i32;
3854
                client.increment_height = read_u32(HEIGHT_INC) as i32;
3855
            } else {
3856
                client.increment_width = 0;
3857
                client.increment_height = 0;
3858
            }
3859
3860
            if flags & P_MAX_SIZE != 0 {
3861
                client.max_width = read_u32(MAX_WIDTH) as i32;
3862
                client.max_height = read_u32(MAX_HEIGHT) as i32;
3863
            } else {
3864
                client.max_width = 0;
3865
                client.max_height = 0;
3866
            }
3867
3868
            if flags & P_MIN_SIZE != 0 {
3869
                client.min_width = read_u32(MIN_WIDTH) as i32;
3870
                client.min_height = read_u32(MIN_HEIGHT) as i32;
3871
            } else if flags & P_BASE_SIZE != 0 {
3872
                client.min_width = read_u32(BASE_WIDTH) as i32;
3873
                client.min_height = read_u32(BASE_HEIGHT) as i32;
3874
            } else {
3875
                client.min_width = 0;
3876
                client.min_height = 0;
3877
            }
3878
3879
            if flags & P_ASPECT != 0 {
3880
                client.min_aspect =
3881
                    (read_u32(MIN_ASPECT_Y) as f32) / (read_u32(MIN_ASPECT_X) as f32).max(1.0);
3882
                client.max_aspect =
3883
                    (read_u32(MAX_ASPECT_X) as f32) / (read_u32(MAX_ASPECT_Y) as f32).max(1.0);
3884
            } else {
3885
                client.min_aspect = 0.0;
3886
                client.max_aspect = 0.0;
3887
            }
3888
3889
            client.is_fixed = client.max_width > 0
3890
                && client.max_height > 0
3891
                && client.max_width == client.min_width
3892
                && client.max_height == client.min_height;
3893
3894
            client.hints_valid = true;
3895
        }
3896
        Ok(())
3897
    }
3898
3899
    fn update_window_title(&mut self, window: Window) -> WmResult<()> {
3900
        let net_name = self
3901
            .connection
3902
            .get_property(
3903
                false,
3904
                window,
3905
                self.atoms.net_wm_name,
3906
                self.atoms.utf8_string,
3907
                0,
3908
                256,
3909
            )
3910
            .ok()
3911
            .and_then(|cookie| cookie.reply().ok());
3912
3913
        if let Some(name) = net_name
3914
            && !name.value.is_empty()
3915
            && let Ok(title) = String::from_utf8(name.value.clone())
3916
            && let Some(client) = self.clients.get_mut(&window)
3917
        {
3918
            client.name = title;
3919
            return Ok(());
3920
        }
3921
3922
        let wm_name = self
3923
            .connection
3924
            .get_property(
3925
                false,
3926
                window,
3927
                self.atoms.wm_name,
3928
                x11rb::protocol::xproto::AtomEnum::STRING,
3929
                0,
3930
                256,
3931
            )?
3932
            .reply()?;
3933
3934
        if !wm_name.value.is_empty()
3935
            && let Ok(title) = String::from_utf8(wm_name.value.clone())
3936
            && let Some(client) = self.clients.get_mut(&window)
3937
        {
3938
            client.name = title;
3939
        }
3940
3941
        Ok(())
3942
    }
3943
3944
    fn update_window_hints(&mut self, window: Window) -> WmResult<()> {
3945
        let hints_reply = self
3946
            .connection
3947
            .get_property(false, window, AtomEnum::WM_HINTS, AtomEnum::WM_HINTS, 0, 9)?
3948
            .reply();
3949
3950
        if let Ok(hints) = hints_reply
3951
            && hints.value.len() >= 4
3952
        {
3953
            let flags = u32::from_ne_bytes([
3954
                hints.value[0],
3955
                hints.value[1],
3956
                hints.value[2],
3957
                hints.value[3],
3958
            ]);
3959
3960
            let selected_window = self
3961
                .monitors
3962
                .get(self.selected_monitor)
3963
                .and_then(|m| m.selected_client);
3964
3965
            if Some(window) == selected_window && (flags & 256) != 0 {
3966
                let new_flags = flags & !256;
3967
                let mut new_hints = hints.value.clone();
3968
                new_hints[0..4].copy_from_slice(&new_flags.to_ne_bytes());
3969
3970
                self.connection.change_property(
3971
                    x11rb::protocol::xproto::PropMode::REPLACE,
3972
                    window,
3973
                    AtomEnum::WM_HINTS,
3974
                    AtomEnum::WM_HINTS,
3975
                    32,
3976
                    9,
3977
                    &new_hints,
3978
                )?;
3979
            } else if let Some(client) = self.clients.get_mut(&window) {
3980
                client.is_urgent = (flags & 256) != 0;
3981
            }
3982
3983
            if hints.value.len() >= 8 && (flags & 1) != 0 {
3984
                let input = i32::from_ne_bytes([
3985
                    hints.value[4],
3986
                    hints.value[5],
3987
                    hints.value[6],
3988
                    hints.value[7],
3989
                ]);
3990
3991
                if let Some(client) = self.clients.get_mut(&window) {
3992
                    client.never_focus = input == 0;
3993
                }
3994
            } else if let Some(client) = self.clients.get_mut(&window) {
3995
                client.never_focus = false;
3996
            }
3997
        }
3998
3999
        Ok(())
4000
    }
4001
4002
    fn update_window_type(&mut self, window: Window) -> WmResult<()> {
4003
        if let Ok(state_atoms) = self.get_window_atom_list_property(window, self.atoms.net_wm_state)
4004
        {
4005
            if state_atoms.contains(&self.atoms.net_wm_state_fullscreen) {
4006
                self.set_window_fullscreen(window, true)?;
4007
            }
4008
        }
4009
4010
        if let Ok(Some(type_atom)) =
4011
            self.get_window_atom_property(window, self.atoms.net_wm_window_type)
4012
            && type_atom == self.atoms.net_wm_window_type_dialog
4013
        {
4014
            if let Some(client) = self.clients.get_mut(&window) {
4015
                client.is_floating = true;
4016
            }
4017
            self.floating_windows.insert(window);
4018
        }
4019
4020
        Ok(())
4021
    }
4022
4023
    fn apply_size_hints(
4024
        &mut self,
4025
        window: Window,
4026
        mut x: i32,
4027
        mut y: i32,
4028
        mut w: i32,
4029
        mut h: i32,
4030
    ) -> (i32, i32, i32, i32, bool) {
4031
        let bh = 20;
4032
4033
        let (
4034
            client_x,
4035
            client_y,
4036
            client_w,
4037
            client_h,
4038
            bw,
4039
            monitor_index,
4040
            is_floating,
4041
            mut hints_valid,
4042
        ) = {
4043
            let client = match self.clients.get(&window) {
4044
                Some(c) => c,
4045
                None => return (x, y, w, h, false),
4046
            };
4047
            (
4048
                client.x_position as i32,
4049
                client.y_position as i32,
4050
                client.width as i32,
4051
                client.height as i32,
4052
                client.border_width as i32,
4053
                client.monitor_index,
4054
                client.is_floating,
4055
                client.hints_valid,
4056
            )
4057
        };
4058
4059
        let monitor = &self.monitors[monitor_index];
4060
        let client_width = client_w + 2 * bw;
4061
        let client_height = client_h + 2 * bw;
4062
4063
        w = w.max(1);
4064
        h = h.max(1);
4065
4066
        if x >= monitor.window_area_x + monitor.window_area_width {
4067
            x = monitor.window_area_x + monitor.window_area_width - client_width;
4068
        }
4069
        if y >= monitor.window_area_y + monitor.window_area_height {
4070
            y = monitor.window_area_y + monitor.window_area_height - client_height;
4071
        }
4072
        if x + w + 2 * bw <= monitor.window_area_x {
4073
            x = monitor.window_area_x;
4074
        }
4075
        if y + h + 2 * bw <= monitor.window_area_y {
4076
            y = monitor.window_area_y;
4077
        }
4078
4079
        if h < bh {
4080
            h = bh;
4081
        }
4082
        if w < bh {
4083
            w = bh;
4084
        }
4085
4086
        if is_floating || self.layout.name() == "normie" {
4087
            if !hints_valid {
4088
                let _ = self.update_size_hints(window);
4089
                hints_valid = self
4090
                    .clients
4091
                    .get(&window)
4092
                    .map(|c| c.hints_valid)
4093
                    .unwrap_or(false);
4094
            }
4095
4096
            if hints_valid {
4097
                let (
4098
                    base_width,
4099
                    base_height,
4100
                    min_width,
4101
                    min_height,
4102
                    max_width,
4103
                    max_height,
4104
                    inc_width,
4105
                    inc_height,
4106
                    min_aspect,
4107
                    max_aspect,
4108
                ) = {
4109
                    let client = self.clients.get(&window).unwrap();
4110
                    (
4111
                        client.base_width,
4112
                        client.base_height,
4113
                        client.min_width,
4114
                        client.min_height,
4115
                        client.max_width,
4116
                        client.max_height,
4117
                        client.increment_width,
4118
                        client.increment_height,
4119
                        client.min_aspect,
4120
                        client.max_aspect,
4121
                    )
4122
                };
4123
4124
                let base_is_min = base_width == min_width && base_height == min_height;
4125
4126
                if !base_is_min {
4127
                    w -= base_width;
4128
                    h -= base_height;
4129
                }
4130
4131
                if min_aspect > 0.0 && max_aspect > 0.0 {
4132
                    if max_aspect < (w as f32 / h as f32) {
4133
                        w = (h as f32 * max_aspect + 0.5) as i32;
4134
                    } else if min_aspect < (h as f32 / w as f32) {
4135
                        h = (w as f32 * min_aspect + 0.5) as i32;
4136
                    }
4137
                }
4138
4139
                if base_is_min {
4140
                    w -= base_width;
4141
                    h -= base_height;
4142
                }
4143
4144
                if inc_width > 0 {
4145
                    w -= w % inc_width;
4146
                }
4147
                if inc_height > 0 {
4148
                    h -= h % inc_height;
4149
                }
4150
4151
                w = (w + base_width).max(min_width);
4152
                h = (h + base_height).max(min_height);
4153
4154
                if max_width > 0 {
4155
                    w = w.min(max_width);
4156
                }
4157
                if max_height > 0 {
4158
                    h = h.min(max_height);
4159
                }
4160
            }
4161
        }
4162
4163
        let changed = x != client_x || y != client_y || w != client_w || h != client_h;
4164
        (x, y, w, h, changed)
4165
    }
4166
4167
    fn next_tiled(&self, start: Option<Window>, monitor: &Monitor) -> Option<Window> {
4168
        let mut current = start;
4169
        while let Some(window) = current {
4170
            if let Some(client) = self.clients.get(&window) {
4171
                let visible_tags = client.tags & monitor.tagset[monitor.selected_tags_index];
4172
                if visible_tags != 0 && !client.is_floating {
4173
                    return Some(window);
4174
                }
4175
                current = client.next;
4176
            } else {
4177
                break;
4178
            }
4179
        }
4180
        None
4181
    }
4182
4183
    fn next_tagged(&self, start: Option<Window>, tags: u32) -> Option<Window> {
4184
        let mut current = start;
4185
        while let Some(window) = current {
4186
            if let Some(client) = self.clients.get(&window) {
4187
                let visible_on_tags = (client.tags & tags) != 0;
4188
                if !client.is_floating && visible_on_tags {
4189
                    return Some(window);
4190
                }
4191
                current = client.next;
4192
            } else {
4193
                break;
4194
            }
4195
        }
4196
        None
4197
    }
4198
4199
    fn attach(&mut self, window: Window, monitor_index: usize) {
4200
        if let Some(monitor) = self.monitors.get_mut(monitor_index)
4201
            && let Some(client) = self.clients.get_mut(&window)
4202
        {
4203
            client.next = monitor.clients_head;
4204
            monitor.clients_head = Some(window);
4205
        }
4206
    }
4207
4208
    fn attach_aside(&mut self, window: Window, monitor_index: usize) {
4209
        let monitor = match self.monitors.get(monitor_index) {
4210
            Some(m) => m,
4211
            None => return,
4212
        };
4213
4214
        let new_window_tags = self.clients.get(&window).map(|c| c.tags).unwrap_or(0);
4215
        let first_tagged = self.next_tagged(monitor.clients_head, new_window_tags);
4216
4217
        if first_tagged.is_none() {
4218
            self.attach(window, monitor_index);
4219
            return;
4220
        }
4221
4222
        if let Some(insert_after_window) = first_tagged
4223
            && let Some(after_client) = self.clients.get(&insert_after_window)
4224
        {
4225
            let old_next = after_client.next;
4226
            if let Some(new_client) = self.clients.get_mut(&window) {
4227
                new_client.next = old_next;
4228
            }
4229
            if let Some(after_client_mut) = self.clients.get_mut(&insert_after_window) {
4230
                after_client_mut.next = Some(window);
4231
            }
4232
        }
4233
    }
4234
4235
    fn detach(&mut self, window: Window) {
4236
        let monitor_index = self.clients.get(&window).map(|c| c.monitor_index);
4237
        if let Some(monitor_index) = monitor_index
4238
            && let Some(monitor) = self.monitors.get_mut(monitor_index)
4239
        {
4240
            if monitor.clients_head == Some(window) {
4241
                if let Some(client) = self.clients.get(&window) {
4242
                    monitor.clients_head = client.next;
4243
                }
4244
            } else {
4245
                let mut current = monitor.clients_head;
4246
                while let Some(current_window) = current {
4247
                    if let Some(current_client) = self.clients.get(&current_window) {
4248
                        if current_client.next == Some(window) {
4249
                            let new_next = self.clients.get(&window).and_then(|c| c.next);
4250
                            if let Some(current_client_mut) = self.clients.get_mut(&current_window)
4251
                            {
4252
                                current_client_mut.next = new_next;
4253
                            }
4254
                            break;
4255
                        }
4256
                        current = current_client.next;
4257
                    } else {
4258
                        break;
4259
                    }
4260
                }
4261
            }
4262
        }
4263
    }
4264
4265
    fn attach_stack(&mut self, window: Window, monitor_index: usize) {
4266
        if let Some(monitor) = self.monitors.get_mut(monitor_index)
4267
            && let Some(client) = self.clients.get_mut(&window)
4268
        {
4269
            client.stack_next = monitor.stack_head;
4270
            monitor.stack_head = Some(window);
4271
        }
4272
    }
4273
4274
    fn detach_stack(&mut self, window: Window) {
4275
        let monitor_index = self.clients.get(&window).map(|c| c.monitor_index);
4276
        if let Some(monitor_index) = monitor_index
4277
            && let Some(monitor) = self.monitors.get_mut(monitor_index)
4278
        {
4279
            if monitor.stack_head == Some(window) {
4280
                if let Some(client) = self.clients.get(&window) {
4281
                    monitor.stack_head = client.stack_next;
4282
                }
4283
                let should_update_selected = monitor.selected_client == Some(window);
4284
                let mut new_selected: Option<Window> = None;
4285
                if should_update_selected {
4286
                    let mut stack_current = monitor.stack_head;
4287
                    while let Some(stack_window) = stack_current {
4288
                        if let Some(stack_client) = self.clients.get(&stack_window) {
4289
                            if self.is_window_visible(stack_window) {
4290
                                new_selected = Some(stack_window);
4291
                                break;
4292
                            }
4293
                            stack_current = stack_client.stack_next;
4294
                        } else {
4295
                            break;
4296
                        }
4297
                    }
4298
                }
4299
                if should_update_selected
4300
                    && let Some(monitor) = self.monitors.get_mut(monitor_index)
4301
                {
4302
                    monitor.selected_client = new_selected;
4303
                }
4304
            } else {
4305
                let mut current = monitor.stack_head;
4306
                while let Some(current_window) = current {
4307
                    if let Some(current_client) = self.clients.get(&current_window) {
4308
                        if current_client.stack_next == Some(window) {
4309
                            let new_stack_next =
4310
                                self.clients.get(&window).and_then(|c| c.stack_next);
4311
                            if let Some(current_client_mut) = self.clients.get_mut(&current_window)
4312
                            {
4313
                                current_client_mut.stack_next = new_stack_next;
4314
                            }
4315
                            break;
4316
                        }
4317
                        current = current_client.stack_next;
4318
                    } else {
4319
                        break;
4320
                    }
4321
                }
4322
            }
4323
        }
4324
    }
4325
4326
    fn remove_window(&mut self, window: Window, destroyed: bool) -> WmResult<()> {
4327
        let initial_count = self.windows.len();
4328
4329
        let focused = self
4330
            .monitors
4331
            .get(self.selected_monitor)
4332
            .and_then(|m| m.selected_client);
4333
4334
        if !destroyed {
4335
            if let Some(client) = self.clients.get(&window) {
4336
                let old_border_width = client.old_border_width;
4337
                self.connection.configure_window(
4338
                    window,
4339
                    &ConfigureWindowAux::new().border_width(old_border_width as u32),
4340
                )?;
4341
            }
4342
            self.set_wm_state(window, 0)?;
4343
        }
4344
4345
        if self.clients.contains_key(&window) {
4346
            self.detach(window);
4347
            self.detach_stack(window);
4348
            self.clients.remove(&window);
4349
        }
4350
4351
        self.windows.retain(|&w| w != window);
4352
        self.floating_windows.remove(&window);
4353
        self.update_client_list()?;
4354
4355
        if self.windows.len() < initial_count {
4356
            if focused == Some(window) {
4357
                let visible = self.visible_windows_on_monitor(self.selected_monitor);
4358
                if let Some(&new_win) = visible.last() {
4359
                    self.focus(Some(new_win))?;
4360
                } else if let Some(monitor) = self.monitors.get_mut(self.selected_monitor) {
4361
                    monitor.selected_client = None;
4362
                }
4363
            }
4364
4365
            self.apply_layout()?;
4366
            self.update_bar()?;
4367
        }
4368
        Ok(())
4369
    }
4370
4371
    fn get_selected_monitor(&self) -> &Monitor {
4372
        &self.monitors[self.selected_monitor]
4373
    }
4374
4375
    fn has_windows_on_tag(&self, monitor_number: usize, tag_index: usize) -> bool {
4376
        let Some(monitor) = self.monitors.get(monitor_number) else {
4377
            return false;
4378
        };
4379
4380
        let mut current = monitor.clients_head;
4381
        while let Some(window) = current {
4382
            let Some(client) = self.clients.get(&window) else {
4383
                break;
4384
            };
4385
4386
            if unmask_tag(client.tags) == tag_index {
4387
                return true;
4388
            }
4389
            current = client.next;
4390
        }
4391
4392
        false
4393
    }
4394
4395
    fn run_autostart_commands(&self) {
4396
        for command in &self.config.autostart {
4397
            crate::signal::spawn_detached(command);
4398
            eprintln!("[autostart] Spawned: {}", command);
4399
        }
4400
    }
4401
}