oxwm

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