oxwm

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