oxwm

https://git.tonybtw.com/oxwm.git git://git.tonybtw.com/oxwm.git
105,830 bytes raw
1
use crate::Config;
2
use crate::bar::Bar;
3
use crate::errors::WmError;
4
use crate::keyboard::{self, Arg, KeyAction, handlers};
5
use crate::layout::GapConfig;
6
use crate::layout::tiling::TilingLayout;
7
use crate::layout::{Layout, LayoutBox, LayoutType, layout_from_str, next_layout};
8
use crate::monitor::{Monitor, detect_monitors};
9
use crate::overlay::{ErrorOverlay, KeybindOverlay, Overlay};
10
use std::collections::HashSet;
11
use std::process::Command;
12
use x11rb::cursor::Handle as CursorHandle;
13
14
use x11rb::connection::Connection;
15
use x11rb::protocol::Event;
16
use x11rb::protocol::xproto::*;
17
use x11rb::rust_connection::RustConnection;
18
19
const DEFAULT_FLOAT_WIDTH_RATIO: f32 = 0.5;
20
const DEFAULT_FLOAT_HEIGHT_RATIO: f32 = 0.5;
21
const SMART_MOVE_STEP_RATIO_VERTICAL: i32 = 4;
22
const SMART_MOVE_STEP_RATIO_HORIZONTAL: i32 = 6;
23
24
#[derive(Debug, Clone, Copy)]
25
pub struct CachedGeometry {
26
    pub x_position: i16,
27
    pub y_position: i16,
28
    pub width: u16,
29
    pub height: u16,
30
    pub border_width: u16,
31
}
32
33
pub type TagMask = u32;
34
pub fn tag_mask(tag: usize) -> TagMask {
35
    1 << tag
36
}
37
38
struct AtomCache {
39
    net_current_desktop: Atom,
40
    net_client_info: Atom,
41
    wm_state: Atom,
42
    wm_protocols: Atom,
43
    wm_delete_window: Atom,
44
    net_wm_state: Atom,
45
    net_wm_state_fullscreen: Atom,
46
    net_wm_window_type: Atom,
47
    net_wm_window_type_dialog: Atom,
48
}
49
50
impl AtomCache {
51
    fn new(connection: &RustConnection) -> WmResult<Self> {
52
        let net_current_desktop = connection
53
            .intern_atom(false, b"_NET_CURRENT_DESKTOP")?
54
            .reply()?
55
            .atom;
56
57
        let net_client_info = connection
58
            .intern_atom(false, b"_NET_CLIENT_INFO")?
59
            .reply()?
60
            .atom;
61
62
        let wm_state = connection.intern_atom(false, b"WM_STATE")?.reply()?.atom;
63
64
        let wm_protocols = connection
65
            .intern_atom(false, b"WM_PROTOCOLS")?
66
            .reply()?
67
            .atom;
68
69
        let wm_delete_window = connection
70
            .intern_atom(false, b"WM_DELETE_WINDOW")?
71
            .reply()?
72
            .atom;
73
74
        let net_wm_state = connection
75
            .intern_atom(false, b"_NET_WM_STATE")?
76
            .reply()?
77
            .atom;
78
79
        let net_wm_state_fullscreen = connection
80
            .intern_atom(false, b"_NET_WM_STATE_FULLSCREEN")?
81
            .reply()?
82
            .atom;
83
84
        let net_wm_window_type = connection
85
            .intern_atom(false, b"_NET_WM_WINDOW_TYPE")?
86
            .reply()?
87
            .atom;
88
89
        let net_wm_window_type_dialog = connection
90
            .intern_atom(false, b"_NET_WM_WINDOW_TYPE_DIALOG")?
91
            .reply()?
92
            .atom;
93
94
        Ok(Self {
95
            net_current_desktop,
96
            net_client_info,
97
            wm_state,
98
            wm_protocols,
99
            wm_delete_window,
100
            net_wm_state,
101
            net_wm_state_fullscreen,
102
            net_wm_window_type,
103
            net_wm_window_type_dialog,
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
    layout: LayoutBox,
116
    window_tags: std::collections::HashMap<Window, TagMask>,
117
    window_monitor: std::collections::HashMap<Window, usize>,
118
    window_geometry_cache: std::collections::HashMap<Window, CachedGeometry>,
119
    gaps_enabled: bool,
120
    floating_windows: HashSet<Window>,
121
    fullscreen_windows: HashSet<Window>,
122
    floating_geometry_before_fullscreen: std::collections::HashMap<Window, (i16, i16, u16, u16, u16)>,
123
    bars: Vec<Bar>,
124
    tab_bars: Vec<crate::tab_bar::TabBar>,
125
    show_bar: bool,
126
    last_layout: Option<&'static str>,
127
    monitors: Vec<Monitor>,
128
    selected_monitor: usize,
129
    atoms: AtomCache,
130
    previous_focused: Option<Window>,
131
    display: *mut x11::xlib::Display,
132
    font: crate::bar::font::Font,
133
    keychord_state: keyboard::handlers::KeychordState,
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.x as i16,
225
                monitor.y as i16,
226
                monitor.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.x + config.gap_outer_horizontal as i32) as i16,
241
                (monitor.y as f32 + bar_height + config.gap_outer_vertical as f32) as i16,
242
                monitor.width.saturating_sub(2 * config.gap_outer_horizontal) 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
            layout: Box::new(TilingLayout),
273
            window_tags: std::collections::HashMap::new(),
274
            window_monitor: std::collections::HashMap::new(),
275
            window_geometry_cache: std::collections::HashMap::new(),
276
            gaps_enabled,
277
            floating_windows: HashSet::new(),
278
            fullscreen_windows: HashSet::new(),
279
            floating_geometry_before_fullscreen: std::collections::HashMap::new(),
280
            bars,
281
            tab_bars,
282
            show_bar: true,
283
            last_layout: None,
284
            monitors,
285
            selected_monitor: 0,
286
            atoms,
287
            previous_focused: None,
288
            display,
289
            font,
290
            keychord_state: keyboard::handlers::KeychordState::Idle,
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 = "Your config.lua uses legacy syntax or has errors.\n\n\
309
                       You are now running with default configuration.\n\n\
310
                       Press Mod+Shift+/ to see default keybinds\n\
311
                       Press Mod+Shift+R to reload after fixing your config";
312
313
        let screen_width = self.screen.width_in_pixels;
314
        let screen_height = self.screen.height_in_pixels;
315
316
        if let Err(e) = self.overlay.show_error(
317
            &self.connection,
318
            &self.font,
319
            message,
320
            screen_width,
321
            screen_height,
322
        ) {
323
            eprintln!("Failed to show migration overlay: {:?}", e);
324
        }
325
    }
326
327
    fn try_reload_config(&mut self) -> Result<(), String> {
328
        let config_dir = if let Some(xdg_config) = std::env::var_os("XDG_CONFIG_HOME") {
329
            std::path::PathBuf::from(xdg_config).join("oxwm")
330
        } else if let Some(home) = std::env::var_os("HOME") {
331
            std::path::PathBuf::from(home).join(".config").join("oxwm")
332
        } else {
333
            return Err("Could not find config directory".to_string());
334
        };
335
336
        let lua_path = config_dir.join("config.lua");
337
338
        if !lua_path.exists() {
339
            return Err("No config.lua file found".to_string());
340
        }
341
342
        let config_str = std::fs::read_to_string(&lua_path)
343
            .map_err(|e| format!("Failed to read config: {}", e))?;
344
345
        let new_config = crate::config::parse_lua_config(&config_str, Some(&config_dir))
346
            .map_err(|e| format!("{}", e))?;
347
348
        self.config = new_config;
349
        self.error_message = None;
350
351
        for bar in &mut self.bars {
352
            bar.update_from_config(&self.config);
353
        }
354
355
        Ok(())
356
    }
357
358
    fn scan_existing_windows(&mut self) -> WmResult<()> {
359
        let tree = self.connection.query_tree(self.root)?.reply()?;
360
        let net_client_info = self.atoms.net_client_info;
361
        let wm_state_atom = self.atoms.wm_state;
362
363
        for &window in &tree.children {
364
            if self.bars.iter().any(|bar| bar.window() == window) {
365
                continue;
366
            }
367
368
            let Ok(attrs) = self.connection.get_window_attributes(window)?.reply() else {
369
                continue;
370
            };
371
372
            if attrs.override_redirect {
373
                continue;
374
            }
375
376
            if attrs.map_state == MapState::VIEWABLE {
377
                let tag = self.get_saved_tag(window, net_client_info)?;
378
                self.windows.push(window);
379
                self.window_tags.insert(window, tag);
380
                self.window_monitor.insert(window, self.selected_monitor);
381
                continue;
382
            }
383
384
            if attrs.map_state == MapState::UNMAPPED {
385
                let has_wm_state = self
386
                    .connection
387
                    .get_property(false, window, wm_state_atom, AtomEnum::ANY, 0, 2)?
388
                    .reply()
389
                    .is_ok_and(|prop| !prop.value.is_empty());
390
391
                if !has_wm_state {
392
                    continue;
393
                }
394
395
                let has_wm_class = self
396
                    .connection
397
                    .get_property(false, window, AtomEnum::WM_CLASS, AtomEnum::STRING, 0, 1024)?
398
                    .reply()
399
                    .is_ok_and(|prop| !prop.value.is_empty());
400
401
                if has_wm_class {
402
                    let tag = self.get_saved_tag(window, net_client_info)?;
403
                    self.connection.map_window(window)?;
404
                    self.windows.push(window);
405
                    self.window_tags.insert(window, tag);
406
                    self.window_monitor.insert(window, self.selected_monitor);
407
                }
408
            }
409
        }
410
411
        if let Some(&first) = self.windows.first() {
412
            self.set_focus(first)?;
413
        }
414
415
        self.apply_layout()?;
416
        Ok(())
417
    }
418
419
    fn get_saved_tag(&self, window: Window, net_client_info: Atom) -> WmResult<TagMask> {
420
        match self
421
            .connection
422
            .get_property(false, window, net_client_info, AtomEnum::CARDINAL, 0, 2)?
423
            .reply()
424
        {
425
            Ok(prop) if prop.value.len() >= 4 => {
426
                let tags = u32::from_ne_bytes([
427
                    prop.value[0],
428
                    prop.value[1],
429
                    prop.value[2],
430
                    prop.value[3],
431
                ]);
432
433
                if tags != 0 && tags < (1 << self.config.tags.len()) {
434
                    return Ok(tags);
435
                }
436
            }
437
            Ok(_) => {}
438
            Err(e) => {
439
                eprintln!("No _NET_CLIENT_INFO property ({})", e);
440
            }
441
        }
442
443
        Ok(self
444
            .monitors
445
            .get(self.selected_monitor)
446
            .map(|m| m.selected_tags)
447
            .unwrap_or(tag_mask(0)))
448
    }
449
450
    fn save_client_tag(&self, window: Window, tag: TagMask) -> WmResult<()> {
451
        let net_client_info = self.atoms.net_client_info;
452
453
        let bytes = tag.to_ne_bytes().to_vec();
454
455
        self.connection.change_property(
456
            PropMode::REPLACE,
457
            window,
458
            net_client_info,
459
            AtomEnum::CARDINAL,
460
            32,
461
            1,
462
            &bytes,
463
        )?;
464
465
        self.connection.flush()?;
466
        Ok(())
467
    }
468
469
    fn set_wm_state(&self, window: Window, state: u32) -> WmResult<()> {
470
        let wm_state_atom = self.atoms.wm_state;
471
472
        let data = [state, 0u32];
473
        let bytes: Vec<u8> = data.iter().flat_map(|&v| v.to_ne_bytes()).collect();
474
475
        self.connection.change_property(
476
            PropMode::REPLACE,
477
            window,
478
            wm_state_atom,
479
            wm_state_atom,
480
            32,
481
            2,
482
            &bytes,
483
        )?;
484
485
        self.connection.flush()?;
486
        Ok(())
487
    }
488
489
    pub fn run(&mut self) -> WmResult<bool> {
490
        println!("oxwm started on display {}", self.screen_number);
491
492
        keyboard::setup_keybinds(&self.connection, self.root, &self.config.keybindings)?;
493
        self.update_bar()?;
494
495
        let mut last_bar_update = std::time::Instant::now();
496
        const BAR_UPDATE_INTERVAL_MS: u64 = 100;
497
498
        loop {
499
            match self.connection.poll_for_event_with_sequence()? {
500
                Some((event, _sequence)) => {
501
                    if let Some(should_restart) = self.handle_event(event)? {
502
                        return Ok(should_restart);
503
                    }
504
                }
505
                None => {
506
                    if last_bar_update.elapsed().as_millis() >= BAR_UPDATE_INTERVAL_MS as u128 {
507
                        if let Some(bar) = self.bars.get_mut(self.selected_monitor) {
508
                            bar.update_blocks();
509
                        }
510
                        if self.bars.iter().any(|bar| bar.needs_redraw()) {
511
                            self.update_bar()?;
512
                        }
513
                        last_bar_update = std::time::Instant::now();
514
                    }
515
516
                    self.connection.flush()?;
517
                    std::thread::sleep(std::time::Duration::from_millis(16));
518
                }
519
            }
520
        }
521
    }
522
523
    fn toggle_floating(&mut self) -> WmResult<()> {
524
        if let Some(focused) = self
525
            .monitors
526
            .get(self.selected_monitor)
527
            .and_then(|m| m.focused_window)
528
        {
529
            if self.floating_windows.contains(&focused) {
530
                self.floating_windows.remove(&focused);
531
532
                let selected_tags = self
533
                    .monitors
534
                    .get(self.selected_monitor)
535
                    .map(|m| m.selected_tags)
536
                    .unwrap_or(tag_mask(0));
537
538
                self.window_tags.insert(focused, selected_tags);
539
                self.window_monitor.insert(focused, self.selected_monitor);
540
                if let Err(error) = self.save_client_tag(focused, selected_tags) {
541
                    eprintln!("Failed to save client tag: {:?}", error);
542
                }
543
544
                self.apply_layout()?;
545
            } else {
546
                let float_width = (self.screen.width_in_pixels / 2) as u32;
547
                let float_height = (self.screen.height_in_pixels / 2) as u32;
548
549
                let border_width = self.config.border_width;
550
551
                let center_width = ((self.screen.width_in_pixels - float_width as u16) / 2) as i32;
552
                let center_height =
553
                    ((self.screen.height_in_pixels - float_height as u16) / 2) as i32;
554
555
                self.connection.configure_window(
556
                    focused,
557
                    &ConfigureWindowAux::new()
558
                        .x(center_width)
559
                        .y(center_height)
560
                        .width(float_width)
561
                        .height(float_height)
562
                        .border_width(border_width)
563
                        .stack_mode(StackMode::ABOVE),
564
                )?;
565
566
                self.floating_windows.insert(focused);
567
                self.apply_layout()?;
568
                self.connection.flush()?;
569
            }
570
        }
571
        Ok(())
572
    }
573
574
    fn collect_visible_floating_geometries(&mut self) -> Vec<(Window, CachedGeometry)> {
575
        let focused_window = self.monitors.get(self.selected_monitor)
576
            .and_then(|monitor| monitor.focused_window)
577
            .unwrap_or(0);
578
        let selected_monitor_index = self.selected_monitor;
579
580
        let candidate_windows: Vec<Window> = self.windows.iter()
581
            .filter(|&&window| {
582
                window != focused_window
583
                    && self.floating_windows.contains(&window)
584
                    && self.is_window_visible(window)
585
                    && self.window_monitor.get(&window).copied().unwrap_or(0) == selected_monitor_index
586
            })
587
            .copied()
588
            .collect();
589
590
        candidate_windows.into_iter()
591
            .filter_map(|window| {
592
                self.get_or_query_geometry(window).ok().map(|geometry| (window, geometry))
593
            })
594
            .collect()
595
    }
596
597
    fn calculate_smart_move_position(
598
        &self,
599
        current_x: i32,
600
        current_y: i32,
601
        current_width: i32,
602
        current_height: i32,
603
        direction: i32,
604
        monitor: &Monitor,
605
        other_geometries: &[(Window, CachedGeometry)],
606
    ) -> (i32, i32) {
607
        let (gap_inner_horizontal, gap_inner_vertical, gap_outer_horizontal, gap_outer_vertical) = if self.gaps_enabled {
608
            (
609
                self.config.gap_inner_horizontal as i32,
610
                self.config.gap_inner_vertical as i32,
611
                self.config.gap_outer_horizontal as i32,
612
                self.config.gap_outer_vertical as i32,
613
            )
614
        } else {
615
            (0, 0, 0, 0)
616
        };
617
618
        match direction {
619
            0 => {
620
                let mut target_position = i32::MIN;
621
                let current_top = current_y;
622
                let mut new_y_position = current_y - (monitor.height as i32 / SMART_MOVE_STEP_RATIO_VERTICAL);
623
624
                for (_other_window, other_geometry) in other_geometries {
625
                    let other_x = other_geometry.x_position as i32;
626
                    let other_y = other_geometry.y_position as i32;
627
                    let other_width = other_geometry.width as i32;
628
                    let other_height = other_geometry.height as i32;
629
630
                    if current_x + current_width < other_x || current_x > other_x + other_width {
631
                        continue;
632
                    }
633
634
                    let other_bottom = other_y + other_height + gap_inner_vertical;
635
                    if current_top > other_bottom && new_y_position < other_bottom {
636
                        target_position = target_position.max(other_bottom);
637
                    }
638
                }
639
640
                if target_position != i32::MIN {
641
                    new_y_position = target_position;
642
                }
643
                new_y_position = new_y_position.max(monitor.y as i32 + gap_outer_vertical);
644
                (current_x, new_y_position)
645
            }
646
            1 => {
647
                let mut target_position = i32::MAX;
648
                let current_bottom = current_y + current_height;
649
                let mut new_y_position = current_y + (monitor.height as i32 / SMART_MOVE_STEP_RATIO_VERTICAL);
650
651
                for (_other_window, other_geometry) in other_geometries {
652
                    let other_x = other_geometry.x_position as i32;
653
                    let other_y = other_geometry.y_position as i32;
654
                    let other_width = other_geometry.width as i32;
655
656
                    if current_x + current_width < other_x || current_x > other_x + other_width {
657
                        continue;
658
                    }
659
660
                    let other_top = other_y - gap_inner_vertical;
661
                    if current_bottom < other_top && (new_y_position + current_height) > other_top {
662
                        target_position = target_position.min(other_top - current_height);
663
                    }
664
                }
665
666
                if target_position != i32::MAX {
667
                    new_y_position = target_position;
668
                }
669
                new_y_position = new_y_position.min(monitor.y as i32 + monitor.height as i32 - current_height - gap_outer_vertical);
670
                (current_x, new_y_position)
671
            }
672
            2 => {
673
                let mut target_position = i32::MIN;
674
                let current_left = current_x;
675
                let mut new_x_position = current_x - (monitor.width as i32 / SMART_MOVE_STEP_RATIO_HORIZONTAL);
676
677
                for (_other_window, other_geometry) in other_geometries {
678
                    let other_x = other_geometry.x_position as i32;
679
                    let other_y = other_geometry.y_position as i32;
680
                    let other_width = other_geometry.width as i32;
681
                    let other_height = other_geometry.height as i32;
682
683
                    if current_y + current_height < other_y || current_y > other_y + other_height {
684
                        continue;
685
                    }
686
687
                    let other_right = other_x + other_width + gap_inner_horizontal;
688
                    if current_left > other_right && new_x_position < other_right {
689
                        target_position = target_position.max(other_right);
690
                    }
691
                }
692
693
                if target_position != i32::MIN {
694
                    new_x_position = target_position;
695
                }
696
                new_x_position = new_x_position.max(monitor.x as i32 + gap_outer_horizontal);
697
                (new_x_position, current_y)
698
            }
699
            3 => {
700
                let mut target_position = i32::MAX;
701
                let current_right = current_x + current_width;
702
                let mut new_x_position = current_x + (monitor.width as i32 / SMART_MOVE_STEP_RATIO_HORIZONTAL);
703
704
                for (_other_window, other_geometry) in other_geometries {
705
                    let other_x = other_geometry.x_position as i32;
706
                    let other_y = other_geometry.y_position as i32;
707
                    let other_height = other_geometry.height as i32;
708
709
                    if current_y + current_height < other_y || current_y > other_y + other_height {
710
                        continue;
711
                    }
712
713
                    let other_left = other_x - gap_inner_horizontal;
714
                    if current_right < other_left && (new_x_position + current_width) > other_left {
715
                        target_position = target_position.min(other_left - current_width);
716
                    }
717
                }
718
719
                if target_position != i32::MAX {
720
                    new_x_position = target_position;
721
                }
722
                new_x_position = new_x_position.min(monitor.x as i32 + monitor.width as i32 - current_width - gap_outer_horizontal);
723
                (new_x_position, current_y)
724
            }
725
            _ => (current_x, current_y),
726
        }
727
    }
728
729
    fn smart_move_window(&mut self, direction: i32) -> WmResult<()> {
730
        let focused = match self
731
            .monitors
732
            .get(self.selected_monitor)
733
            .and_then(|monitor| monitor.focused_window)
734
        {
735
            Some(window) => window,
736
            None => return Ok(()),
737
        };
738
739
        if !self.floating_windows.contains(&focused) {
740
            let float_width = (self.screen.width_in_pixels as f32 * DEFAULT_FLOAT_WIDTH_RATIO) as u32;
741
            let float_height = (self.screen.height_in_pixels as f32 * DEFAULT_FLOAT_HEIGHT_RATIO) as u32;
742
            let border_width = self.config.border_width;
743
            let center_x_position = ((self.screen.width_in_pixels - float_width as u16) / 2) as i32;
744
            let center_y_position = ((self.screen.height_in_pixels - float_height as u16) / 2) as i32;
745
746
            self.connection.configure_window(
747
                focused,
748
                &ConfigureWindowAux::new()
749
                    .x(center_x_position)
750
                    .y(center_y_position)
751
                    .width(float_width)
752
                    .height(float_height)
753
                    .border_width(border_width)
754
                    .stack_mode(StackMode::ABOVE),
755
            )?;
756
            self.floating_windows.insert(focused);
757
        }
758
759
        let current_geometry = self.get_or_query_geometry(focused)?;
760
        let current_x = current_geometry.x_position as i32;
761
        let current_y = current_geometry.y_position as i32;
762
        let current_width = current_geometry.width as i32;
763
        let current_height = current_geometry.height as i32;
764
765
        let other_geometries = self.collect_visible_floating_geometries();
766
767
        let monitor = match self.monitors.get(self.selected_monitor) {
768
            Some(monitor) => monitor,
769
            None => return Ok(()),
770
        };
771
772
        let (new_x_position, new_y_position) = self.calculate_smart_move_position(
773
            current_x,
774
            current_y,
775
            current_width,
776
            current_height,
777
            direction,
778
            monitor,
779
            &other_geometries,
780
        );
781
782
        self.connection.configure_window(
783
            focused,
784
            &ConfigureWindowAux::new()
785
                .x(new_x_position)
786
                .y(new_y_position)
787
                .stack_mode(StackMode::ABOVE),
788
        )?;
789
790
        self.update_geometry_cache(focused, CachedGeometry {
791
            x_position: new_x_position as i16,
792
            y_position: new_y_position as i16,
793
            width: current_width as u16,
794
            height: current_height as u16,
795
            border_width: current_geometry.border_width,
796
        });
797
798
        self.connection.flush()?;
799
        Ok(())
800
    }
801
802
    fn exchange_client(&mut self, direction: i32) -> WmResult<()> {
803
        let focused = match self
804
            .monitors
805
            .get(self.selected_monitor)
806
            .and_then(|m| m.focused_window)
807
        {
808
            Some(win) => win,
809
            None => return Ok(()),
810
        };
811
812
        if self.floating_windows.contains(&focused) {
813
            return Ok(());
814
        }
815
816
        let visible = self.visible_windows();
817
        if visible.len() < 2 {
818
            return Ok(());
819
        }
820
821
        let current_idx = match visible.iter().position(|&w| w == focused) {
822
            Some(idx) => idx,
823
            None => return Ok(()),
824
        };
825
826
        let target_idx = match direction {
827
            0 | 2 => {
828
                // UP or LEFT - previous in stack
829
                if current_idx == 0 {
830
                    visible.len() - 1
831
                } else {
832
                    current_idx - 1
833
                }
834
            }
835
            1 | 3 => {
836
                // DOWN or RIGHT - next in stack
837
                (current_idx + 1) % visible.len()
838
            }
839
            _ => return Ok(()),
840
        };
841
842
        let target = visible[target_idx];
843
844
        let focused_pos = self.windows.iter().position(|&w| w == focused);
845
        let target_pos = self.windows.iter().position(|&w| w == target);
846
847
        if let (Some(f_pos), Some(t_pos)) = (focused_pos, target_pos) {
848
            self.windows.swap(f_pos, t_pos);
849
850
            self.apply_layout()?;
851
852
            self.set_focus(focused)?;
853
854
            if let Ok(geometry) = self.connection.get_geometry(focused)?.reply() {
855
                self.connection.warp_pointer(
856
                    x11rb::NONE,
857
                    focused,
858
                    0,
859
                    0,
860
                    0,
861
                    0,
862
                    geometry.width as i16 / 2,
863
                    geometry.height as i16 / 2,
864
                )?;
865
            }
866
        }
867
868
        Ok(())
869
    }
870
871
872
    fn get_layout_symbol(&self) -> String {
873
        let layout_name = self.layout.name();
874
        self.config
875
            .layout_symbols
876
            .iter()
877
            .find(|l| l.name == layout_name)
878
            .map(|l| l.symbol.clone())
879
            .unwrap_or_else(|| self.layout.symbol().to_string())
880
    }
881
882
    fn get_keychord_indicator(&self) -> Option<String> {
883
        match &self.keychord_state {
884
            keyboard::handlers::KeychordState::Idle => None,
885
            keyboard::handlers::KeychordState::InProgress {
886
                candidates,
887
                keys_pressed,
888
            } => {
889
                if candidates.is_empty() {
890
                    return None;
891
                }
892
893
                let binding = &self.config.keybindings[candidates[0]];
894
                let mut indicator = String::new();
895
896
                for (i, key_press) in binding.keys.iter().take(*keys_pressed).enumerate() {
897
                    if i > 0 {
898
                        indicator.push(' ');
899
                    }
900
901
                    for modifier in &key_press.modifiers {
902
                        indicator.push_str(Self::format_modifier(*modifier));
903
                        indicator.push('+');
904
                    }
905
906
                    indicator.push_str(&keyboard::keysyms::format_keysym(key_press.keysym));
907
                }
908
909
                indicator.push('-');
910
                Some(indicator)
911
            }
912
        }
913
    }
914
915
    fn format_modifier(modifier: KeyButMask) -> &'static str {
916
        match modifier {
917
            KeyButMask::MOD1 => "Alt",
918
            KeyButMask::MOD4 => "Super",
919
            KeyButMask::SHIFT => "Shift",
920
            KeyButMask::CONTROL => "Ctrl",
921
            _ => "Mod",
922
        }
923
    }
924
925
926
    fn update_bar(&mut self) -> WmResult<()> {
927
        let layout_symbol = self.get_layout_symbol();
928
        let keychord_indicator = self.get_keychord_indicator();
929
930
        for (monitor_index, monitor) in self.monitors.iter().enumerate() {
931
            if let Some(bar) = self.bars.get_mut(monitor_index) {
932
                let mut occupied_tags: TagMask = 0;
933
                for (&window, &tags) in &self.window_tags {
934
                    if self.window_monitor.get(&window).copied().unwrap_or(0) == monitor_index {
935
                        occupied_tags |= tags;
936
                    }
937
                }
938
939
                let draw_blocks = monitor_index == self.selected_monitor;
940
                bar.invalidate();
941
                bar.draw(
942
                    &self.connection,
943
                    &self.font,
944
                    self.display,
945
                    monitor.selected_tags,
946
                    occupied_tags,
947
                    draw_blocks,
948
                    &layout_symbol,
949
                    keychord_indicator.as_deref(),
950
                )?;
951
            }
952
        }
953
        Ok(())
954
    }
955
956
    fn update_tab_bars(&mut self) -> WmResult<()> {
957
        for (monitor_index, monitor) in self.monitors.iter().enumerate() {
958
            if let Some(tab_bar) = self.tab_bars.get_mut(monitor_index) {
959
                let visible_windows: Vec<Window> = self
960
                    .windows
961
                    .iter()
962
                    .filter(|&&window| {
963
                        let window_monitor_index = self.window_monitor.get(&window).copied().unwrap_or(0);
964
                        if window_monitor_index != monitor_index
965
                            || self.floating_windows.contains(&window)
966
                            || self.fullscreen_windows.contains(&window)
967
                        {
968
                            return false;
969
                        }
970
                        if let Some(&tags) = self.window_tags.get(&window) {
971
                            (tags & monitor.selected_tags) != 0
972
                        } else {
973
                            false
974
                        }
975
                    })
976
                    .copied()
977
                    .collect();
978
979
                let focused_window = monitor.focused_window;
980
981
                tab_bar.draw(
982
                    &self.connection,
983
                    &self.font,
984
                    &visible_windows,
985
                    focused_window,
986
                )?;
987
            }
988
        }
989
        Ok(())
990
    }
991
992
    fn handle_key_action(&mut self, action: KeyAction, arg: &Arg) -> WmResult<()> {
993
        match action {
994
            KeyAction::Spawn => handlers::handle_spawn_action(action, arg, self.selected_monitor)?,
995
            KeyAction::SpawnTerminal => {
996
                use std::process::Command;
997
                let terminal = &self.config.terminal;
998
                if let Err(error) = Command::new(terminal).spawn() {
999
                    eprintln!("Failed to spawn terminal {}: {:?}", terminal, error);
1000
                }
1001
            }
1002
            KeyAction::KillClient => {
1003
                if let Some(focused) = self
1004
                    .monitors
1005
                    .get(self.selected_monitor)
1006
                    .and_then(|m| m.focused_window)
1007
                {
1008
                    self.kill_client(focused)?;
1009
                }
1010
            }
1011
            KeyAction::ToggleFullScreen => {
1012
                self.fullscreen()?;
1013
            }
1014
            KeyAction::ChangeLayout => {
1015
                if let Arg::Str(layout_name) = arg {
1016
                    match layout_from_str(layout_name) {
1017
                        Ok(layout) => {
1018
                            self.layout = layout;
1019
                            if layout_name != "normie" && layout_name != "floating" {
1020
                                self.floating_windows.clear();
1021
                            }
1022
                            self.apply_layout()?;
1023
                            self.update_bar()?;
1024
                        }
1025
                        Err(e) => eprintln!("Failed to change layout: {}", e),
1026
                    }
1027
                }
1028
            }
1029
            KeyAction::CycleLayout => {
1030
                let current_name = self.layout.name();
1031
                let next_name = next_layout(current_name);
1032
                match layout_from_str(next_name) {
1033
                    Ok(layout) => {
1034
                        self.layout = layout;
1035
                        if next_name != "normie" && next_name != "floating" {
1036
                            self.floating_windows.clear();
1037
                        }
1038
                        self.apply_layout()?;
1039
                        self.update_bar()?;
1040
                    }
1041
                    Err(e) => eprintln!("Failed to cycle layout: {}", e),
1042
                }
1043
            }
1044
            KeyAction::ToggleFloating => {
1045
                self.toggle_floating()?;
1046
            }
1047
1048
            KeyAction::SmartMoveWin => {
1049
                if let Arg::Int(direction) = arg {
1050
                    self.smart_move_window(*direction)?;
1051
                }
1052
            }
1053
1054
            KeyAction::ExchangeClient => {
1055
                if let Arg::Int(direction) = arg {
1056
                    self.exchange_client(*direction)?;
1057
                }
1058
            }
1059
1060
            KeyAction::FocusStack => {
1061
                if let Arg::Int(direction) = arg {
1062
                    self.cycle_focus(*direction)?;
1063
                }
1064
            }
1065
            KeyAction::FocusDirection => {
1066
                if let Arg::Int(direction) = arg {
1067
                    self.focus_direction(*direction)?;
1068
                }
1069
            }
1070
            KeyAction::SwapDirection => {
1071
                if let Arg::Int(direction) = arg {
1072
                    self.swap_direction(*direction)?;
1073
                }
1074
            }
1075
            KeyAction::Quit | KeyAction::Restart => {
1076
                // Handled in handle_event
1077
            }
1078
            KeyAction::Recompile => {
1079
                match std::process::Command::new("oxwm")
1080
                    .arg("--recompile")
1081
                    .spawn()
1082
                {
1083
                    Ok(_) => eprintln!("Recompiling in background"),
1084
                    Err(e) => eprintln!("Failed to spawn recompile: {}", e),
1085
                }
1086
            }
1087
            KeyAction::ViewTag => {
1088
                if let Arg::Int(tag_index) = arg {
1089
                    self.view_tag(*tag_index as usize)?;
1090
                }
1091
            }
1092
            KeyAction::MoveToTag => {
1093
                if let Arg::Int(tag_index) = arg {
1094
                    self.move_to_tag(*tag_index as usize)?;
1095
                }
1096
            }
1097
            KeyAction::ToggleGaps => {
1098
                self.gaps_enabled = !self.gaps_enabled;
1099
                self.apply_layout()?;
1100
            }
1101
            KeyAction::FocusMonitor => {
1102
                if let Arg::Int(direction) = arg {
1103
                    self.focus_monitor(*direction)?;
1104
                }
1105
            }
1106
            KeyAction::ShowKeybindOverlay => {
1107
                let monitor = &self.monitors[self.selected_monitor];
1108
                self.keybind_overlay.toggle(
1109
                    &self.connection,
1110
                    &self.font,
1111
                    &self.config.keybindings,
1112
                    monitor.width as u16,
1113
                    monitor.height as u16,
1114
                )?;
1115
            }
1116
            KeyAction::None => {}
1117
        }
1118
        Ok(())
1119
    }
1120
1121
    fn focus_monitor(&mut self, direction: i32) -> WmResult<()> {
1122
        if self.monitors.is_empty() {
1123
            return Ok(());
1124
        }
1125
1126
        let new_monitor = if direction > 0 {
1127
            (self.selected_monitor + 1) % self.monitors.len()
1128
        } else {
1129
            (self.selected_monitor + self.monitors.len() - 1) % self.monitors.len()
1130
        };
1131
1132
        if new_monitor == self.selected_monitor {
1133
            return Ok(());
1134
        }
1135
1136
        self.selected_monitor = new_monitor;
1137
1138
        self.update_bar()?;
1139
1140
        let visible = self.visible_windows_on_monitor(new_monitor);
1141
        if let Some(&win) = visible.first() {
1142
            self.set_focus(win)?;
1143
        }
1144
1145
        Ok(())
1146
    }
1147
1148
    fn is_window_visible(&self, window: Window) -> bool {
1149
        let window_mon = self.window_monitor.get(&window).copied().unwrap_or(0);
1150
1151
        if let Some(&tags) = self.window_tags.get(&window) {
1152
            let monitor = self.monitors.get(window_mon);
1153
            let selected_tags = monitor.map(|m| m.selected_tags).unwrap_or(0);
1154
            (tags & selected_tags) != 0
1155
        } else {
1156
            false
1157
        }
1158
    }
1159
1160
    fn visible_windows(&self) -> Vec<Window> {
1161
        self.windows
1162
            .iter()
1163
            .filter(|&&w| self.is_window_visible(w))
1164
            .copied()
1165
            .collect()
1166
    }
1167
1168
    fn visible_windows_on_monitor(&self, monitor_index: usize) -> Vec<Window> {
1169
        self.windows
1170
            .iter()
1171
            .filter(|&&w| {
1172
                let window_mon = self.window_monitor.get(&w).copied().unwrap_or(0);
1173
                if window_mon != monitor_index {
1174
                    return false;
1175
                }
1176
                if let Some(&tags) = self.window_tags.get(&w) {
1177
                    let monitor = self.monitors.get(monitor_index);
1178
                    let selected_tags = monitor.map(|m| m.selected_tags).unwrap_or(0);
1179
                    (tags & selected_tags) != 0
1180
                } else {
1181
                    false
1182
                }
1183
            })
1184
            .copied()
1185
            .collect()
1186
    }
1187
1188
    fn get_monitor_at_point(&self, x: i32, y: i32) -> Option<usize> {
1189
        self.monitors
1190
            .iter()
1191
            .position(|mon| mon.contains_point(x, y))
1192
    }
1193
1194
    // Dwm's g-loaded approach to handling the spam alternating crash.
1195
    fn update_window_visibility(&self) -> WmResult<()> {
1196
        for &window in &self.windows {
1197
            if !self.is_window_visible(window) {
1198
                if let Ok(geom) = self.connection.get_geometry(window)?.reply() {
1199
                    self.connection.configure_window(
1200
                        window,
1201
                        &ConfigureWindowAux::new()
1202
                            .x(-(geom.width as i32 * 2))
1203
                            .y(geom.y as i32),
1204
                    )?;
1205
                }
1206
            }
1207
        }
1208
        self.connection.flush()?;
1209
        Ok(())
1210
    }
1211
1212
    pub fn view_tag(&mut self, tag_index: usize) -> WmResult<()> {
1213
        if tag_index >= self.config.tags.len() {
1214
            return Ok(());
1215
        }
1216
1217
        if let Some(monitor) = self.monitors.get_mut(self.selected_monitor) {
1218
            monitor.selected_tags = tag_mask(tag_index);
1219
        }
1220
1221
        self.save_selected_tags()?;
1222
        self.update_window_visibility()?;
1223
        self.apply_layout()?;
1224
        self.update_bar()?;
1225
1226
        let visible = self.visible_windows_on_monitor(self.selected_monitor);
1227
        if let Some(&win) = visible.first() {
1228
            self.set_focus(win)?;
1229
        }
1230
1231
        Ok(())
1232
    }
1233
1234
    fn save_selected_tags(&self) -> WmResult<()> {
1235
        let net_current_desktop = self.atoms.net_current_desktop;
1236
1237
        let selected_tags = self
1238
            .monitors
1239
            .get(self.selected_monitor)
1240
            .map(|m| m.selected_tags)
1241
            .unwrap_or(tag_mask(0));
1242
        let desktop = selected_tags.trailing_zeros();
1243
1244
        let bytes = (desktop as u32).to_ne_bytes();
1245
        self.connection.change_property(
1246
            PropMode::REPLACE,
1247
            self.root,
1248
            net_current_desktop,
1249
            AtomEnum::CARDINAL,
1250
            32,
1251
            1,
1252
            &bytes,
1253
        )?;
1254
1255
        self.connection.flush()?;
1256
        Ok(())
1257
    }
1258
1259
    pub fn move_to_tag(&mut self, tag_index: usize) -> WmResult<()> {
1260
        if tag_index >= self.config.tags.len() {
1261
            return Ok(());
1262
        }
1263
1264
        if let Some(focused) = self
1265
            .monitors
1266
            .get(self.selected_monitor)
1267
            .and_then(|m| m.focused_window)
1268
        {
1269
            let mask = tag_mask(tag_index);
1270
            self.window_tags.insert(focused, mask);
1271
1272
            if let Err(error) = self.save_client_tag(focused, mask) {
1273
                eprintln!("Failed to save client tag: {:?}", error);
1274
            }
1275
1276
            self.update_window_visibility()?;
1277
            self.apply_layout()?;
1278
            self.update_bar()?;
1279
        }
1280
1281
        Ok(())
1282
    }
1283
1284
    pub fn cycle_focus(&mut self, direction: i32) -> WmResult<()> {
1285
        let visible = self.visible_windows();
1286
1287
        if visible.is_empty() {
1288
            return Ok(());
1289
        }
1290
1291
        let current = self
1292
            .monitors
1293
            .get(self.selected_monitor)
1294
            .and_then(|m| m.focused_window);
1295
1296
        let next_window = if let Some(current) = current {
1297
            if let Some(current_index) = visible.iter().position(|&w| w == current) {
1298
                let next_index = if direction > 0 {
1299
                    (current_index + 1) % visible.len()
1300
                } else {
1301
                    (current_index + visible.len() - 1) % visible.len()
1302
                };
1303
                visible[next_index]
1304
            } else {
1305
                visible[0]
1306
            }
1307
        } else {
1308
            visible[0]
1309
        };
1310
1311
        let is_tabbed = self.layout.name() == "tabbed";
1312
        if is_tabbed {
1313
            self.connection.configure_window(
1314
                next_window,
1315
                &ConfigureWindowAux::new().stack_mode(StackMode::ABOVE),
1316
            )?;
1317
        }
1318
1319
        self.set_focus(next_window)?;
1320
1321
        if is_tabbed {
1322
            self.update_tab_bars()?;
1323
        }
1324
1325
        Ok(())
1326
    }
1327
1328
    fn find_directional_window_candidate(&mut self, focused_window: Window, direction: i32) -> Option<Window> {
1329
        let visible_windows = self.visible_windows();
1330
        if visible_windows.len() < 2 {
1331
            return None;
1332
        }
1333
1334
        let focused_geometry = self.get_or_query_geometry(focused_window).ok()?;
1335
        let focused_center_x = focused_geometry.x_position + (focused_geometry.width as i16 / 2);
1336
        let focused_center_y = focused_geometry.y_position + (focused_geometry.height as i16 / 2);
1337
1338
        let mut candidates = Vec::new();
1339
1340
        for &window in &visible_windows {
1341
            if window == focused_window {
1342
                continue;
1343
            }
1344
1345
            let geometry = match self.get_or_query_geometry(window) {
1346
                Ok(geometry) => geometry,
1347
                Err(_) => continue,
1348
            };
1349
1350
            let center_x = geometry.x_position + (geometry.width as i16 / 2);
1351
            let center_y = geometry.y_position + (geometry.height as i16 / 2);
1352
1353
            let is_valid_direction = match direction {
1354
                0 => center_y < focused_center_y,
1355
                1 => center_y > focused_center_y,
1356
                2 => center_x < focused_center_x,
1357
                3 => center_x > focused_center_x,
1358
                _ => false,
1359
            };
1360
1361
            if is_valid_direction {
1362
                let delta_x = (center_x - focused_center_x) as i32;
1363
                let delta_y = (center_y - focused_center_y) as i32;
1364
                let distance_squared = delta_x * delta_x + delta_y * delta_y;
1365
                candidates.push((window, distance_squared));
1366
            }
1367
        }
1368
1369
        candidates.iter().min_by_key(|&(_window, distance)| distance).map(|&(window, _distance)| window)
1370
    }
1371
1372
    pub fn focus_direction(&mut self, direction: i32) -> WmResult<()> {
1373
        let focused_window = match self
1374
            .monitors
1375
            .get(self.selected_monitor)
1376
            .and_then(|monitor| monitor.focused_window)
1377
        {
1378
            Some(window) => window,
1379
            None => return Ok(()),
1380
        };
1381
1382
        if let Some(target_window) = self.find_directional_window_candidate(focused_window, direction) {
1383
            self.set_focus(target_window)?;
1384
        }
1385
1386
        Ok(())
1387
    }
1388
1389
    pub fn swap_direction(&mut self, direction: i32) -> WmResult<()> {
1390
        let focused_window = match self
1391
            .monitors
1392
            .get(self.selected_monitor)
1393
            .and_then(|monitor| monitor.focused_window)
1394
        {
1395
            Some(window) => window,
1396
            None => return Ok(()),
1397
        };
1398
1399
        if let Some(target_window) = self.find_directional_window_candidate(focused_window, direction) {
1400
            let focused_position = self.windows.iter().position(|&window| window == focused_window);
1401
            let target_position = self.windows.iter().position(|&window| window == target_window);
1402
1403
            if let (Some(focused_index), Some(target_index)) = (focused_position, target_position) {
1404
                self.windows.swap(focused_index, target_index);
1405
                self.apply_layout()?;
1406
                self.set_focus(focused_window)?;
1407
1408
                if let Ok(geometry) = self.get_or_query_geometry(focused_window) {
1409
                    self.connection.warp_pointer(
1410
                        x11rb::NONE,
1411
                        focused_window,
1412
                        0,
1413
                        0,
1414
                        0,
1415
                        0,
1416
                        geometry.width as i16 / 2,
1417
                        geometry.height as i16 / 2,
1418
                    )?;
1419
                }
1420
            }
1421
        }
1422
1423
        Ok(())
1424
    }
1425
1426
    fn grab_next_keys(&self, candidates: &[usize], keys_pressed: usize) -> WmResult<()> {
1427
        use std::collections::HashMap;
1428
        use x11rb::protocol::xproto::Keycode;
1429
1430
        let setup = self.connection.setup();
1431
        let min_keycode = setup.min_keycode;
1432
        let max_keycode = setup.max_keycode;
1433
1434
        let keyboard_mapping = self
1435
            .connection
1436
            .get_keyboard_mapping(min_keycode, max_keycode - min_keycode + 1)?
1437
            .reply()?;
1438
1439
        let mut keysym_to_keycode: HashMap<keyboard::keysyms::Keysym, Vec<Keycode>> =
1440
            HashMap::new();
1441
        let keysyms_per_keycode = keyboard_mapping.keysyms_per_keycode;
1442
1443
        for keycode in min_keycode..=max_keycode {
1444
            let index = (keycode - min_keycode) as usize * keysyms_per_keycode as usize;
1445
            for i in 0..keysyms_per_keycode as usize {
1446
                if let Some(&keysym) = keyboard_mapping.keysyms.get(index + i) {
1447
                    if keysym != 0 {
1448
                        keysym_to_keycode
1449
                            .entry(keysym)
1450
                            .or_insert_with(Vec::new)
1451
                            .push(keycode);
1452
                    }
1453
                }
1454
            }
1455
        }
1456
1457
        let mut grabbed_keys: HashSet<(u16, Keycode)> = HashSet::new();
1458
1459
        let ignore_modifiers = [
1460
            0,
1461
            u16::from(ModMask::LOCK),
1462
            u16::from(ModMask::M2),
1463
            u16::from(ModMask::LOCK | ModMask::M2),
1464
        ];
1465
1466
        for &candidate_index in candidates {
1467
            let binding = &self.config.keybindings[candidate_index];
1468
            if keys_pressed < binding.keys.len() {
1469
                let next_key = &binding.keys[keys_pressed];
1470
                let modifier_mask = keyboard::handlers::modifiers_to_mask(&next_key.modifiers);
1471
1472
                if let Some(keycodes) = keysym_to_keycode.get(&next_key.keysym) {
1473
                    if let Some(&keycode) = keycodes.first() {
1474
                        for &ignore_mask in &ignore_modifiers {
1475
                            let grab_mask = modifier_mask | ignore_mask;
1476
                            let key_tuple = (grab_mask, keycode);
1477
1478
                            if grabbed_keys.insert(key_tuple) {
1479
                                self.connection.grab_key(
1480
                                    false,
1481
                                    self.root,
1482
                                    grab_mask.into(),
1483
                                    keycode,
1484
                                    GrabMode::ASYNC,
1485
                                    GrabMode::ASYNC,
1486
                                )?;
1487
                            }
1488
                        }
1489
                    }
1490
                }
1491
            }
1492
        }
1493
1494
        if let Some(keycodes) = keysym_to_keycode.get(&keyboard::keysyms::XK_ESCAPE) {
1495
            if let Some(&keycode) = keycodes.first() {
1496
                for &ignore_mask in &ignore_modifiers {
1497
                    self.connection.grab_key(
1498
                        false,
1499
                        self.root,
1500
                        ModMask::from(ignore_mask),
1501
                        keycode,
1502
                        GrabMode::ASYNC,
1503
                        GrabMode::ASYNC,
1504
                    )?;
1505
                }
1506
            }
1507
        }
1508
1509
        self.connection.flush()?;
1510
        Ok(())
1511
    }
1512
1513
    fn ungrab_chord_keys(&self) -> WmResult<()> {
1514
        self.connection
1515
            .ungrab_key(x11rb::protocol::xproto::Grab::ANY, self.root, ModMask::ANY)?;
1516
        keyboard::setup_keybinds(&self.connection, self.root, &self.config.keybindings)?;
1517
        self.connection.flush()?;
1518
        Ok(())
1519
    }
1520
1521
    fn kill_client(&self, window: Window) -> WmResult<()> {
1522
        if self.send_event(window, self.atoms.wm_delete_window)? {
1523
            self.connection.flush()?;
1524
        } else {
1525
            eprintln!("Window {} doesn't support WM_DELETE_WINDOW, killing forcefully", window);
1526
            self.connection.kill_client(window)?;
1527
            self.connection.flush()?;
1528
        }
1529
        Ok(())
1530
    }
1531
1532
    fn send_event(&self, window: Window, protocol: Atom) -> WmResult<bool> {
1533
        let protocols_reply = self.connection.get_property(
1534
            false,
1535
            window,
1536
            self.atoms.wm_protocols,
1537
            AtomEnum::ATOM,
1538
            0,
1539
            100,
1540
        )?.reply();
1541
1542
        let protocols_reply = match protocols_reply {
1543
            Ok(reply) => reply,
1544
            Err(_) => return Ok(false),
1545
        };
1546
1547
        let protocols: Vec<Atom> = protocols_reply
1548
            .value
1549
            .chunks_exact(4)
1550
            .map(|chunk| u32::from_ne_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]))
1551
            .collect();
1552
1553
        if !protocols.contains(&protocol) {
1554
            return Ok(false);
1555
        }
1556
1557
        let event = x11rb::protocol::xproto::ClientMessageEvent {
1558
            response_type: x11rb::protocol::xproto::CLIENT_MESSAGE_EVENT,
1559
            format: 32,
1560
            sequence: 0,
1561
            window,
1562
            type_: self.atoms.wm_protocols,
1563
            data: x11rb::protocol::xproto::ClientMessageData::from([protocol, x11rb::CURRENT_TIME, 0, 0, 0]),
1564
        };
1565
1566
        self.connection.send_event(
1567
            false,
1568
            window,
1569
            EventMask::NO_EVENT,
1570
            event,
1571
        )?;
1572
        self.connection.flush()?;
1573
        Ok(true)
1574
    }
1575
1576
    fn fullscreen(&mut self) -> WmResult<()> {
1577
        if self.show_bar {
1578
            let windows: Vec<Window> = self.windows.iter()
1579
                .filter(|&&w| self.is_window_visible(w))
1580
                .copied()
1581
                .collect();
1582
1583
            for window in &windows {
1584
                if let Ok(geom) = self.connection.get_geometry(*window)?.reply() {
1585
                        self.floating_geometry_before_fullscreen.insert(
1586
                            *window,
1587
                            (geom.x, geom.y, geom.width, geom.height, geom.border_width as u16),
1588
                        );
1589
                    }
1590
            }
1591
1592
            self.last_layout = Some(self.layout.name());
1593
            if let Ok(layout) = layout_from_str("monocle") {
1594
                self.layout = layout;
1595
            }
1596
            self.toggle_bar()?;
1597
            self.apply_layout()?;
1598
1599
            let border_width = self.config.border_width;
1600
            let floating_windows: Vec<Window> = windows.iter()
1601
                .filter(|&&w| self.floating_windows.contains(&w))
1602
                .copied()
1603
                .collect();
1604
1605
            for window in floating_windows {
1606
                let monitor_idx = *self.window_monitor.get(&window).unwrap_or(&self.selected_monitor);
1607
                let monitor = &self.monitors[monitor_idx];
1608
1609
                let (outer_gap_h, outer_gap_v) = if self.gaps_enabled {
1610
                    (
1611
                        self.config.gap_outer_horizontal,
1612
                        self.config.gap_outer_vertical,
1613
                    )
1614
                } else {
1615
                    (0, 0)
1616
                };
1617
1618
                let window_x = monitor.x + outer_gap_h as i32;
1619
                let window_y = monitor.y + outer_gap_v as i32;
1620
                let window_width = monitor.width.saturating_sub(2 * outer_gap_h).saturating_sub(2 * border_width);
1621
                let window_height = monitor.height.saturating_sub(2 * outer_gap_v).saturating_sub(2 * border_width);
1622
1623
                self.connection.configure_window(
1624
                    window,
1625
                    &x11rb::protocol::xproto::ConfigureWindowAux::new()
1626
                        .x(window_x)
1627
                        .y(window_y)
1628
                        .width(window_width)
1629
                        .height(window_height),
1630
                )?;
1631
            }
1632
            self.connection.flush()?;
1633
        } else {
1634
            if let Some(last) = self.last_layout {
1635
                if let Ok(layout) = layout_from_str(last) {
1636
                    self.layout = layout;
1637
                }
1638
            }
1639
1640
            let windows_to_restore: Vec<Window> = self.floating_geometry_before_fullscreen
1641
                .keys()
1642
                .copied()
1643
                .collect();
1644
1645
            for window in windows_to_restore {
1646
                if let Some(&(x, y, width, height, border_width)) = self.floating_geometry_before_fullscreen.get(&window) {
1647
                    self.connection.configure_window(
1648
                        window,
1649
                        &x11rb::protocol::xproto::ConfigureWindowAux::new()
1650
                            .x(x as i32)
1651
                            .y(y as i32)
1652
                            .width(width as u32)
1653
                            .height(height as u32)
1654
                            .border_width(border_width as u32),
1655
                    )?;
1656
1657
                    self.update_geometry_cache(window, CachedGeometry {
1658
                        x_position: x,
1659
                        y_position: y,
1660
                        width,
1661
                        height,
1662
                        border_width,
1663
                    });
1664
1665
                    self.floating_geometry_before_fullscreen.remove(&window);
1666
                }
1667
            }
1668
            self.connection.flush()?;
1669
1670
            self.toggle_bar()?;
1671
1672
            if self.layout.name() != "normie" {
1673
                self.apply_layout()?;
1674
            } else {
1675
                if let Some(bar) = self.bars.get(self.selected_monitor) {
1676
                    self.connection.configure_window(
1677
                        bar.window(),
1678
                        &x11rb::protocol::xproto::ConfigureWindowAux::new()
1679
                            .stack_mode(x11rb::protocol::xproto::StackMode::ABOVE),
1680
                    )?;
1681
                    self.connection.flush()?;
1682
                }
1683
            }
1684
        }
1685
        Ok(())
1686
    }
1687
1688
    fn set_window_fullscreen(&mut self, window: Window, fullscreen: bool) -> WmResult<()> {
1689
        if fullscreen && !self.fullscreen_windows.contains(&window) {
1690
            let bytes = self.atoms.net_wm_state_fullscreen.to_ne_bytes().to_vec();
1691
            self.connection.change_property(
1692
                PropMode::REPLACE,
1693
                window,
1694
                self.atoms.net_wm_state,
1695
                AtomEnum::ATOM,
1696
                32,
1697
                1,
1698
                &bytes,
1699
            )?;
1700
1701
            self.fullscreen_windows.insert(window);
1702
1703
            let monitor_idx = *self.window_monitor.get(&window).unwrap_or(&self.selected_monitor);
1704
            let monitor = &self.monitors[monitor_idx];
1705
1706
            if self.show_bar {
1707
                if let Some(bar) = self.bars.get(monitor_idx) {
1708
                    self.connection.unmap_window(bar.window())?;
1709
                }
1710
            }
1711
1712
            self.connection.configure_window(
1713
                window,
1714
                &x11rb::protocol::xproto::ConfigureWindowAux::new()
1715
                    .border_width(0)
1716
                    .x(monitor.x)
1717
                    .y(monitor.y)
1718
                    .width(monitor.width as u32)
1719
                    .height(monitor.height as u32)
1720
                    .stack_mode(x11rb::protocol::xproto::StackMode::ABOVE),
1721
            )?;
1722
1723
            self.connection.flush()?;
1724
            self.update_bar()?;
1725
        } else if !fullscreen && self.fullscreen_windows.contains(&window) {
1726
            self.connection.change_property(
1727
                PropMode::REPLACE,
1728
                window,
1729
                self.atoms.net_wm_state,
1730
                AtomEnum::ATOM,
1731
                32,
1732
                0,
1733
                &[],
1734
            )?;
1735
1736
            self.fullscreen_windows.remove(&window);
1737
1738
            self.connection.configure_window(
1739
                window,
1740
                &x11rb::protocol::xproto::ConfigureWindowAux::new()
1741
                    .border_width(self.config.border_width),
1742
            )?;
1743
1744
            let monitor_idx = *self.window_monitor.get(&window).unwrap_or(&self.selected_monitor);
1745
            if self.show_bar {
1746
                if let Some(bar) = self.bars.get(monitor_idx) {
1747
                    self.connection.map_window(bar.window())?;
1748
                }
1749
            }
1750
1751
            self.apply_layout()?;
1752
        }
1753
1754
        Ok(())
1755
    }
1756
1757
    fn toggle_bar(&mut self) -> WmResult<()> {
1758
        self.show_bar = !self.show_bar;
1759
        if let Some(bar) = self.bars.get(self.selected_monitor) {
1760
            if self.show_bar {
1761
                self.connection.map_window(bar.window())?;
1762
            } else {
1763
                self.connection.unmap_window(bar.window())?;
1764
            }
1765
            self.connection.flush()?;
1766
        }
1767
        self.apply_layout()?;
1768
        Ok(())
1769
    }
1770
1771
    fn get_transient_parent(&self, window: Window) -> Option<Window> {
1772
        self.connection
1773
            .get_property(
1774
                false,
1775
                window,
1776
                AtomEnum::WM_TRANSIENT_FOR,
1777
                AtomEnum::WINDOW,
1778
                0,
1779
                1,
1780
            )
1781
            .ok()
1782
            .and_then(|cookie| cookie.reply().ok())
1783
            .filter(|reply| !reply.value.is_empty())
1784
            .and_then(|reply| {
1785
                if reply.value.len() >= 4 {
1786
                    let parent_window = u32::from_ne_bytes([
1787
                        reply.value[0],
1788
                        reply.value[1],
1789
                        reply.value[2],
1790
                        reply.value[3],
1791
                    ]);
1792
                    Some(parent_window)
1793
                } else {
1794
                    None
1795
                }
1796
            })
1797
    }
1798
1799
    fn is_transient_window(&self, window: Window) -> bool {
1800
        self.get_transient_parent(window).is_some()
1801
    }
1802
1803
    fn is_dialog_window(&self, window: Window) -> bool {
1804
        let window_type_property = self.connection
1805
            .get_property(
1806
                false,
1807
                window,
1808
                self.atoms.net_wm_window_type,
1809
                AtomEnum::ATOM,
1810
                0,
1811
                32,
1812
            )
1813
            .ok()
1814
            .and_then(|cookie| cookie.reply().ok());
1815
1816
        if let Some(reply) = window_type_property {
1817
            let atoms: Vec<Atom> = reply
1818
                .value
1819
                .chunks_exact(4)
1820
                .map(|chunk| u32::from_ne_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]))
1821
                .collect();
1822
1823
            atoms.contains(&self.atoms.net_wm_window_type_dialog)
1824
        } else {
1825
            false
1826
        }
1827
    }
1828
1829
    fn get_window_class(&self, window: Window) -> Option<String> {
1830
        self.connection
1831
            .get_property(false, window, AtomEnum::WM_CLASS, AtomEnum::STRING, 0, 1024)
1832
            .ok()
1833
            .and_then(|cookie| cookie.reply().ok())
1834
            .and_then(|reply| {
1835
                if reply.value.is_empty() {
1836
                    None
1837
                } else {
1838
                    std::str::from_utf8(&reply.value).ok().map(|s| {
1839
                        s.split('\0').nth(1).unwrap_or(s.split('\0').next().unwrap_or("")).to_string()
1840
                    })
1841
                }
1842
            })
1843
    }
1844
1845
    fn manage_window(&mut self, window: Window) -> WmResult<()> {
1846
        let geometry = self.connection.get_geometry(window)?.reply()?;
1847
        let mut window_x = geometry.x as i32;
1848
        let mut window_y = geometry.y as i32;
1849
        let window_width = geometry.width as u32;
1850
        let window_height = geometry.height as u32;
1851
1852
        let transient_parent = self.get_transient_parent(window);
1853
        let (window_tags, monitor_index) = if let Some(parent) = transient_parent {
1854
            let parent_tags = self.window_tags.get(&parent).copied().unwrap_or_else(|| {
1855
                self.monitors
1856
                    .get(self.selected_monitor)
1857
                    .map(|m| m.selected_tags)
1858
                    .unwrap_or(tag_mask(0))
1859
            });
1860
            let parent_monitor = self.window_monitor.get(&parent).copied().unwrap_or(self.selected_monitor);
1861
            (parent_tags, parent_monitor)
1862
        } else {
1863
            let selected_tags = self.monitors
1864
                .get(self.selected_monitor)
1865
                .map(|m| m.selected_tags)
1866
                .unwrap_or(tag_mask(0));
1867
            (selected_tags, self.selected_monitor)
1868
        };
1869
1870
        let monitor = self.monitors[monitor_index].clone();
1871
        let border_width = self.config.border_width;
1872
1873
        if window_x + (window_width as i32) + (2 * border_width as i32) > monitor.x + monitor.width as i32 {
1874
            window_x = monitor.x + monitor.width as i32 - (window_width as i32) - (2 * border_width as i32);
1875
        }
1876
        if window_y + (window_height as i32) + (2 * border_width as i32) > monitor.y + monitor.height as i32 {
1877
            window_y = monitor.y + monitor.height as i32 - (window_height as i32) - (2 * border_width as i32);
1878
        }
1879
        window_x = window_x.max(monitor.x);
1880
        window_y = window_y.max(monitor.y);
1881
1882
        let is_transient = transient_parent.is_some();
1883
        let is_dialog = self.is_dialog_window(window);
1884
1885
        let class_name = self.get_window_class(window).unwrap_or_default();
1886
        eprintln!("MapRequest 0x{:x}: class='{}' size={}x{} pos=({},{}) transient={} dialog={}",
1887
            window, class_name, window_width, window_height, window_x, window_y, is_transient, is_dialog);
1888
1889
        let off_screen_x = window_x + (2 * self.screen.width_in_pixels as i32);
1890
1891
        self.connection.configure_window(
1892
            window,
1893
            &ConfigureWindowAux::new()
1894
                .x(off_screen_x)
1895
                .y(window_y)
1896
                .width(window_width)
1897
                .height(window_height)
1898
                .border_width(border_width)
1899
        )?;
1900
1901
        self.connection.change_window_attributes(
1902
            window,
1903
            &ChangeWindowAttributesAux::new().event_mask(
1904
                EventMask::ENTER_WINDOW | EventMask::STRUCTURE_NOTIFY | EventMask::PROPERTY_CHANGE
1905
            ),
1906
        )?;
1907
1908
        self.windows.push(window);
1909
        self.window_tags.insert(window, window_tags);
1910
        self.window_monitor.insert(window, monitor_index);
1911
1912
        if is_transient || is_dialog {
1913
            self.floating_windows.insert(window);
1914
1915
            let (center_x, center_y) = if let Some(parent) = transient_parent {
1916
                if let Ok(parent_geom) = self.connection.get_geometry(parent)?.reply() {
1917
                    let parent_center_x = parent_geom.x as i32 + (parent_geom.width as i32 / 2);
1918
                    let parent_center_y = parent_geom.y as i32 + (parent_geom.height as i32 / 2);
1919
                    (parent_center_x, parent_center_y)
1920
                } else {
1921
                    let monitor_center_x = monitor.x + (monitor.width as i32 / 2);
1922
                    let monitor_center_y = monitor.y + (monitor.height as i32 / 2);
1923
                    (monitor_center_x, monitor_center_y)
1924
                }
1925
            } else {
1926
                let monitor_center_x = monitor.x + (monitor.width as i32 / 2);
1927
                let monitor_center_y = monitor.y + (monitor.height as i32 / 2);
1928
                (monitor_center_x, monitor_center_y)
1929
            };
1930
1931
            let positioned_x = center_x - (window_width as i32 / 2);
1932
            let positioned_y = center_y - (window_height as i32 / 2);
1933
1934
            let clamped_x = positioned_x
1935
                .max(monitor.x)
1936
                .min(monitor.x + monitor.width as i32 - window_width as i32);
1937
            let clamped_y = positioned_y
1938
                .max(monitor.y)
1939
                .min(monitor.y + monitor.height as i32 - window_height as i32);
1940
1941
            self.update_geometry_cache(window, CachedGeometry {
1942
                x_position: clamped_x as i16,
1943
                y_position: clamped_y as i16,
1944
                width: window_width as u16,
1945
                height: window_height as u16,
1946
                border_width: border_width as u16,
1947
            });
1948
1949
            self.connection.configure_window(
1950
                window,
1951
                &ConfigureWindowAux::new()
1952
                    .x(clamped_x)
1953
                    .y(clamped_y)
1954
                    .width(window_width)
1955
                    .height(window_height)
1956
                    .border_width(border_width)
1957
                    .stack_mode(StackMode::ABOVE),
1958
            )?;
1959
        }
1960
1961
        let is_normie_layout = self.layout.name() == "normie";
1962
        if is_normie_layout && !is_transient && !is_dialog {
1963
            if let Ok(pointer) = self.connection.query_pointer(self.root)?.reply() {
1964
                let cursor_monitor = self.get_monitor_at_point(pointer.root_x as i32, pointer.root_y as i32)
1965
                    .and_then(|idx| self.monitors.get(idx))
1966
                    .unwrap_or(&monitor);
1967
1968
                let float_width = (cursor_monitor.width as f32 * 0.6) as u32;
1969
                let float_height = (cursor_monitor.height as f32 * 0.6) as u32;
1970
                let spawn_x = pointer.root_x as i32 - (float_width as i32 / 2);
1971
                let spawn_y = pointer.root_y as i32 - (float_height as i32 / 2);
1972
1973
                let clamped_x = spawn_x
1974
                    .max(cursor_monitor.x)
1975
                    .min(cursor_monitor.x + cursor_monitor.width as i32 - float_width as i32);
1976
                let clamped_y = spawn_y
1977
                    .max(cursor_monitor.y)
1978
                    .min(cursor_monitor.y + cursor_monitor.height as i32 - float_height as i32);
1979
1980
                self.connection.configure_window(
1981
                    window,
1982
                    &ConfigureWindowAux::new()
1983
                        .x(clamped_x)
1984
                        .y(clamped_y)
1985
                        .width(float_width)
1986
                        .height(float_height)
1987
                        .border_width(border_width)
1988
                        .stack_mode(StackMode::ABOVE),
1989
                )?;
1990
1991
                self.floating_windows.insert(window);
1992
            }
1993
        }
1994
1995
        self.set_wm_state(window, 1)?;
1996
        if let Err(error) = self.save_client_tag(window, window_tags) {
1997
            eprintln!("Failed to save client tag for new window: {:?}", error);
1998
        }
1999
2000
        self.apply_layout()?;
2001
        self.connection.map_window(window)?;
2002
        self.update_bar()?;
2003
        self.set_focus(window)?;
2004
2005
        if self.layout.name() == "tabbed" {
2006
            self.update_tab_bars()?;
2007
        }
2008
2009
        Ok(())
2010
    }
2011
2012
    pub fn set_focus(&mut self, window: Window) -> WmResult<()> {
2013
        let old_focused = self.previous_focused;
2014
2015
        if let Some(monitor) = self.monitors.get_mut(self.selected_monitor) {
2016
            monitor.focused_window = Some(window);
2017
        }
2018
2019
        self.connection
2020
            .set_input_focus(InputFocus::POINTER_ROOT, window, x11rb::CURRENT_TIME)?;
2021
        self.connection.flush()?;
2022
2023
        self.update_focus_visuals(old_focused, window)?;
2024
        self.previous_focused = Some(window);
2025
2026
        if self.layout.name() == "tabbed" {
2027
            self.update_tab_bars()?;
2028
        }
2029
2030
        Ok(())
2031
    }
2032
2033
    fn update_focus_visuals(
2034
        &self,
2035
        old_focused: Option<Window>,
2036
        new_focused: Window,
2037
    ) -> WmResult<()> {
2038
        if let Some(old_win) = old_focused {
2039
            if old_win != new_focused {
2040
                self.connection.configure_window(
2041
                    old_win,
2042
                    &ConfigureWindowAux::new().border_width(self.config.border_width),
2043
                )?;
2044
2045
                self.connection.change_window_attributes(
2046
                    old_win,
2047
                    &ChangeWindowAttributesAux::new().border_pixel(self.config.border_unfocused),
2048
                )?;
2049
            }
2050
        }
2051
2052
        self.connection.configure_window(
2053
            new_focused,
2054
            &ConfigureWindowAux::new().border_width(self.config.border_width),
2055
        )?;
2056
2057
        self.connection.change_window_attributes(
2058
            new_focused,
2059
            &ChangeWindowAttributesAux::new().border_pixel(self.config.border_focused),
2060
        )?;
2061
2062
        self.connection.flush()?;
2063
        Ok(())
2064
    }
2065
2066
    fn move_mouse(&mut self, window: Window) -> WmResult<()> {
2067
        self.floating_windows.insert(window);
2068
2069
        let geometry = self.connection.get_geometry(window)?.reply()?;
2070
2071
        self.connection
2072
            .grab_pointer(
2073
                false,
2074
                self.root,
2075
                (EventMask::POINTER_MOTION | EventMask::BUTTON_RELEASE).into(),
2076
                GrabMode::ASYNC,
2077
                GrabMode::ASYNC,
2078
                x11rb::NONE,
2079
                x11rb::NONE,
2080
                x11rb::CURRENT_TIME,
2081
            )?
2082
            .reply()?;
2083
2084
        let pointer = self.connection.query_pointer(self.root)?.reply()?;
2085
        let (start_x, start_y) = (pointer.root_x, pointer.root_y);
2086
2087
        self.connection.configure_window(
2088
            window,
2089
            &ConfigureWindowAux::new().stack_mode(StackMode::ABOVE),
2090
        )?;
2091
2092
        loop {
2093
            let event = self.connection.wait_for_event()?;
2094
            match event {
2095
                Event::MotionNotify(e) => {
2096
                    let new_x = geometry.x + (e.root_x - start_x);
2097
                    let new_y = geometry.y + (e.root_y - start_y);
2098
                    self.connection.configure_window(
2099
                        window,
2100
                        &ConfigureWindowAux::new().x(new_x as i32).y(new_y as i32),
2101
                    )?;
2102
                    self.connection.flush()?;
2103
                }
2104
                Event::ButtonRelease(_) => break,
2105
                _ => {}
2106
            }
2107
        }
2108
2109
        self.connection
2110
            .ungrab_pointer(x11rb::CURRENT_TIME)?
2111
            .check()?;
2112
        self.connection
2113
            .allow_events(Allow::REPLAY_POINTER, x11rb::CURRENT_TIME)?
2114
            .check()?;
2115
2116
        Ok(())
2117
    }
2118
2119
    fn resize_mouse(&mut self, window: Window) -> WmResult<()> {
2120
        self.floating_windows.insert(window);
2121
2122
        let geometry = self.connection.get_geometry(window)?.reply()?;
2123
2124
        self.connection.warp_pointer(
2125
            x11rb::NONE,
2126
            window,
2127
            0,
2128
            0,
2129
            0,
2130
            0,
2131
            geometry.width as i16,
2132
            geometry.height as i16,
2133
        )?;
2134
2135
        self.connection
2136
            .grab_pointer(
2137
                false,
2138
                self.root,
2139
                (EventMask::POINTER_MOTION | EventMask::BUTTON_RELEASE).into(),
2140
                GrabMode::ASYNC,
2141
                GrabMode::ASYNC,
2142
                x11rb::NONE,
2143
                x11rb::NONE,
2144
                x11rb::CURRENT_TIME,
2145
            )?
2146
            .reply()?;
2147
2148
        self.connection.configure_window(
2149
            window,
2150
            &ConfigureWindowAux::new().stack_mode(StackMode::ABOVE),
2151
        )?;
2152
2153
        loop {
2154
            let event = self.connection.wait_for_event()?;
2155
            match event {
2156
                Event::MotionNotify(e) => {
2157
                    let new_width = (e.root_x - geometry.x).max(1) as u32;
2158
                    let new_height = (e.root_y - geometry.y).max(1) as u32;
2159
2160
                    self.connection.configure_window(
2161
                        window,
2162
                        &ConfigureWindowAux::new()
2163
                            .width(new_width)
2164
                            .height(new_height),
2165
                    )?;
2166
                    self.connection.flush()?;
2167
                }
2168
                Event::ButtonRelease(_) => break,
2169
                _ => {}
2170
            }
2171
        }
2172
2173
        self.connection
2174
            .ungrab_pointer(x11rb::CURRENT_TIME)?
2175
            .check()?;
2176
        self.connection
2177
            .allow_events(Allow::REPLAY_POINTER, x11rb::CURRENT_TIME)?
2178
            .check()?;
2179
2180
        Ok(())
2181
    }
2182
2183
    fn handle_event(&mut self, event: Event) -> WmResult<Option<bool>> {
2184
        match event {
2185
            Event::KeyPress(ref key_event) if key_event.event == self.overlay.window() => {
2186
                if self.overlay.is_visible() {
2187
                    if let Err(error) = self.overlay.hide(&self.connection) {
2188
                        eprintln!("Failed to hide overlay: {:?}", error);
2189
                    }
2190
                }
2191
                return Ok(None);
2192
            }
2193
            Event::ButtonPress(ref button_event) if button_event.event == self.overlay.window() => {
2194
                if self.overlay.is_visible() {
2195
                    if let Err(error) = self.overlay.hide(&self.connection) {
2196
                        eprintln!("Failed to hide overlay: {:?}", error);
2197
                    }
2198
                }
2199
                return Ok(None);
2200
            }
2201
            Event::Expose(ref expose_event) if expose_event.window == self.overlay.window() => {
2202
                if self.overlay.is_visible() {
2203
                    if let Err(error) = self.overlay.draw(&self.connection, &self.font) {
2204
                        eprintln!("Failed to draw overlay: {:?}", error);
2205
                    }
2206
                }
2207
                return Ok(None);
2208
            }
2209
            Event::KeyPress(ref e) if e.event == self.keybind_overlay.window() => {
2210
                if self.keybind_overlay.is_visible()
2211
                    && !self.keybind_overlay.should_suppress_input()
2212
                {
2213
                    use crate::keyboard::keysyms;
2214
                    let keyboard_mapping = self
2215
                        .connection
2216
                        .get_keyboard_mapping(
2217
                            self.connection.setup().min_keycode,
2218
                            self.connection.setup().max_keycode
2219
                                - self.connection.setup().min_keycode
2220
                                + 1,
2221
                        )?
2222
                        .reply()?;
2223
2224
                    let min_keycode = self.connection.setup().min_keycode;
2225
                    let keysyms_per_keycode = keyboard_mapping.keysyms_per_keycode;
2226
                    let index = (e.detail - min_keycode) as usize * keysyms_per_keycode as usize;
2227
2228
                    if let Some(&keysym) = keyboard_mapping.keysyms.get(index) {
2229
                        if keysym == keysyms::XK_ESCAPE || keysym == keysyms::XK_Q {
2230
                            if let Err(error) = self.keybind_overlay.hide(&self.connection) {
2231
                                eprintln!("Failed to hide keybind overlay: {:?}", error);
2232
                            }
2233
                        }
2234
                    }
2235
                }
2236
                return Ok(None);
2237
            }
2238
            Event::ButtonPress(ref e) if e.event == self.keybind_overlay.window() => {
2239
                return Ok(None);
2240
            }
2241
            Event::Expose(ref expose_event) if expose_event.window == self.keybind_overlay.window() => {
2242
                if self.keybind_overlay.is_visible() {
2243
                    if let Err(error) = self.keybind_overlay.draw(&self.connection, &self.font) {
2244
                        eprintln!("Failed to draw keybind overlay: {:?}", error);
2245
                    }
2246
                }
2247
                return Ok(None);
2248
            }
2249
            Event::MapRequest(event) => {
2250
                let attrs = match self.connection.get_window_attributes(event.window)?.reply() {
2251
                    Ok(attrs) => attrs,
2252
                    Err(_) => return Ok(None),
2253
                };
2254
2255
                if attrs.override_redirect {
2256
                    return Ok(None);
2257
                }
2258
2259
                if !self.windows.contains(&event.window) {
2260
                    self.manage_window(event.window)?;
2261
                }
2262
            }
2263
            Event::UnmapNotify(event) => {
2264
                if self.windows.contains(&event.window) && self.is_window_visible(event.window) {
2265
                    self.remove_window(event.window)?;
2266
                }
2267
            }
2268
            Event::DestroyNotify(event) => {
2269
                if self.windows.contains(&event.window) {
2270
                    self.remove_window(event.window)?;
2271
                }
2272
            }
2273
            Event::EnterNotify(event) => {
2274
                if event.mode != x11rb::protocol::xproto::NotifyMode::NORMAL {
2275
                    return Ok(None);
2276
                }
2277
                if self.windows.contains(&event.event) {
2278
                    if let Some(&window_monitor_index) = self.window_monitor.get(&event.event) {
2279
                        if window_monitor_index != self.selected_monitor {
2280
                            self.selected_monitor = window_monitor_index;
2281
                            self.update_bar()?;
2282
                        }
2283
                    }
2284
                    self.set_focus(event.event)?;
2285
                }
2286
            }
2287
            Event::MotionNotify(event) => {
2288
                if event.event != self.root {
2289
                    return Ok(None);
2290
                }
2291
2292
                if let Some(monitor_index) =
2293
                    self.get_monitor_at_point(event.root_x as i32, event.root_y as i32)
2294
                {
2295
                    if monitor_index != self.selected_monitor {
2296
                        self.selected_monitor = monitor_index;
2297
                        self.update_bar()?;
2298
2299
                        let visible = self.visible_windows_on_monitor(monitor_index);
2300
                        if let Some(&win) = visible.first() {
2301
                            self.set_focus(win)?;
2302
                        }
2303
                    }
2304
                }
2305
            }
2306
            Event::KeyPress(event) => {
2307
                let result = keyboard::handle_key_press(
2308
                    event,
2309
                    &self.config.keybindings,
2310
                    &self.keychord_state,
2311
                    &self.connection,
2312
                )?;
2313
2314
                match result {
2315
                    keyboard::handlers::KeychordResult::Completed(action, arg) => {
2316
                        self.keychord_state = keyboard::handlers::KeychordState::Idle;
2317
                        self.ungrab_chord_keys()?;
2318
                        self.update_bar()?;
2319
2320
                        match action {
2321
                            KeyAction::Quit => return Ok(Some(false)),
2322
                            KeyAction::Restart => match self.try_reload_config() {
2323
                                Ok(()) => {
2324
                                    self.gaps_enabled = self.config.gaps_enabled;
2325
                                    self.error_message = None;
2326
                                    if let Err(error) = self.overlay.hide(&self.connection) {
2327
                                        eprintln!("Failed to hide overlay after config reload: {:?}", error);
2328
                                    }
2329
                                    self.apply_layout()?;
2330
                                    self.update_bar()?;
2331
                                }
2332
                                Err(err) => {
2333
                                    eprintln!("Config reload error: {}", err);
2334
                                    self.error_message = Some(err.clone());
2335
                                    let screen_width = self.screen.width_in_pixels;
2336
                                    let screen_height = self.screen.height_in_pixels;
2337
                                    match self.overlay.show_error(
2338
                                        &self.connection,
2339
                                        &self.font,
2340
                                        &err,
2341
                                        screen_width,
2342
                                        screen_height,
2343
                                    ) {
2344
                                        Ok(()) => eprintln!("Error modal displayed"),
2345
                                        Err(e) => eprintln!("Failed to show error modal: {:?}", e),
2346
                                    }
2347
                                }
2348
                            },
2349
                            _ => self.handle_key_action(action, &arg)?,
2350
                        }
2351
                    }
2352
                    keyboard::handlers::KeychordResult::InProgress(candidates) => {
2353
                        let keys_pressed = match &self.keychord_state {
2354
                            keyboard::handlers::KeychordState::Idle => 1,
2355
                            keyboard::handlers::KeychordState::InProgress {
2356
                                keys_pressed, ..
2357
                            } => keys_pressed + 1,
2358
                        };
2359
2360
                        self.keychord_state = keyboard::handlers::KeychordState::InProgress {
2361
                            candidates: candidates.clone(),
2362
                            keys_pressed,
2363
                        };
2364
2365
                        self.grab_next_keys(&candidates, keys_pressed)?;
2366
                        self.update_bar()?;
2367
                    }
2368
                    keyboard::handlers::KeychordResult::Cancelled
2369
                    | keyboard::handlers::KeychordResult::None => {
2370
                        self.keychord_state = keyboard::handlers::KeychordState::Idle;
2371
                        self.ungrab_chord_keys()?;
2372
                        self.update_bar()?;
2373
                    }
2374
                }
2375
            }
2376
            Event::ButtonPress(event) => {
2377
                let is_bar_click = self
2378
                    .bars
2379
                    .iter()
2380
                    .enumerate()
2381
                    .find(|(_, bar)| bar.window() == event.event);
2382
2383
                if let Some((monitor_index, bar)) = is_bar_click {
2384
                    if let Some(tag_index) = bar.handle_click(event.event_x) {
2385
                        if monitor_index != self.selected_monitor {
2386
                            self.selected_monitor = monitor_index;
2387
                        }
2388
                        self.view_tag(tag_index)?;
2389
                    }
2390
                } else {
2391
                    let is_tab_bar_click = self
2392
                        .tab_bars
2393
                        .iter()
2394
                        .enumerate()
2395
                        .find(|(_, tab_bar)| tab_bar.window() == event.event);
2396
2397
                    if let Some((monitor_index, tab_bar)) = is_tab_bar_click {
2398
                        if monitor_index != self.selected_monitor {
2399
                            self.selected_monitor = monitor_index;
2400
                        }
2401
2402
                        let visible_windows: Vec<Window> = self
2403
                            .windows
2404
                            .iter()
2405
                            .filter(|&&window| {
2406
                                let window_monitor_index = self.window_monitor.get(&window).copied().unwrap_or(0);
2407
                                if window_monitor_index != monitor_index
2408
                                    || self.floating_windows.contains(&window)
2409
                                    || self.fullscreen_windows.contains(&window)
2410
                                {
2411
                                    return false;
2412
                                }
2413
                                let monitor_tags = self.monitors.get(monitor_index).map(|m| m.selected_tags).unwrap_or(0);
2414
                                if let Some(&tags) = self.window_tags.get(&window) {
2415
                                    (tags & monitor_tags) != 0
2416
                                } else {
2417
                                    false
2418
                                }
2419
                            })
2420
                            .copied()
2421
                            .collect();
2422
2423
                        if let Some(clicked_window) = tab_bar.get_clicked_window(&visible_windows, event.event_x) {
2424
                            self.connection.configure_window(
2425
                                clicked_window,
2426
                                &ConfigureWindowAux::new().stack_mode(StackMode::ABOVE),
2427
                            )?;
2428
                            self.set_focus(clicked_window)?;
2429
                            self.update_tab_bars()?;
2430
                        }
2431
                    } else if event.child != x11rb::NONE {
2432
                        self.set_focus(event.child)?;
2433
2434
                        if event.detail == ButtonIndex::M1.into() {
2435
                            self.move_mouse(event.child)?;
2436
                        } else if event.detail == ButtonIndex::M3.into() {
2437
                            self.resize_mouse(event.child)?;
2438
                        }
2439
                    }
2440
                }
2441
            }
2442
            Event::Expose(event) => {
2443
                for bar in &mut self.bars {
2444
                    if event.window == bar.window() {
2445
                        bar.invalidate();
2446
                        self.update_bar()?;
2447
                        break;
2448
                    }
2449
                }
2450
                for _tab_bar in &self.tab_bars {
2451
                    if event.window == _tab_bar.window() {
2452
                        self.update_tab_bars()?;
2453
                        break;
2454
                    }
2455
                }
2456
            }
2457
            Event::ConfigureRequest(event) => {
2458
                if self.windows.contains(&event.window) {
2459
                    let monitor_index = *self.window_monitor.get(&event.window).unwrap_or(&self.selected_monitor);
2460
                    let monitor = &self.monitors[monitor_index];
2461
                    let is_floating = self.floating_windows.contains(&event.window);
2462
                    let is_tiling_layout = self.layout.name() != "normie";
2463
2464
                    if is_floating || !is_tiling_layout {
2465
                        let cached_geom = self.window_geometry_cache.get(&event.window);
2466
                        let border_width = self.config.border_width as u16;
2467
2468
                        let mut config = ConfigureWindowAux::new();
2469
                        let value_mask = event.value_mask;
2470
2471
                        if value_mask.contains(ConfigWindow::BORDER_WIDTH) {
2472
                            config = config.border_width(event.border_width as u32);
2473
                        }
2474
2475
                        if value_mask.contains(ConfigWindow::X) {
2476
                            let mut x = event.x as i32;
2477
                            x = x.max(monitor.x);
2478
                            if x + event.width as i32 + 2 * border_width as i32 > monitor.x + monitor.width as i32 {
2479
                                x = monitor.x + monitor.width as i32 - event.width as i32 - 2 * border_width as i32;
2480
                            }
2481
                            config = config.x(x);
2482
                        }
2483
2484
                        if value_mask.contains(ConfigWindow::Y) {
2485
                            let mut y = event.y as i32;
2486
                            y = y.max(monitor.y);
2487
                            if y + event.height as i32 + 2 * border_width as i32 > monitor.y + monitor.height as i32 {
2488
                                y = monitor.y + monitor.height as i32 - event.height as i32 - 2 * border_width as i32;
2489
                            }
2490
                            config = config.y(y);
2491
                        }
2492
2493
                        if value_mask.contains(ConfigWindow::WIDTH) {
2494
                            config = config.width(event.width as u32);
2495
                        }
2496
2497
                        if value_mask.contains(ConfigWindow::HEIGHT) {
2498
                            config = config.height(event.height as u32);
2499
                        }
2500
2501
                        if value_mask.contains(ConfigWindow::SIBLING) {
2502
                            config = config.sibling(event.sibling);
2503
                        }
2504
2505
                        if value_mask.contains(ConfigWindow::STACK_MODE) {
2506
                            config = config.stack_mode(event.stack_mode);
2507
                        }
2508
2509
                        self.connection.configure_window(event.window, &config)?;
2510
2511
                        let final_x = if value_mask.contains(ConfigWindow::X) {
2512
                            let mut x = event.x as i32;
2513
                            x = x.max(monitor.x);
2514
                            if x + event.width as i32 + 2 * border_width as i32 > monitor.x + monitor.width as i32 {
2515
                                x = monitor.x + monitor.width as i32 - event.width as i32 - 2 * border_width as i32;
2516
                            }
2517
                            x as i16
2518
                        } else {
2519
                            cached_geom.map(|g| g.x_position).unwrap_or(0)
2520
                        };
2521
2522
                        let final_y = if value_mask.contains(ConfigWindow::Y) {
2523
                            let mut y = event.y as i32;
2524
                            y = y.max(monitor.y);
2525
                            if y + event.height as i32 + 2 * border_width as i32 > monitor.y + monitor.height as i32 {
2526
                                y = monitor.y + monitor.height as i32 - event.height as i32 - 2 * border_width as i32;
2527
                            }
2528
                            y as i16
2529
                        } else {
2530
                            cached_geom.map(|g| g.y_position).unwrap_or(0)
2531
                        };
2532
2533
                        self.update_geometry_cache(event.window, CachedGeometry {
2534
                            x_position: final_x,
2535
                            y_position: final_y,
2536
                            width: if value_mask.contains(ConfigWindow::WIDTH) { event.width } else { cached_geom.map(|g| g.width).unwrap_or(1) },
2537
                            height: if value_mask.contains(ConfigWindow::HEIGHT) { event.height } else { cached_geom.map(|g| g.height).unwrap_or(1) },
2538
                            border_width: if value_mask.contains(ConfigWindow::BORDER_WIDTH) { event.border_width } else { border_width },
2539
                        });
2540
                    } else {
2541
                        self.send_configure_notify(event.window)?;
2542
                    }
2543
                } else {
2544
                    let mut config = ConfigureWindowAux::new()
2545
                        .x(event.x as i32)
2546
                        .y(event.y as i32)
2547
                        .width(event.width as u32)
2548
                        .height(event.height as u32)
2549
                        .border_width(event.border_width as u32);
2550
2551
                    if event.value_mask.contains(ConfigWindow::SIBLING) {
2552
                        config = config.sibling(event.sibling);
2553
                    }
2554
2555
                    if event.value_mask.contains(ConfigWindow::STACK_MODE) {
2556
                        config = config.stack_mode(event.stack_mode);
2557
                    }
2558
2559
                    self.connection.configure_window(event.window, &config)?;
2560
                }
2561
            }
2562
            Event::ClientMessage(event) => {
2563
                if event.type_ == self.atoms.net_wm_state {
2564
                    if let Some(data) = event.data.as_data32().get(1) {
2565
                        if *data == self.atoms.net_wm_state_fullscreen {
2566
                            let action = event.data.as_data32()[0];
2567
                            let fullscreen = match action {
2568
                                1 => true,
2569
                                0 => false,
2570
                                2 => !self.fullscreen_windows.contains(&event.window),
2571
                                _ => return Ok(None),
2572
                            };
2573
                            self.set_window_fullscreen(event.window, fullscreen)?;
2574
                        }
2575
                    }
2576
                }
2577
            }
2578
            _ => {}
2579
        }
2580
        Ok(None)
2581
    }
2582
2583
    fn apply_layout(&mut self) -> WmResult<()> {
2584
        if self.layout.name() == LayoutType::Normie.as_str() {
2585
            return Ok(());
2586
        }
2587
2588
        let monitor_count = self.monitors.len();
2589
        for monitor_index in 0..monitor_count {
2590
            let monitor = &self.monitors[monitor_index];
2591
            let border_width = self.config.border_width;
2592
2593
            let gaps = if self.gaps_enabled {
2594
                GapConfig {
2595
                    inner_horizontal: self.config.gap_inner_horizontal,
2596
                    inner_vertical: self.config.gap_inner_vertical,
2597
                    outer_horizontal: self.config.gap_outer_horizontal,
2598
                    outer_vertical: self.config.gap_outer_vertical,
2599
                }
2600
            } else {
2601
                GapConfig {
2602
                    inner_horizontal: 0,
2603
                    inner_vertical: 0,
2604
                    outer_horizontal: 0,
2605
                    outer_vertical: 0,
2606
                }
2607
            };
2608
2609
            let monitor_selected_tags = monitor.selected_tags;
2610
            let monitor_x = monitor.x;
2611
            let monitor_y = monitor.y;
2612
            let monitor_width = monitor.width;
2613
            let monitor_height = monitor.height;
2614
2615
            let visible: Vec<Window> = self
2616
                .windows
2617
                .iter()
2618
                .filter(|&&window| {
2619
                    let window_monitor_index = self.window_monitor.get(&window).copied().unwrap_or(0);
2620
                    if window_monitor_index != monitor_index
2621
                        || self.floating_windows.contains(&window)
2622
                        || self.fullscreen_windows.contains(&window)
2623
                    {
2624
                        return false;
2625
                    }
2626
                    if let Some(&tags) = self.window_tags.get(&window) {
2627
                        (tags & monitor_selected_tags) != 0
2628
                    } else {
2629
                        false
2630
                    }
2631
                })
2632
                .copied()
2633
                .collect();
2634
2635
            let bar_height = if self.show_bar {
2636
                self.bars
2637
                    .get(monitor_index)
2638
                    .map(|bar| bar.height() as u32)
2639
                    .unwrap_or(0)
2640
            } else {
2641
                0
2642
            };
2643
            let usable_height = monitor_height.saturating_sub(bar_height);
2644
2645
            let geometries = self
2646
                .layout
2647
                .arrange(&visible, monitor_width, usable_height, &gaps);
2648
2649
            for (window, geometry) in visible.iter().zip(geometries.iter()) {
2650
                let adjusted_width = geometry.width.saturating_sub(2 * border_width);
2651
                let adjusted_height = geometry.height.saturating_sub(2 * border_width);
2652
2653
                let adjusted_x = geometry.x_coordinate + monitor_x;
2654
                let adjusted_y = geometry.y_coordinate + monitor_y + bar_height as i32;
2655
2656
                self.connection.configure_window(
2657
                    *window,
2658
                    &ConfigureWindowAux::new()
2659
                        .x(adjusted_x)
2660
                        .y(adjusted_y)
2661
                        .width(adjusted_width)
2662
                        .height(adjusted_height)
2663
                        .border_width(border_width),
2664
                )?;
2665
2666
                self.update_geometry_cache(*window, CachedGeometry {
2667
                    x_position: adjusted_x as i16,
2668
                    y_position: adjusted_y as i16,
2669
                    width: adjusted_width as u16,
2670
                    height: adjusted_height as u16,
2671
                    border_width: border_width as u16,
2672
                });
2673
            }
2674
        }
2675
2676
        self.connection.flush()?;
2677
2678
        let is_tabbed = self.layout.name() == LayoutType::Tabbed.as_str();
2679
2680
        if is_tabbed {
2681
            let outer_horizontal = if self.gaps_enabled {
2682
                self.config.gap_outer_horizontal
2683
            } else {
2684
                0
2685
            };
2686
            let outer_vertical = if self.gaps_enabled {
2687
                self.config.gap_outer_vertical
2688
            } else {
2689
                0
2690
            };
2691
2692
            for monitor_index in 0..self.tab_bars.len() {
2693
                if let Some(monitor) = self.monitors.get(monitor_index) {
2694
                    let bar_height = if self.show_bar {
2695
                        self.bars
2696
                            .get(monitor_index)
2697
                            .map(|bar| bar.height() as f32)
2698
                            .unwrap_or(0.0)
2699
                    } else {
2700
                        0.0
2701
                    };
2702
2703
                    let tab_bar_x = (monitor.x + outer_horizontal as i32) as i16;
2704
                    let tab_bar_y = (monitor.y as f32 + bar_height + outer_vertical as f32) as i16;
2705
                    let tab_bar_width = monitor.width.saturating_sub(2 * outer_horizontal) as u16;
2706
2707
                    if let Err(e) = self.tab_bars[monitor_index].reposition(
2708
                        &self.connection,
2709
                        tab_bar_x,
2710
                        tab_bar_y,
2711
                        tab_bar_width,
2712
                    ) {
2713
                        eprintln!("Failed to reposition tab bar: {:?}", e);
2714
                    }
2715
                }
2716
            }
2717
        }
2718
2719
        for monitor_index in 0..self.tab_bars.len() {
2720
            let has_visible_windows = self
2721
                .windows
2722
                .iter()
2723
                .any(|&window| {
2724
                    let window_monitor_index = self.window_monitor.get(&window).copied().unwrap_or(0);
2725
                    if window_monitor_index != monitor_index
2726
                        || self.floating_windows.contains(&window)
2727
                        || self.fullscreen_windows.contains(&window)
2728
                    {
2729
                        return false;
2730
                    }
2731
                    if let Some(monitor) = self.monitors.get(monitor_index) {
2732
                        if let Some(&tags) = self.window_tags.get(&window) {
2733
                            return (tags & monitor.selected_tags) != 0;
2734
                        }
2735
                    }
2736
                    false
2737
                });
2738
2739
            if is_tabbed && has_visible_windows {
2740
                if let Err(e) = self.tab_bars[monitor_index].show(&self.connection) {
2741
                    eprintln!("Failed to show tab bar: {:?}", e);
2742
                }
2743
            } else {
2744
                if let Err(e) = self.tab_bars[monitor_index].hide(&self.connection) {
2745
                    eprintln!("Failed to hide tab bar: {:?}", e);
2746
                }
2747
            }
2748
        }
2749
2750
        if is_tabbed {
2751
            self.update_tab_bars()?;
2752
        }
2753
2754
        Ok(())
2755
    }
2756
2757
    pub fn change_layout<L: Layout + 'static>(&mut self, new_layout: L) -> WmResult<()> {
2758
        self.layout = Box::new(new_layout);
2759
        self.apply_layout()?;
2760
        Ok(())
2761
    }
2762
2763
    fn update_geometry_cache(&mut self, window: Window, geometry: CachedGeometry) {
2764
        self.window_geometry_cache.insert(window, geometry);
2765
    }
2766
2767
    fn get_cached_geometry(&self, window: Window) -> Option<CachedGeometry> {
2768
        self.window_geometry_cache.get(&window).copied()
2769
    }
2770
2771
    fn get_or_query_geometry(&mut self, window: Window) -> WmResult<CachedGeometry> {
2772
        if let Some(cached) = self.get_cached_geometry(window) {
2773
            return Ok(cached);
2774
        }
2775
2776
        let geometry = self.connection.get_geometry(window)?.reply()?;
2777
        let cached = CachedGeometry {
2778
            x_position: geometry.x,
2779
            y_position: geometry.y,
2780
            width: geometry.width,
2781
            height: geometry.height,
2782
            border_width: geometry.border_width as u16,
2783
        };
2784
        self.update_geometry_cache(window, cached);
2785
        Ok(cached)
2786
    }
2787
2788
    fn send_configure_notify(&mut self, window: Window) -> WmResult<()> {
2789
        let geometry = self.get_or_query_geometry(window)?;
2790
2791
        let event = x11rb::protocol::xproto::ConfigureNotifyEvent {
2792
            response_type: x11rb::protocol::xproto::CONFIGURE_NOTIFY_EVENT,
2793
            sequence: 0,
2794
            event: window,
2795
            window,
2796
            above_sibling: x11rb::NONE,
2797
            x: geometry.x_position,
2798
            y: geometry.y_position,
2799
            width: geometry.width,
2800
            height: geometry.height,
2801
            border_width: geometry.border_width,
2802
            override_redirect: false,
2803
        };
2804
2805
        self.connection.send_event(
2806
            false,
2807
            window,
2808
            x11rb::protocol::xproto::EventMask::STRUCTURE_NOTIFY,
2809
            event,
2810
        )?;
2811
2812
        Ok(())
2813
    }
2814
2815
    fn remove_window(&mut self, window: Window) -> WmResult<()> {
2816
        let initial_count = self.windows.len();
2817
2818
        self.windows.retain(|&w| w != window);
2819
        self.window_tags.remove(&window);
2820
        self.window_monitor.remove(&window);
2821
        self.window_geometry_cache.remove(&window);
2822
        self.floating_windows.remove(&window);
2823
2824
        if self.windows.len() < initial_count {
2825
            let focused = self
2826
                .monitors
2827
                .get(self.selected_monitor)
2828
                .and_then(|m| m.focused_window);
2829
            if focused == Some(window) {
2830
                let visible = self.visible_windows_on_monitor(self.selected_monitor);
2831
                if let Some(&new_win) = visible.last() {
2832
                    self.set_focus(new_win)?;
2833
                } else if let Some(monitor) = self.monitors.get_mut(self.selected_monitor) {
2834
                    monitor.focused_window = None;
2835
                }
2836
            }
2837
2838
            self.apply_layout()?;
2839
            self.update_bar()?;
2840
        }
2841
        Ok(())
2842
    }
2843
2844
    fn run_autostart_commands(&self) -> Result<(), WmError> {
2845
        for command in &self.config.autostart {
2846
            Command::new("sh")
2847
                .arg("-c")
2848
                .arg(command)
2849
                .spawn()
2850
                .map_err(|e| WmError::Autostart(command.clone(), e))?;
2851
            eprintln!("[autostart] Spawned: {}", command);
2852
        }
2853
        Ok(())
2854
    }
2855
}