oxwm

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