oxwm

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