oxwm

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