oxwm

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