oxwm

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