oxwm

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