oxwm

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