oxwm

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