oxwm

https://git.tonybtw.com/oxwm.git git://git.tonybtw.com/oxwm.git
81,331 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
pub type TagMask = u32;
20
pub fn tag_mask(tag: usize) -> TagMask {
21
    1 << tag
22
}
23
24
struct AtomCache {
25
    net_current_desktop: Atom,
26
    net_client_info: Atom,
27
    wm_state: Atom,
28
    wm_protocols: Atom,
29
    wm_delete_window: Atom,
30
    net_wm_state: Atom,
31
    net_wm_state_fullscreen: Atom,
32
}
33
34
impl AtomCache {
35
    fn new(connection: &RustConnection) -> WmResult<Self> {
36
        let net_current_desktop = connection
37
            .intern_atom(false, b"_NET_CURRENT_DESKTOP")?
38
            .reply()?
39
            .atom;
40
41
        let net_client_info = connection
42
            .intern_atom(false, b"_NET_CLIENT_INFO")?
43
            .reply()?
44
            .atom;
45
46
        let wm_state = connection.intern_atom(false, b"WM_STATE")?.reply()?.atom;
47
48
        let wm_protocols = connection
49
            .intern_atom(false, b"WM_PROTOCOLS")?
50
            .reply()?
51
            .atom;
52
53
        let wm_delete_window = connection
54
            .intern_atom(false, b"WM_DELETE_WINDOW")?
55
            .reply()?
56
            .atom;
57
58
        let net_wm_state = connection
59
            .intern_atom(false, b"_NET_WM_STATE")?
60
            .reply()?
61
            .atom;
62
63
        let net_wm_state_fullscreen = connection
64
            .intern_atom(false, b"_NET_WM_STATE_FULLSCREEN")?
65
            .reply()?
66
            .atom;
67
68
        Ok(Self {
69
            net_current_desktop,
70
            net_client_info,
71
            wm_state,
72
            wm_protocols,
73
            wm_delete_window,
74
            net_wm_state,
75
            net_wm_state_fullscreen,
76
        })
77
    }
78
}
79
80
pub struct WindowManager {
81
    config: Config,
82
    connection: RustConnection,
83
    screen_number: usize,
84
    root: Window,
85
    screen: Screen,
86
    windows: Vec<Window>,
87
    layout: LayoutBox,
88
    window_tags: std::collections::HashMap<Window, TagMask>,
89
    window_monitor: std::collections::HashMap<Window, usize>,
90
    gaps_enabled: bool,
91
    floating_windows: HashSet<Window>,
92
    fullscreen_windows: HashSet<Window>,
93
    floating_geometry_before_fullscreen: std::collections::HashMap<Window, (i16, i16, u16, u16, u16)>,
94
    bars: Vec<Bar>,
95
    show_bar: bool,
96
    last_layout: Option<&'static str>,
97
    monitors: Vec<Monitor>,
98
    selected_monitor: usize,
99
    atoms: AtomCache,
100
    previous_focused: Option<Window>,
101
    display: *mut x11::xlib::Display,
102
    font: crate::bar::font::Font,
103
    keychord_state: keyboard::handlers::KeychordState,
104
    error_message: Option<String>,
105
    overlay: ErrorOverlay,
106
    keybind_overlay: KeybindOverlay,
107
}
108
109
type WmResult<T> = Result<T, WmError>;
110
111
impl WindowManager {
112
    pub fn new(config: Config) -> WmResult<Self> {
113
        let (connection, screen_number) = x11rb::connect(None)?;
114
        let root = connection.setup().roots[screen_number].root;
115
        let screen = connection.setup().roots[screen_number].clone();
116
117
        let normal_cursor = CursorHandle::new(
118
            &connection,
119
            screen_number,
120
            &x11rb::resource_manager::new_from_default(&connection)?,
121
        )?
122
        .reply()?
123
        .load_cursor(&connection, "left_ptr")?;
124
125
        connection
126
            .change_window_attributes(
127
                root,
128
                &ChangeWindowAttributesAux::new()
129
                    .cursor(normal_cursor)
130
                    .event_mask(
131
                        EventMask::SUBSTRUCTURE_REDIRECT
132
                            | EventMask::SUBSTRUCTURE_NOTIFY
133
                            | EventMask::PROPERTY_CHANGE
134
                            | EventMask::KEY_PRESS
135
                            | EventMask::BUTTON_PRESS
136
                            | EventMask::POINTER_MOTION,
137
                    ),
138
            )?
139
            .check()?;
140
141
        connection.grab_button(
142
            false,
143
            root,
144
            EventMask::BUTTON_PRESS | EventMask::BUTTON_RELEASE,
145
            GrabMode::SYNC,
146
            GrabMode::ASYNC,
147
            x11rb::NONE,
148
            x11rb::NONE,
149
            ButtonIndex::M1,
150
            u16::from(config.modkey).into(),
151
        )?;
152
153
        connection.grab_button(
154
            false,
155
            root,
156
            EventMask::BUTTON_PRESS | EventMask::BUTTON_RELEASE,
157
            GrabMode::SYNC,
158
            GrabMode::ASYNC,
159
            x11rb::NONE,
160
            x11rb::NONE,
161
            ButtonIndex::M3,
162
            u16::from(config.modkey).into(),
163
        )?;
164
165
        let monitors = detect_monitors(&connection, &screen, root)?;
166
167
        let display = unsafe { x11::xlib::XOpenDisplay(std::ptr::null()) };
168
        if display.is_null() {
169
            return Err(WmError::X11(crate::errors::X11Error::DisplayOpenFailed));
170
        }
171
172
        let font = crate::bar::font::Font::new(display, screen_number as i32, &config.font)?;
173
174
        let mut bars = Vec::new();
175
        for monitor in monitors.iter() {
176
            let bar = Bar::new(
177
                &connection,
178
                &screen,
179
                screen_number,
180
                &config,
181
                display,
182
                &font,
183
                monitor.x as i16,
184
                monitor.y as i16,
185
                monitor.width as u16,
186
            )?;
187
            bars.push(bar);
188
        }
189
190
        let gaps_enabled = config.gaps_enabled;
191
192
        let atoms = AtomCache::new(&connection)?;
193
194
        let overlay = ErrorOverlay::new(
195
            &connection,
196
            &screen,
197
            screen_number,
198
            display,
199
            &font,
200
            screen.width_in_pixels,
201
        )?;
202
203
        let keybind_overlay =
204
            KeybindOverlay::new(&connection, &screen, screen_number, display, config.modkey)?;
205
206
        let mut window_manager = Self {
207
            config,
208
            connection,
209
            screen_number,
210
            root,
211
            screen,
212
            windows: Vec::new(),
213
            layout: Box::new(TilingLayout),
214
            window_tags: std::collections::HashMap::new(),
215
            window_monitor: std::collections::HashMap::new(),
216
            gaps_enabled,
217
            floating_windows: HashSet::new(),
218
            fullscreen_windows: HashSet::new(),
219
            floating_geometry_before_fullscreen: std::collections::HashMap::new(),
220
            bars,
221
            show_bar: true,
222
            last_layout: None,
223
            monitors,
224
            selected_monitor: 0,
225
            atoms,
226
            previous_focused: None,
227
            display,
228
            font,
229
            keychord_state: keyboard::handlers::KeychordState::Idle,
230
            error_message: None,
231
            overlay,
232
            keybind_overlay,
233
        };
234
235
        window_manager.scan_existing_windows()?;
236
        window_manager.update_bar()?;
237
        window_manager.run_autostart_commands()?;
238
239
        Ok(window_manager)
240
    }
241
242
    /**
243
     *
244
     * This function is deprecated for now, but will potentially be used in the future.
245
     *
246
     */
247
    fn _get_saved_selected_tags(
248
        connection: &RustConnection,
249
        root: Window,
250
        tag_count: usize,
251
    ) -> WmResult<TagMask> {
252
        let net_current_desktop = connection
253
            .intern_atom(false, b"_NET_CURRENT_DESKTOP")?
254
            .reply()?
255
            .atom;
256
257
        match connection
258
            .get_property(false, root, net_current_desktop, AtomEnum::CARDINAL, 0, 1)?
259
            .reply()
260
        {
261
            Ok(prop) if prop.value.len() >= 4 => {
262
                let desktop = u32::from_ne_bytes([
263
                    prop.value[0],
264
                    prop.value[1],
265
                    prop.value[2],
266
                    prop.value[3],
267
                ]);
268
                if desktop < tag_count as u32 {
269
                    let mask = tag_mask(desktop as usize);
270
                    return Ok(mask);
271
                }
272
            }
273
            Ok(_) => {}
274
            Err(e) => {
275
                eprintln!("No _NET_CURRENT_DESKTOP property ({})", e);
276
            }
277
        }
278
        Ok(tag_mask(0))
279
    }
280
281
    pub fn show_migration_overlay(&mut self) {
282
        let message = "Your config.lua uses legacy syntax or has errors.\n\n\
283
                       You are now running with default configuration.\n\n\
284
                       Press Mod+Shift+/ to see default keybinds\n\
285
                       Press Mod+Shift+R to reload after fixing your config";
286
287
        let screen_width = self.screen.width_in_pixels;
288
        let screen_height = self.screen.height_in_pixels;
289
290
        if let Err(e) = self.overlay.show_error(
291
            &self.connection,
292
            &self.font,
293
            message,
294
            screen_width,
295
            screen_height,
296
        ) {
297
            eprintln!("Failed to show migration overlay: {:?}", e);
298
        }
299
    }
300
301
    fn try_reload_config(&mut self) -> Result<(), String> {
302
        let config_dir = if let Some(xdg_config) = std::env::var_os("XDG_CONFIG_HOME") {
303
            std::path::PathBuf::from(xdg_config).join("oxwm")
304
        } else if let Some(home) = std::env::var_os("HOME") {
305
            std::path::PathBuf::from(home).join(".config").join("oxwm")
306
        } else {
307
            return Err("Could not find config directory".to_string());
308
        };
309
310
        let lua_path = config_dir.join("config.lua");
311
312
        if !lua_path.exists() {
313
            return Err("No config.lua file found".to_string());
314
        }
315
316
        let config_str = std::fs::read_to_string(&lua_path)
317
            .map_err(|e| format!("Failed to read config: {}", e))?;
318
319
        let new_config = crate::config::parse_lua_config(&config_str, Some(&config_dir))
320
            .map_err(|e| format!("{}", e))?;
321
322
        self.config = new_config;
323
        self.error_message = None;
324
325
        for bar in &mut self.bars {
326
            bar.update_from_config(&self.config);
327
        }
328
329
        Ok(())
330
    }
331
332
    fn scan_existing_windows(&mut self) -> WmResult<()> {
333
        let tree = self.connection.query_tree(self.root)?.reply()?;
334
        let net_client_info = self.atoms.net_client_info;
335
        let wm_state_atom = self.atoms.wm_state;
336
337
        for &window in &tree.children {
338
            if self.bars.iter().any(|bar| bar.window() == window) {
339
                continue;
340
            }
341
342
            let Ok(attrs) = self.connection.get_window_attributes(window)?.reply() else {
343
                continue;
344
            };
345
346
            if attrs.override_redirect {
347
                continue;
348
            }
349
350
            if attrs.map_state == MapState::VIEWABLE {
351
                let tag = self.get_saved_tag(window, net_client_info)?;
352
                self.windows.push(window);
353
                self.window_tags.insert(window, tag);
354
                self.window_monitor.insert(window, self.selected_monitor);
355
                continue;
356
            }
357
358
            if attrs.map_state == MapState::UNMAPPED {
359
                let has_wm_state = self
360
                    .connection
361
                    .get_property(false, window, wm_state_atom, AtomEnum::ANY, 0, 2)?
362
                    .reply()
363
                    .is_ok_and(|prop| !prop.value.is_empty());
364
365
                if !has_wm_state {
366
                    continue;
367
                }
368
369
                let has_wm_class = self
370
                    .connection
371
                    .get_property(false, window, AtomEnum::WM_CLASS, AtomEnum::STRING, 0, 1024)?
372
                    .reply()
373
                    .is_ok_and(|prop| !prop.value.is_empty());
374
375
                if has_wm_class {
376
                    let tag = self.get_saved_tag(window, net_client_info)?;
377
                    self.connection.map_window(window)?;
378
                    self.windows.push(window);
379
                    self.window_tags.insert(window, tag);
380
                    self.window_monitor.insert(window, self.selected_monitor);
381
                }
382
            }
383
        }
384
385
        if let Some(&first) = self.windows.first() {
386
            self.set_focus(first)?;
387
        }
388
389
        self.apply_layout()?;
390
        Ok(())
391
    }
392
393
    fn get_saved_tag(&self, window: Window, net_client_info: Atom) -> WmResult<TagMask> {
394
        match self
395
            .connection
396
            .get_property(false, window, net_client_info, AtomEnum::CARDINAL, 0, 2)?
397
            .reply()
398
        {
399
            Ok(prop) if prop.value.len() >= 4 => {
400
                let tags = u32::from_ne_bytes([
401
                    prop.value[0],
402
                    prop.value[1],
403
                    prop.value[2],
404
                    prop.value[3],
405
                ]);
406
407
                if tags != 0 && tags < (1 << self.config.tags.len()) {
408
                    return Ok(tags);
409
                }
410
            }
411
            Ok(_) => {}
412
            Err(e) => {
413
                eprintln!("No _NET_CLIENT_INFO property ({})", e);
414
            }
415
        }
416
417
        Ok(self
418
            .monitors
419
            .get(self.selected_monitor)
420
            .map(|m| m.selected_tags)
421
            .unwrap_or(tag_mask(0)))
422
    }
423
424
    fn save_client_tag(&self, window: Window, tag: TagMask) -> WmResult<()> {
425
        let net_client_info = self.atoms.net_client_info;
426
427
        let bytes = tag.to_ne_bytes().to_vec();
428
429
        self.connection.change_property(
430
            PropMode::REPLACE,
431
            window,
432
            net_client_info,
433
            AtomEnum::CARDINAL,
434
            32,
435
            1,
436
            &bytes,
437
        )?;
438
439
        self.connection.flush()?;
440
        Ok(())
441
    }
442
443
    fn set_wm_state(&self, window: Window, state: u32) -> WmResult<()> {
444
        let wm_state_atom = self.atoms.wm_state;
445
446
        let data = [state, 0u32];
447
        let bytes: Vec<u8> = data.iter().flat_map(|&v| v.to_ne_bytes()).collect();
448
449
        self.connection.change_property(
450
            PropMode::REPLACE,
451
            window,
452
            wm_state_atom,
453
            wm_state_atom,
454
            32,
455
            2,
456
            &bytes,
457
        )?;
458
459
        self.connection.flush()?;
460
        Ok(())
461
    }
462
463
    pub fn run(&mut self) -> WmResult<bool> {
464
        println!("oxwm started on display {}", self.screen_number);
465
466
        keyboard::setup_keybinds(&self.connection, self.root, &self.config.keybindings)?;
467
        self.update_bar()?;
468
469
        loop {
470
            while let Some(event) = self.connection.poll_for_event()? {
471
                if let Some(should_restart) = self.handle_event(event)? {
472
                    return Ok(should_restart);
473
                }
474
            }
475
476
            if let Some(bar) = self.bars.get_mut(self.selected_monitor) {
477
                bar.update_blocks();
478
            }
479
480
            if self.bars.iter().any(|bar| bar.needs_redraw()) {
481
                self.update_bar()?;
482
            }
483
484
            std::thread::sleep(std::time::Duration::from_millis(100));
485
        }
486
    }
487
488
    fn toggle_floating(&mut self) -> WmResult<()> {
489
        if let Some(focused) = self
490
            .monitors
491
            .get(self.selected_monitor)
492
            .and_then(|m| m.focused_window)
493
        {
494
            if self.floating_windows.contains(&focused) {
495
                self.floating_windows.remove(&focused);
496
497
                let selected_tags = self
498
                    .monitors
499
                    .get(self.selected_monitor)
500
                    .map(|m| m.selected_tags)
501
                    .unwrap_or(tag_mask(0));
502
503
                self.window_tags.insert(focused, selected_tags);
504
                self.window_monitor.insert(focused, self.selected_monitor);
505
                let _ = self.save_client_tag(focused, selected_tags);
506
507
                self.apply_layout()?;
508
            } else {
509
                let float_width = (self.screen.width_in_pixels / 2) as u32;
510
                let float_height = (self.screen.height_in_pixels / 2) as u32;
511
512
                let border_width = self.config.border_width;
513
514
                let center_width = ((self.screen.width_in_pixels - float_width as u16) / 2) as i32;
515
                let center_height =
516
                    ((self.screen.height_in_pixels - float_height as u16) / 2) as i32;
517
518
                self.connection.configure_window(
519
                    focused,
520
                    &ConfigureWindowAux::new()
521
                        .x(center_width)
522
                        .y(center_height)
523
                        .width(float_width)
524
                        .height(float_height)
525
                        .border_width(border_width)
526
                        .stack_mode(StackMode::ABOVE),
527
                )?;
528
529
                self.floating_windows.insert(focused);
530
                self.apply_layout()?;
531
                self.connection.flush()?;
532
            }
533
        }
534
        Ok(())
535
    }
536
537
    fn smart_move_window(&mut self, direction: i32) -> WmResult<()> {
538
        let focused = match self
539
            .monitors
540
            .get(self.selected_monitor)
541
            .and_then(|m| m.focused_window)
542
        {
543
            Some(win) => win,
544
            None => return Ok(()),
545
        };
546
547
        if !self.floating_windows.contains(&focused) {
548
            let float_width = (self.screen.width_in_pixels / 2) as u32;
549
            let float_height = (self.screen.height_in_pixels / 2) as u32;
550
            let border_width = self.config.border_width;
551
            let center_width = ((self.screen.width_in_pixels - float_width as u16) / 2) as i32;
552
            let center_height = ((self.screen.height_in_pixels - float_height as u16) / 2) as i32;
553
554
            self.connection.configure_window(
555
                focused,
556
                &ConfigureWindowAux::new()
557
                    .x(center_width)
558
                    .y(center_height)
559
                    .width(float_width)
560
                    .height(float_height)
561
                    .border_width(border_width)
562
                    .stack_mode(StackMode::ABOVE),
563
            )?;
564
            self.floating_windows.insert(focused);
565
        }
566
567
        let current_geom = match self.connection.get_geometry(focused)?.reply() {
568
            Ok(geom) => geom,
569
            Err(_) => return Ok(()),
570
        };
571
572
        let c_x = current_geom.x as i32;
573
        let c_y = current_geom.y as i32;
574
        let c_width = current_geom.width as i32;
575
        let c_height = current_geom.height as i32;
576
577
        let monitor = match self.monitors.get(self.selected_monitor) {
578
            Some(m) => m,
579
            None => return Ok(()),
580
        };
581
582
        let (gap_ih, gap_iv, gap_oh, gap_ov) = if self.gaps_enabled {
583
            (
584
                self.config.gap_inner_horizontal as i32,
585
                self.config.gap_inner_vertical as i32,
586
                self.config.gap_outer_horizontal as i32,
587
                self.config.gap_outer_vertical as i32,
588
            )
589
        } else {
590
            (0, 0, 0, 0)
591
        };
592
593
        let (new_x, new_y) = match direction {
594
            0 => {
595
                // UP
596
                let mut target = i32::MIN;
597
                let top = c_y;
598
                let mut ny = c_y - (monitor.height as i32 / 4);
599
600
                for &other_window in &self.windows {
601
                    if other_window == focused {
602
                        continue;
603
                    }
604
                    if !self.floating_windows.contains(&other_window) {
605
                        continue;
606
                    }
607
                    if !self.is_window_visible(other_window) {
608
                        continue;
609
                    }
610
                    let other_mon = self.window_monitor.get(&other_window).copied().unwrap_or(0);
611
                    if other_mon != self.selected_monitor {
612
                        continue;
613
                    }
614
615
                    let other_geom = match self.connection.get_geometry(other_window)?.reply() {
616
                        Ok(geom) => geom,
617
                        Err(_) => continue,
618
                    };
619
620
                    let o_x = other_geom.x as i32;
621
                    let o_y = other_geom.y as i32;
622
                    let o_width = other_geom.width as i32;
623
                    let o_height = other_geom.height as i32;
624
625
                    if c_x + c_width < o_x || c_x > o_x + o_width {
626
                        continue;
627
                    }
628
629
                    let bottom = o_y + o_height + gap_iv;
630
                    if top > bottom && ny < bottom {
631
                        target = target.max(bottom);
632
                    }
633
                }
634
635
                if target != i32::MIN {
636
                    ny = target;
637
                }
638
                ny = ny.max(monitor.y as i32 + gap_ov);
639
                (c_x, ny)
640
            }
641
            1 => {
642
                // DOWN
643
                let mut target = i32::MAX;
644
                let bottom = c_y + c_height;
645
                let mut ny = c_y + (monitor.height as i32 / 4);
646
647
                for &other_window in &self.windows {
648
                    if other_window == focused {
649
                        continue;
650
                    }
651
                    if !self.floating_windows.contains(&other_window) {
652
                        continue;
653
                    }
654
                    if !self.is_window_visible(other_window) {
655
                        continue;
656
                    }
657
                    let other_mon = self.window_monitor.get(&other_window).copied().unwrap_or(0);
658
                    if other_mon != self.selected_monitor {
659
                        continue;
660
                    }
661
662
                    let other_geom = match self.connection.get_geometry(other_window)?.reply() {
663
                        Ok(geom) => geom,
664
                        Err(_) => continue,
665
                    };
666
667
                    let o_x = other_geom.x as i32;
668
                    let o_y = other_geom.y as i32;
669
                    let o_width = other_geom.width as i32;
670
671
                    if c_x + c_width < o_x || c_x > o_x + o_width {
672
                        continue;
673
                    }
674
675
                    let top = o_y - gap_iv;
676
                    if bottom < top && (ny + c_height) > top {
677
                        target = target.min(top - c_height);
678
                    }
679
                }
680
681
                if target != i32::MAX {
682
                    ny = target;
683
                }
684
                ny = ny.min(monitor.y as i32 + monitor.height as i32 - c_height - gap_ov);
685
                (c_x, ny)
686
            }
687
            2 => {
688
                // LEFT
689
                let mut target = i32::MIN;
690
                let left = c_x;
691
                let mut nx = c_x - (monitor.width as i32 / 6);
692
693
                for &other_window in &self.windows {
694
                    if other_window == focused {
695
                        continue;
696
                    }
697
                    if !self.floating_windows.contains(&other_window) {
698
                        continue;
699
                    }
700
                    if !self.is_window_visible(other_window) {
701
                        continue;
702
                    }
703
                    let other_mon = self.window_monitor.get(&other_window).copied().unwrap_or(0);
704
                    if other_mon != self.selected_monitor {
705
                        continue;
706
                    }
707
708
                    let other_geom = match self.connection.get_geometry(other_window)?.reply() {
709
                        Ok(geom) => geom,
710
                        Err(_) => continue,
711
                    };
712
713
                    let o_x = other_geom.x as i32;
714
                    let o_y = other_geom.y as i32;
715
                    let o_width = other_geom.width as i32;
716
                    let o_height = other_geom.height as i32;
717
718
                    if c_y + c_height < o_y || c_y > o_y + o_height {
719
                        continue;
720
                    }
721
722
                    let right = o_x + o_width + gap_ih;
723
                    if left > right && nx < right {
724
                        target = target.max(right);
725
                    }
726
                }
727
728
                if target != i32::MIN {
729
                    nx = target;
730
                }
731
                nx = nx.max(monitor.x as i32 + gap_oh);
732
                (nx, c_y)
733
            }
734
            3 => {
735
                // RIGHT
736
                let mut target = i32::MAX;
737
                let right = c_x + c_width;
738
                let mut nx = c_x + (monitor.width as i32 / 6);
739
740
                for &other_window in &self.windows {
741
                    if other_window == focused {
742
                        continue;
743
                    }
744
                    if !self.floating_windows.contains(&other_window) {
745
                        continue;
746
                    }
747
                    if !self.is_window_visible(other_window) {
748
                        continue;
749
                    }
750
                    let other_mon = self.window_monitor.get(&other_window).copied().unwrap_or(0);
751
                    if other_mon != self.selected_monitor {
752
                        continue;
753
                    }
754
755
                    let other_geom = match self.connection.get_geometry(other_window)?.reply() {
756
                        Ok(geom) => geom,
757
                        Err(_) => continue,
758
                    };
759
760
                    let o_x = other_geom.x as i32;
761
                    let o_y = other_geom.y as i32;
762
                    let o_height = other_geom.height as i32;
763
764
                    if c_y + c_height < o_y || c_y > o_y + o_height {
765
                        continue;
766
                    }
767
768
                    let left = o_x - gap_ih;
769
                    if right < left && (nx + c_width) > left {
770
                        target = target.min(left - c_width);
771
                    }
772
                }
773
774
                if target != i32::MAX {
775
                    nx = target;
776
                }
777
                nx = nx.min(monitor.x as i32 + monitor.width as i32 - c_width - gap_oh);
778
                (nx, c_y)
779
            }
780
            _ => return Ok(()),
781
        };
782
783
        self.connection.configure_window(
784
            focused,
785
            &ConfigureWindowAux::new()
786
                .x(new_x)
787
                .y(new_y)
788
                .stack_mode(StackMode::ABOVE),
789
        )?;
790
791
        self.connection.flush()?;
792
        Ok(())
793
    }
794
795
    fn exchange_client(&mut self, direction: i32) -> WmResult<()> {
796
        let focused = match self
797
            .monitors
798
            .get(self.selected_monitor)
799
            .and_then(|m| m.focused_window)
800
        {
801
            Some(win) => win,
802
            None => return Ok(()),
803
        };
804
805
        if self.floating_windows.contains(&focused) {
806
            return Ok(());
807
        }
808
809
        let visible = self.visible_windows();
810
        if visible.len() < 2 {
811
            return Ok(());
812
        }
813
814
        let current_idx = match visible.iter().position(|&w| w == focused) {
815
            Some(idx) => idx,
816
            None => return Ok(()),
817
        };
818
819
        let target_idx = match direction {
820
            0 | 2 => {
821
                // UP or LEFT - previous in stack
822
                if current_idx == 0 {
823
                    visible.len() - 1
824
                } else {
825
                    current_idx - 1
826
                }
827
            }
828
            1 | 3 => {
829
                // DOWN or RIGHT - next in stack
830
                (current_idx + 1) % visible.len()
831
            }
832
            _ => return Ok(()),
833
        };
834
835
        let target = visible[target_idx];
836
837
        let focused_pos = self.windows.iter().position(|&w| w == focused);
838
        let target_pos = self.windows.iter().position(|&w| w == target);
839
840
        if let (Some(f_pos), Some(t_pos)) = (focused_pos, target_pos) {
841
            self.windows.swap(f_pos, t_pos);
842
843
            self.apply_layout()?;
844
845
            self.set_focus(focused)?;
846
847
            if let Ok(geometry) = self.connection.get_geometry(focused)?.reply() {
848
                self.connection.warp_pointer(
849
                    x11rb::NONE,
850
                    focused,
851
                    0,
852
                    0,
853
                    0,
854
                    0,
855
                    geometry.width as i16 / 2,
856
                    geometry.height as i16 / 2,
857
                )?;
858
            }
859
        }
860
861
        Ok(())
862
    }
863
864
865
    fn get_layout_symbol(&self) -> String {
866
        let layout_name = self.layout.name();
867
        self.config
868
            .layout_symbols
869
            .iter()
870
            .find(|l| l.name == layout_name)
871
            .map(|l| l.symbol.clone())
872
            .unwrap_or_else(|| self.layout.symbol().to_string())
873
    }
874
875
    fn get_keychord_indicator(&self) -> Option<String> {
876
        match &self.keychord_state {
877
            keyboard::handlers::KeychordState::Idle => None,
878
            keyboard::handlers::KeychordState::InProgress {
879
                candidates,
880
                keys_pressed,
881
            } => {
882
                if candidates.is_empty() {
883
                    return None;
884
                }
885
886
                let binding = &self.config.keybindings[candidates[0]];
887
                let mut indicator = String::new();
888
889
                for (i, key_press) in binding.keys.iter().take(*keys_pressed).enumerate() {
890
                    if i > 0 {
891
                        indicator.push(' ');
892
                    }
893
894
                    for modifier in &key_press.modifiers {
895
                        indicator.push_str(Self::format_modifier(*modifier));
896
                        indicator.push('+');
897
                    }
898
899
                    indicator.push_str(&keyboard::keysyms::format_keysym(key_press.keysym));
900
                }
901
902
                indicator.push('-');
903
                Some(indicator)
904
            }
905
        }
906
    }
907
908
    fn format_modifier(modifier: KeyButMask) -> &'static str {
909
        match modifier {
910
            KeyButMask::MOD1 => "Alt",
911
            KeyButMask::MOD4 => "Super",
912
            KeyButMask::SHIFT => "Shift",
913
            KeyButMask::CONTROL => "Ctrl",
914
            _ => "Mod",
915
        }
916
    }
917
918
919
    fn update_bar(&mut self) -> WmResult<()> {
920
        let layout_symbol = self.get_layout_symbol();
921
        let keychord_indicator = self.get_keychord_indicator();
922
923
        for (monitor_index, monitor) in self.monitors.iter().enumerate() {
924
            if let Some(bar) = self.bars.get_mut(monitor_index) {
925
                let mut occupied_tags: TagMask = 0;
926
                for (&window, &tags) in &self.window_tags {
927
                    if self.window_monitor.get(&window).copied().unwrap_or(0) == monitor_index {
928
                        occupied_tags |= tags;
929
                    }
930
                }
931
932
                let draw_blocks = monitor_index == self.selected_monitor;
933
                bar.invalidate();
934
                bar.draw(
935
                    &self.connection,
936
                    &self.font,
937
                    self.display,
938
                    monitor.selected_tags,
939
                    occupied_tags,
940
                    draw_blocks,
941
                    &layout_symbol,
942
                    keychord_indicator.as_deref(),
943
                )?;
944
            }
945
        }
946
        Ok(())
947
    }
948
949
    fn handle_key_action(&mut self, action: KeyAction, arg: &Arg) -> WmResult<()> {
950
        match action {
951
            KeyAction::Spawn => handlers::handle_spawn_action(action, arg)?,
952
            KeyAction::SpawnTerminal => {
953
                use std::process::Command;
954
                let terminal = &self.config.terminal;
955
                let _ = Command::new(terminal).spawn();
956
            }
957
            KeyAction::KillClient => {
958
                if let Some(focused) = self
959
                    .monitors
960
                    .get(self.selected_monitor)
961
                    .and_then(|m| m.focused_window)
962
                {
963
                    self.kill_client(focused)?;
964
                }
965
            }
966
            KeyAction::ToggleFullScreen => {
967
                self.fullscreen()?;
968
            }
969
            KeyAction::ChangeLayout => {
970
                if let Arg::Str(layout_name) = arg {
971
                    match layout_from_str(layout_name) {
972
                        Ok(layout) => {
973
                            self.layout = layout;
974
                            if layout_name != "normie" && layout_name != "floating" {
975
                                self.floating_windows.clear();
976
                            }
977
                            self.apply_layout()?;
978
                            self.update_bar()?;
979
                        }
980
                        Err(e) => eprintln!("Failed to change layout: {}", e),
981
                    }
982
                }
983
            }
984
            KeyAction::CycleLayout => {
985
                let current_name = self.layout.name();
986
                let next_name = next_layout(current_name);
987
                match layout_from_str(next_name) {
988
                    Ok(layout) => {
989
                        self.layout = layout;
990
                        if next_name != "normie" && next_name != "floating" {
991
                            self.floating_windows.clear();
992
                        }
993
                        self.apply_layout()?;
994
                        self.update_bar()?;
995
                    }
996
                    Err(e) => eprintln!("Failed to cycle layout: {}", e),
997
                }
998
            }
999
            KeyAction::ToggleFloating => {
1000
                self.toggle_floating()?;
1001
            }
1002
1003
            KeyAction::SmartMoveWin => {
1004
                if let Arg::Int(direction) = arg {
1005
                    self.smart_move_window(*direction)?;
1006
                }
1007
            }
1008
1009
            KeyAction::ExchangeClient => {
1010
                if let Arg::Int(direction) = arg {
1011
                    self.exchange_client(*direction)?;
1012
                }
1013
            }
1014
1015
            KeyAction::FocusStack => {
1016
                if let Arg::Int(direction) = arg {
1017
                    self.cycle_focus(*direction)?;
1018
                }
1019
            }
1020
            KeyAction::FocusDirection => {
1021
                if let Arg::Int(direction) = arg {
1022
                    self.focus_direction(*direction)?;
1023
                }
1024
            }
1025
            KeyAction::SwapDirection => {
1026
                if let Arg::Int(direction) = arg {
1027
                    self.swap_direction(*direction)?;
1028
                }
1029
            }
1030
            KeyAction::Quit | KeyAction::Restart => {
1031
                // Handled in handle_event
1032
            }
1033
            KeyAction::Recompile => {
1034
                match std::process::Command::new("oxwm")
1035
                    .arg("--recompile")
1036
                    .spawn()
1037
                {
1038
                    Ok(_) => eprintln!("Recompiling in background"),
1039
                    Err(e) => eprintln!("Failed to spawn recompile: {}", e),
1040
                }
1041
            }
1042
            KeyAction::ViewTag => {
1043
                if let Arg::Int(tag_index) = arg {
1044
                    self.view_tag(*tag_index as usize)?;
1045
                }
1046
            }
1047
            KeyAction::MoveToTag => {
1048
                if let Arg::Int(tag_index) = arg {
1049
                    self.move_to_tag(*tag_index as usize)?;
1050
                }
1051
            }
1052
            KeyAction::ToggleGaps => {
1053
                self.gaps_enabled = !self.gaps_enabled;
1054
                self.apply_layout()?;
1055
            }
1056
            KeyAction::FocusMonitor => {
1057
                if let Arg::Int(direction) = arg {
1058
                    self.focus_monitor(*direction)?;
1059
                }
1060
            }
1061
            KeyAction::ShowKeybindOverlay => {
1062
                let monitor = &self.monitors[self.selected_monitor];
1063
                self.keybind_overlay.toggle(
1064
                    &self.connection,
1065
                    &self.font,
1066
                    &self.config.keybindings,
1067
                    monitor.width as u16,
1068
                    monitor.height as u16,
1069
                )?;
1070
            }
1071
            KeyAction::None => {}
1072
        }
1073
        Ok(())
1074
    }
1075
1076
    fn focus_monitor(&mut self, direction: i32) -> WmResult<()> {
1077
        if self.monitors.is_empty() {
1078
            return Ok(());
1079
        }
1080
1081
        let new_monitor = if direction > 0 {
1082
            (self.selected_monitor + 1) % self.monitors.len()
1083
        } else {
1084
            (self.selected_monitor + self.monitors.len() - 1) % self.monitors.len()
1085
        };
1086
1087
        if new_monitor == self.selected_monitor {
1088
            return Ok(());
1089
        }
1090
1091
        self.selected_monitor = new_monitor;
1092
1093
        self.update_bar()?;
1094
1095
        let visible = self.visible_windows_on_monitor(new_monitor);
1096
        if let Some(&win) = visible.first() {
1097
            self.set_focus(win)?;
1098
        }
1099
1100
        Ok(())
1101
    }
1102
1103
    fn is_window_visible(&self, window: Window) -> bool {
1104
        let window_mon = self.window_monitor.get(&window).copied().unwrap_or(0);
1105
1106
        if let Some(&tags) = self.window_tags.get(&window) {
1107
            let monitor = self.monitors.get(window_mon);
1108
            let selected_tags = monitor.map(|m| m.selected_tags).unwrap_or(0);
1109
            (tags & selected_tags) != 0
1110
        } else {
1111
            false
1112
        }
1113
    }
1114
1115
    fn visible_windows(&self) -> Vec<Window> {
1116
        self.windows
1117
            .iter()
1118
            .filter(|&&w| self.is_window_visible(w))
1119
            .copied()
1120
            .collect()
1121
    }
1122
1123
    fn visible_windows_on_monitor(&self, monitor_index: usize) -> Vec<Window> {
1124
        self.windows
1125
            .iter()
1126
            .filter(|&&w| {
1127
                let window_mon = self.window_monitor.get(&w).copied().unwrap_or(0);
1128
                if window_mon != monitor_index {
1129
                    return false;
1130
                }
1131
                if let Some(&tags) = self.window_tags.get(&w) {
1132
                    let monitor = self.monitors.get(monitor_index);
1133
                    let selected_tags = monitor.map(|m| m.selected_tags).unwrap_or(0);
1134
                    (tags & selected_tags) != 0
1135
                } else {
1136
                    false
1137
                }
1138
            })
1139
            .copied()
1140
            .collect()
1141
    }
1142
1143
    fn get_monitor_at_point(&self, x: i32, y: i32) -> Option<usize> {
1144
        self.monitors
1145
            .iter()
1146
            .position(|mon| mon.contains_point(x, y))
1147
    }
1148
1149
    // Dwm's g-loaded approach to handling the spam alternating crash.
1150
    fn update_window_visibility(&self) -> WmResult<()> {
1151
        for &window in &self.windows {
1152
            if !self.is_window_visible(window) {
1153
                if let Ok(geom) = self.connection.get_geometry(window)?.reply() {
1154
                    self.connection.configure_window(
1155
                        window,
1156
                        &ConfigureWindowAux::new()
1157
                            .x(-(geom.width as i32 * 2))
1158
                            .y(geom.y as i32),
1159
                    )?;
1160
                }
1161
            }
1162
        }
1163
        self.connection.flush()?;
1164
        Ok(())
1165
    }
1166
1167
    pub fn view_tag(&mut self, tag_index: usize) -> WmResult<()> {
1168
        if tag_index >= self.config.tags.len() {
1169
            return Ok(());
1170
        }
1171
1172
        if let Some(monitor) = self.monitors.get_mut(self.selected_monitor) {
1173
            monitor.selected_tags = tag_mask(tag_index);
1174
        }
1175
1176
        self.save_selected_tags()?;
1177
        self.update_window_visibility()?;
1178
        self.apply_layout()?;
1179
        self.update_bar()?;
1180
1181
        let visible = self.visible_windows_on_monitor(self.selected_monitor);
1182
        if let Some(&win) = visible.first() {
1183
            self.set_focus(win)?;
1184
        }
1185
1186
        Ok(())
1187
    }
1188
1189
    fn save_selected_tags(&self) -> WmResult<()> {
1190
        let net_current_desktop = self.atoms.net_current_desktop;
1191
1192
        let selected_tags = self
1193
            .monitors
1194
            .get(self.selected_monitor)
1195
            .map(|m| m.selected_tags)
1196
            .unwrap_or(tag_mask(0));
1197
        let desktop = selected_tags.trailing_zeros();
1198
1199
        let bytes = (desktop as u32).to_ne_bytes();
1200
        self.connection.change_property(
1201
            PropMode::REPLACE,
1202
            self.root,
1203
            net_current_desktop,
1204
            AtomEnum::CARDINAL,
1205
            32,
1206
            1,
1207
            &bytes,
1208
        )?;
1209
1210
        self.connection.flush()?;
1211
        Ok(())
1212
    }
1213
1214
    pub fn move_to_tag(&mut self, tag_index: usize) -> WmResult<()> {
1215
        if tag_index >= self.config.tags.len() {
1216
            return Ok(());
1217
        }
1218
1219
        if let Some(focused) = self
1220
            .monitors
1221
            .get(self.selected_monitor)
1222
            .and_then(|m| m.focused_window)
1223
        {
1224
            let mask = tag_mask(tag_index);
1225
            self.window_tags.insert(focused, mask);
1226
1227
            let _ = self.save_client_tag(focused, mask);
1228
1229
            self.update_window_visibility()?;
1230
            self.apply_layout()?;
1231
            self.update_bar()?;
1232
        }
1233
1234
        Ok(())
1235
    }
1236
1237
    pub fn cycle_focus(&mut self, direction: i32) -> WmResult<()> {
1238
        let visible = self.visible_windows();
1239
1240
        if visible.is_empty() {
1241
            return Ok(());
1242
        }
1243
1244
        let current = self
1245
            .monitors
1246
            .get(self.selected_monitor)
1247
            .and_then(|m| m.focused_window);
1248
1249
        let next_window = if let Some(current) = current {
1250
            if let Some(current_index) = visible.iter().position(|&w| w == current) {
1251
                let next_index = if direction > 0 {
1252
                    (current_index + 1) % visible.len()
1253
                } else {
1254
                    (current_index + visible.len() - 1) % visible.len()
1255
                };
1256
                visible[next_index]
1257
            } else {
1258
                visible[0]
1259
            }
1260
        } else {
1261
            visible[0]
1262
        };
1263
1264
        self.set_focus(next_window)?;
1265
        Ok(())
1266
    }
1267
1268
    pub fn focus_direction(&mut self, direction: i32) -> WmResult<()> {
1269
        let focused = match self
1270
            .monitors
1271
            .get(self.selected_monitor)
1272
            .and_then(|m| m.focused_window)
1273
        {
1274
            Some(win) => win,
1275
            None => return Ok(()),
1276
        };
1277
1278
        let visible = self.visible_windows();
1279
        if visible.len() < 2 {
1280
            return Ok(());
1281
        }
1282
1283
        let focused_geom = match self.connection.get_geometry(focused)?.reply() {
1284
            Ok(geom) => geom,
1285
            Err(_) => return Ok(()),
1286
        };
1287
1288
        let focused_center_x = focused_geom.x + (focused_geom.width as i16 / 2);
1289
        let focused_center_y = focused_geom.y + (focused_geom.height as i16 / 2);
1290
1291
        let mut candidates = Vec::new();
1292
1293
        for &window in &visible {
1294
            if window == focused {
1295
                continue;
1296
            }
1297
1298
            let geom = match self.connection.get_geometry(window)?.reply() {
1299
                Ok(g) => g,
1300
                Err(_) => continue,
1301
            };
1302
1303
            let center_x = geom.x + (geom.width as i16 / 2);
1304
            let center_y = geom.y + (geom.height as i16 / 2);
1305
1306
            let is_valid_direction = match direction {
1307
                0 => center_y < focused_center_y,
1308
                1 => center_y > focused_center_y,
1309
                2 => center_x < focused_center_x,
1310
                3 => center_x > focused_center_x,
1311
                _ => false,
1312
            };
1313
1314
            if is_valid_direction {
1315
                let dx = (center_x - focused_center_x) as i32;
1316
                let dy = (center_y - focused_center_y) as i32;
1317
                let distance = dx * dx + dy * dy;
1318
                candidates.push((window, distance));
1319
            }
1320
        }
1321
1322
        if let Some(&(closest_window, _)) = candidates.iter().min_by_key(|&(_, dist)| dist) {
1323
            self.set_focus(closest_window)?;
1324
        }
1325
1326
        Ok(())
1327
    }
1328
1329
    pub fn swap_direction(&mut self, direction: i32) -> WmResult<()> {
1330
        let focused = match self
1331
            .monitors
1332
            .get(self.selected_monitor)
1333
            .and_then(|m| m.focused_window)
1334
        {
1335
            Some(win) => win,
1336
            None => return Ok(()),
1337
        };
1338
1339
        let visible = self.visible_windows();
1340
        if visible.len() < 2 {
1341
            return Ok(());
1342
        }
1343
1344
        let focused_geom = match self.connection.get_geometry(focused)?.reply() {
1345
            Ok(geom) => geom,
1346
            Err(_) => return Ok(()),
1347
        };
1348
1349
        let focused_center_x = focused_geom.x + (focused_geom.width as i16 / 2);
1350
        let focused_center_y = focused_geom.y + (focused_geom.height as i16 / 2);
1351
1352
        let mut candidates = Vec::new();
1353
1354
        for &window in &visible {
1355
            if window == focused {
1356
                continue;
1357
            }
1358
1359
            let geom = match self.connection.get_geometry(window)?.reply() {
1360
                Ok(g) => g,
1361
                Err(_) => continue,
1362
            };
1363
1364
            let center_x = geom.x + (geom.width as i16 / 2);
1365
            let center_y = geom.y + (geom.height as i16 / 2);
1366
1367
            let is_valid_direction = match direction {
1368
                0 => center_y < focused_center_y,
1369
                1 => center_y > focused_center_y,
1370
                2 => center_x < focused_center_x,
1371
                3 => center_x > focused_center_x,
1372
                _ => false,
1373
            };
1374
1375
            if is_valid_direction {
1376
                let dx = (center_x - focused_center_x) as i32;
1377
                let dy = (center_y - focused_center_y) as i32;
1378
                let distance = dx * dx + dy * dy;
1379
                candidates.push((window, distance));
1380
            }
1381
        }
1382
1383
        if let Some(&(target_window, _)) = candidates.iter().min_by_key(|&(_, dist)| dist) {
1384
            let focused_pos = self.windows.iter().position(|&w| w == focused);
1385
            let target_pos = self.windows.iter().position(|&w| w == target_window);
1386
1387
            if let (Some(f_pos), Some(t_pos)) = (focused_pos, target_pos) {
1388
                self.windows.swap(f_pos, t_pos);
1389
                self.apply_layout()?;
1390
                self.set_focus(focused)?;
1391
1392
                if let Ok(geometry) = self.connection.get_geometry(focused)?.reply() {
1393
                    self.connection.warp_pointer(
1394
                        x11rb::NONE,
1395
                        focused,
1396
                        0,
1397
                        0,
1398
                        0,
1399
                        0,
1400
                        geometry.width as i16 / 2,
1401
                        geometry.height as i16 / 2,
1402
                    )?;
1403
                }
1404
            }
1405
        }
1406
1407
        Ok(())
1408
    }
1409
1410
    fn grab_next_keys(&self, candidates: &[usize], keys_pressed: usize) -> WmResult<()> {
1411
        use std::collections::HashMap;
1412
        use x11rb::protocol::xproto::Keycode;
1413
1414
        let setup = self.connection.setup();
1415
        let min_keycode = setup.min_keycode;
1416
        let max_keycode = setup.max_keycode;
1417
1418
        let keyboard_mapping = self
1419
            .connection
1420
            .get_keyboard_mapping(min_keycode, max_keycode - min_keycode + 1)?
1421
            .reply()?;
1422
1423
        let mut keysym_to_keycode: HashMap<keyboard::keysyms::Keysym, Vec<Keycode>> =
1424
            HashMap::new();
1425
        let keysyms_per_keycode = keyboard_mapping.keysyms_per_keycode;
1426
1427
        for keycode in min_keycode..=max_keycode {
1428
            let index = (keycode - min_keycode) as usize * keysyms_per_keycode as usize;
1429
            for i in 0..keysyms_per_keycode as usize {
1430
                if let Some(&keysym) = keyboard_mapping.keysyms.get(index + i) {
1431
                    if keysym != 0 {
1432
                        keysym_to_keycode
1433
                            .entry(keysym)
1434
                            .or_insert_with(Vec::new)
1435
                            .push(keycode);
1436
                    }
1437
                }
1438
            }
1439
        }
1440
1441
        let mut grabbed_keys: HashSet<(u16, Keycode)> = HashSet::new();
1442
1443
        for &candidate_index in candidates {
1444
            let binding = &self.config.keybindings[candidate_index];
1445
            if keys_pressed < binding.keys.len() {
1446
                let next_key = &binding.keys[keys_pressed];
1447
                let modifier_mask = keyboard::handlers::modifiers_to_mask(&next_key.modifiers);
1448
1449
                if let Some(keycodes) = keysym_to_keycode.get(&next_key.keysym) {
1450
                    if let Some(&keycode) = keycodes.first() {
1451
                        let key_tuple = (modifier_mask, keycode);
1452
1453
                        if grabbed_keys.insert(key_tuple) {
1454
                            self.connection.grab_key(
1455
                                false,
1456
                                self.root,
1457
                                modifier_mask.into(),
1458
                                keycode,
1459
                                GrabMode::ASYNC,
1460
                                GrabMode::ASYNC,
1461
                            )?;
1462
                        }
1463
                    }
1464
                }
1465
            }
1466
        }
1467
1468
        if let Some(keycodes) = keysym_to_keycode.get(&keyboard::keysyms::XK_ESCAPE) {
1469
            if let Some(&keycode) = keycodes.first() {
1470
                self.connection.grab_key(
1471
                    false,
1472
                    self.root,
1473
                    ModMask::from(0u16),
1474
                    keycode,
1475
                    GrabMode::ASYNC,
1476
                    GrabMode::ASYNC,
1477
                )?;
1478
            }
1479
        }
1480
1481
        self.connection.flush()?;
1482
        Ok(())
1483
    }
1484
1485
    fn ungrab_chord_keys(&self) -> WmResult<()> {
1486
        self.connection
1487
            .ungrab_key(x11rb::protocol::xproto::Grab::ANY, self.root, ModMask::ANY)?;
1488
        keyboard::setup_keybinds(&self.connection, self.root, &self.config.keybindings)?;
1489
        self.connection.flush()?;
1490
        Ok(())
1491
    }
1492
1493
    fn kill_client(&self, window: Window) -> WmResult<()> {
1494
        // Try to send WM_DELETE_WINDOW message first (graceful close)
1495
        if self.send_event(window, self.atoms.wm_delete_window)? {
1496
            // Window supports WM_DELETE_WINDOW, it will close gracefully
1497
            self.connection.flush()?;
1498
        } else {
1499
            // Window doesn't support WM_DELETE_WINDOW, forcefully kill it
1500
            eprintln!("Window {} doesn't support WM_DELETE_WINDOW, killing forcefully", window);
1501
            self.connection.kill_client(window)?;
1502
            self.connection.flush()?;
1503
        }
1504
        Ok(())
1505
    }
1506
1507
    fn send_event(&self, window: Window, protocol: Atom) -> WmResult<bool> {
1508
        // Check if the window supports this protocol
1509
        let protocols_reply = self.connection.get_property(
1510
            false,
1511
            window,
1512
            self.atoms.wm_protocols,
1513
            AtomEnum::ATOM,
1514
            0,
1515
            100,
1516
        )?.reply();
1517
1518
        let protocols_reply = match protocols_reply {
1519
            Ok(reply) => reply,
1520
            Err(_) => return Ok(false),
1521
        };
1522
1523
        let protocols: Vec<Atom> = protocols_reply
1524
            .value
1525
            .chunks_exact(4)
1526
            .map(|chunk| u32::from_ne_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]))
1527
            .collect();
1528
1529
        if !protocols.contains(&protocol) {
1530
            return Ok(false);
1531
        }
1532
1533
        // Send a ClientMessage event
1534
        let event = x11rb::protocol::xproto::ClientMessageEvent {
1535
            response_type: x11rb::protocol::xproto::CLIENT_MESSAGE_EVENT,
1536
            format: 32,
1537
            sequence: 0,
1538
            window,
1539
            type_: self.atoms.wm_protocols,
1540
            data: x11rb::protocol::xproto::ClientMessageData::from([protocol, x11rb::CURRENT_TIME, 0, 0, 0]),
1541
        };
1542
1543
        self.connection.send_event(
1544
            false,
1545
            window,
1546
            EventMask::NO_EVENT,
1547
            event,
1548
        )?;
1549
        self.connection.flush()?;
1550
        Ok(true)
1551
    }
1552
1553
    fn fullscreen(&mut self) -> WmResult<()> {
1554
        if self.show_bar {
1555
            self.last_layout = Some(self.layout.name());
1556
            if let Ok(layout) = layout_from_str("monocle") {
1557
                self.layout = layout;
1558
            }
1559
            self.toggle_bar()?;
1560
            self.apply_layout()?;
1561
1562
            let border_width = self.config.border_width;
1563
            let windows: Vec<Window> = self.windows.iter()
1564
                .filter(|&&w| self.floating_windows.contains(&w) && self.is_window_visible(w))
1565
                .copied()
1566
                .collect();
1567
1568
            for window in windows {
1569
                if let Ok(geom) = self.connection.get_geometry(window)?.reply() {
1570
                        self.floating_geometry_before_fullscreen.insert(
1571
                            window,
1572
                            (geom.x, geom.y, geom.width, geom.height, geom.border_width as u16),
1573
                        );
1574
                    }
1575
1576
                let monitor_idx = *self.window_monitor.get(&window).unwrap_or(&self.selected_monitor);
1577
                let monitor = &self.monitors[monitor_idx];
1578
1579
                let (outer_gap_h, outer_gap_v) = if self.gaps_enabled {
1580
                    (
1581
                        self.config.gap_outer_horizontal,
1582
                        self.config.gap_outer_vertical,
1583
                    )
1584
                } else {
1585
                    (0, 0)
1586
                };
1587
1588
                let x = monitor.x + outer_gap_h as i32;
1589
                let y = monitor.y + outer_gap_v as i32;
1590
                let width = monitor.width.saturating_sub(2 * outer_gap_h).saturating_sub(2 * border_width);
1591
                let height = monitor.height.saturating_sub(2 * outer_gap_v).saturating_sub(2 * border_width);
1592
1593
                self.connection.configure_window(
1594
                    window,
1595
                    &x11rb::protocol::xproto::ConfigureWindowAux::new()
1596
                        .x(x)
1597
                        .y(y)
1598
                        .width(width)
1599
                        .height(height),
1600
                )?;
1601
            }
1602
            self.connection.flush()?;
1603
        } else {
1604
            if let Some(last) = self.last_layout {
1605
                if let Ok(layout) = layout_from_str(last) {
1606
                    self.layout = layout;
1607
                }
1608
            }
1609
            self.toggle_bar()?;
1610
            self.apply_layout()?;
1611
1612
            let windows: Vec<Window> = self.windows.iter()
1613
                .filter(|&&w| self.floating_windows.contains(&w) && self.is_window_visible(w))
1614
                .copied()
1615
                .collect();
1616
1617
            for window in windows {
1618
                if let Some(&(x, y, width, height, border_width)) = self.floating_geometry_before_fullscreen.get(&window) {
1619
                    self.connection.configure_window(
1620
                        window,
1621
                        &x11rb::protocol::xproto::ConfigureWindowAux::new()
1622
                            .x(x as i32)
1623
                            .y(y as i32)
1624
                            .width(width as u32)
1625
                            .height(height as u32)
1626
                            .border_width(border_width as u32),
1627
                    )?;
1628
                    self.floating_geometry_before_fullscreen.remove(&window);
1629
                }
1630
            }
1631
            self.connection.flush()?;
1632
        }
1633
        Ok(())
1634
    }
1635
1636
    fn set_window_fullscreen(&mut self, window: Window, fullscreen: bool) -> WmResult<()> {
1637
        if fullscreen && !self.fullscreen_windows.contains(&window) {
1638
            let bytes = self.atoms.net_wm_state_fullscreen.to_ne_bytes().to_vec();
1639
            self.connection.change_property(
1640
                PropMode::REPLACE,
1641
                window,
1642
                self.atoms.net_wm_state,
1643
                AtomEnum::ATOM,
1644
                32,
1645
                1,
1646
                &bytes,
1647
            )?;
1648
1649
            self.fullscreen_windows.insert(window);
1650
1651
            let monitor_idx = *self.window_monitor.get(&window).unwrap_or(&self.selected_monitor);
1652
            let monitor = &self.monitors[monitor_idx];
1653
1654
            if self.show_bar {
1655
                if let Some(bar) = self.bars.get(monitor_idx) {
1656
                    self.connection.unmap_window(bar.window())?;
1657
                }
1658
            }
1659
1660
            self.connection.configure_window(
1661
                window,
1662
                &x11rb::protocol::xproto::ConfigureWindowAux::new()
1663
                    .border_width(0)
1664
                    .x(monitor.x)
1665
                    .y(monitor.y)
1666
                    .width(monitor.width as u32)
1667
                    .height(monitor.height as u32)
1668
                    .stack_mode(x11rb::protocol::xproto::StackMode::ABOVE),
1669
            )?;
1670
1671
            self.connection.flush()?;
1672
            self.update_bar()?;
1673
        } else if !fullscreen && self.fullscreen_windows.contains(&window) {
1674
            self.connection.change_property(
1675
                PropMode::REPLACE,
1676
                window,
1677
                self.atoms.net_wm_state,
1678
                AtomEnum::ATOM,
1679
                32,
1680
                0,
1681
                &[],
1682
            )?;
1683
1684
            self.fullscreen_windows.remove(&window);
1685
1686
            self.connection.configure_window(
1687
                window,
1688
                &x11rb::protocol::xproto::ConfigureWindowAux::new()
1689
                    .border_width(self.config.border_width),
1690
            )?;
1691
1692
            let monitor_idx = *self.window_monitor.get(&window).unwrap_or(&self.selected_monitor);
1693
            if self.show_bar {
1694
                if let Some(bar) = self.bars.get(monitor_idx) {
1695
                    self.connection.map_window(bar.window())?;
1696
                }
1697
            }
1698
1699
            self.apply_layout()?;
1700
        }
1701
1702
        Ok(())
1703
    }
1704
1705
    fn toggle_bar(&mut self) -> WmResult<()> {
1706
        self.show_bar = !self.show_bar;
1707
        if let Some(bar) = self.bars.get(self.selected_monitor) {
1708
            if self.show_bar {
1709
                self.connection.map_window(bar.window())?;
1710
            } else {
1711
                self.connection.unmap_window(bar.window())?;
1712
            }
1713
            self.connection.flush()?;
1714
        }
1715
        self.apply_layout()?;
1716
        Ok(())
1717
    }
1718
1719
    fn is_transient_window(&self, window: Window) -> bool {
1720
        let transient_for = self.connection
1721
            .get_property(
1722
                false,
1723
                window,
1724
                AtomEnum::WM_TRANSIENT_FOR,
1725
                AtomEnum::WINDOW,
1726
                0,
1727
                1,
1728
            )
1729
            .ok()
1730
            .and_then(|cookie| cookie.reply().ok())
1731
            .filter(|reply| !reply.value.is_empty());
1732
1733
        transient_for.is_some()
1734
    }
1735
1736
    pub fn set_focus(&mut self, window: Window) -> WmResult<()> {
1737
        let old_focused = self.previous_focused;
1738
1739
        if let Some(monitor) = self.monitors.get_mut(self.selected_monitor) {
1740
            monitor.focused_window = Some(window);
1741
        }
1742
1743
        self.connection
1744
            .set_input_focus(InputFocus::POINTER_ROOT, window, x11rb::CURRENT_TIME)?;
1745
        self.connection.flush()?;
1746
1747
        self.update_focus_visuals(old_focused, window)?;
1748
        self.previous_focused = Some(window);
1749
        Ok(())
1750
    }
1751
1752
    fn update_focus_visuals(
1753
        &self,
1754
        old_focused: Option<Window>,
1755
        new_focused: Window,
1756
    ) -> WmResult<()> {
1757
        if let Some(old_win) = old_focused {
1758
            if old_win != new_focused {
1759
                self.connection.configure_window(
1760
                    old_win,
1761
                    &ConfigureWindowAux::new().border_width(self.config.border_width),
1762
                )?;
1763
1764
                self.connection.change_window_attributes(
1765
                    old_win,
1766
                    &ChangeWindowAttributesAux::new().border_pixel(self.config.border_unfocused),
1767
                )?;
1768
            }
1769
        }
1770
1771
        self.connection.configure_window(
1772
            new_focused,
1773
            &ConfigureWindowAux::new().border_width(self.config.border_width),
1774
        )?;
1775
1776
        self.connection.change_window_attributes(
1777
            new_focused,
1778
            &ChangeWindowAttributesAux::new().border_pixel(self.config.border_focused),
1779
        )?;
1780
1781
        self.connection.flush()?;
1782
        Ok(())
1783
    }
1784
1785
    fn move_mouse(&mut self, window: Window) -> WmResult<()> {
1786
        self.floating_windows.insert(window);
1787
1788
        let geometry = self.connection.get_geometry(window)?.reply()?;
1789
1790
        self.connection
1791
            .grab_pointer(
1792
                false,
1793
                self.root,
1794
                (EventMask::POINTER_MOTION | EventMask::BUTTON_RELEASE).into(),
1795
                GrabMode::ASYNC,
1796
                GrabMode::ASYNC,
1797
                x11rb::NONE,
1798
                x11rb::NONE,
1799
                x11rb::CURRENT_TIME,
1800
            )?
1801
            .reply()?;
1802
1803
        let pointer = self.connection.query_pointer(self.root)?.reply()?;
1804
        let (start_x, start_y) = (pointer.root_x, pointer.root_y);
1805
1806
        self.connection.configure_window(
1807
            window,
1808
            &ConfigureWindowAux::new().stack_mode(StackMode::ABOVE),
1809
        )?;
1810
1811
        loop {
1812
            let event = self.connection.wait_for_event()?;
1813
            match event {
1814
                Event::MotionNotify(e) => {
1815
                    let new_x = geometry.x + (e.root_x - start_x);
1816
                    let new_y = geometry.y + (e.root_y - start_y);
1817
                    self.connection.configure_window(
1818
                        window,
1819
                        &ConfigureWindowAux::new().x(new_x as i32).y(new_y as i32),
1820
                    )?;
1821
                    self.connection.flush()?;
1822
                }
1823
                Event::ButtonRelease(_) => break,
1824
                _ => {}
1825
            }
1826
        }
1827
1828
        self.connection
1829
            .ungrab_pointer(x11rb::CURRENT_TIME)?
1830
            .check()?;
1831
        self.connection
1832
            .allow_events(Allow::REPLAY_POINTER, x11rb::CURRENT_TIME)?
1833
            .check()?;
1834
1835
        Ok(())
1836
    }
1837
1838
    fn resize_mouse(&mut self, window: Window) -> WmResult<()> {
1839
        self.floating_windows.insert(window);
1840
1841
        let geometry = self.connection.get_geometry(window)?.reply()?;
1842
1843
        self.connection.warp_pointer(
1844
            x11rb::NONE,
1845
            window,
1846
            0,
1847
            0,
1848
            0,
1849
            0,
1850
            geometry.width as i16,
1851
            geometry.height as i16,
1852
        )?;
1853
1854
        self.connection
1855
            .grab_pointer(
1856
                false,
1857
                self.root,
1858
                (EventMask::POINTER_MOTION | EventMask::BUTTON_RELEASE).into(),
1859
                GrabMode::ASYNC,
1860
                GrabMode::ASYNC,
1861
                x11rb::NONE,
1862
                x11rb::NONE,
1863
                x11rb::CURRENT_TIME,
1864
            )?
1865
            .reply()?;
1866
1867
        self.connection.configure_window(
1868
            window,
1869
            &ConfigureWindowAux::new().stack_mode(StackMode::ABOVE),
1870
        )?;
1871
1872
        loop {
1873
            let event = self.connection.wait_for_event()?;
1874
            match event {
1875
                Event::MotionNotify(e) => {
1876
                    let new_width = (e.root_x - geometry.x).max(1) as u32;
1877
                    let new_height = (e.root_y - geometry.y).max(1) as u32;
1878
1879
                    self.connection.configure_window(
1880
                        window,
1881
                        &ConfigureWindowAux::new()
1882
                            .width(new_width)
1883
                            .height(new_height),
1884
                    )?;
1885
                    self.connection.flush()?;
1886
                }
1887
                Event::ButtonRelease(_) => break,
1888
                _ => {}
1889
            }
1890
        }
1891
1892
        self.connection
1893
            .ungrab_pointer(x11rb::CURRENT_TIME)?
1894
            .check()?;
1895
        self.connection
1896
            .allow_events(Allow::REPLAY_POINTER, x11rb::CURRENT_TIME)?
1897
            .check()?;
1898
1899
        Ok(())
1900
    }
1901
1902
    fn handle_event(&mut self, event: Event) -> WmResult<Option<bool>> {
1903
        match event {
1904
            Event::KeyPress(ref e) if e.event == self.overlay.window() => {
1905
                if self.overlay.is_visible() {
1906
                    let _ = self.overlay.hide(&self.connection);
1907
                }
1908
                return Ok(None);
1909
            }
1910
            Event::ButtonPress(ref e) if e.event == self.overlay.window() => {
1911
                if self.overlay.is_visible() {
1912
                    let _ = self.overlay.hide(&self.connection);
1913
                }
1914
                return Ok(None);
1915
            }
1916
            Event::Expose(ref e) if e.window == self.overlay.window() => {
1917
                if self.overlay.is_visible() {
1918
                    let _ = self.overlay.draw(&self.connection, &self.font);
1919
                }
1920
                return Ok(None);
1921
            }
1922
            Event::KeyPress(ref e) if e.event == self.keybind_overlay.window() => {
1923
                if self.keybind_overlay.is_visible()
1924
                    && !self.keybind_overlay.should_suppress_input()
1925
                {
1926
                    use crate::keyboard::keysyms;
1927
                    let keyboard_mapping = self
1928
                        .connection
1929
                        .get_keyboard_mapping(
1930
                            self.connection.setup().min_keycode,
1931
                            self.connection.setup().max_keycode
1932
                                - self.connection.setup().min_keycode
1933
                                + 1,
1934
                        )?
1935
                        .reply()?;
1936
1937
                    let min_keycode = self.connection.setup().min_keycode;
1938
                    let keysyms_per_keycode = keyboard_mapping.keysyms_per_keycode;
1939
                    let index = (e.detail - min_keycode) as usize * keysyms_per_keycode as usize;
1940
1941
                    if let Some(&keysym) = keyboard_mapping.keysyms.get(index) {
1942
                        if keysym == keysyms::XK_ESCAPE || keysym == keysyms::XK_Q {
1943
                            let _ = self.keybind_overlay.hide(&self.connection);
1944
                        }
1945
                    }
1946
                }
1947
                return Ok(None);
1948
            }
1949
            Event::ButtonPress(ref e) if e.event == self.keybind_overlay.window() => {
1950
                return Ok(None);
1951
            }
1952
            Event::Expose(ref e) if e.window == self.keybind_overlay.window() => {
1953
                if self.keybind_overlay.is_visible() {
1954
                    let _ = self.keybind_overlay.draw(&self.connection, &self.font);
1955
                }
1956
                return Ok(None);
1957
            }
1958
            Event::MapRequest(event) => {
1959
                let attrs = match self.connection.get_window_attributes(event.window)?.reply() {
1960
                    Ok(attrs) => attrs,
1961
                    Err(_) => return Ok(None),
1962
                };
1963
1964
                if attrs.override_redirect {
1965
                    return Ok(None);
1966
                }
1967
1968
                if self.windows.contains(&event.window) {
1969
                    return Ok(None);
1970
                }
1971
1972
                self.connection.map_window(event.window)?;
1973
                self.connection.change_window_attributes(
1974
                    event.window,
1975
                    &ChangeWindowAttributesAux::new().event_mask(EventMask::ENTER_WINDOW | EventMask::STRUCTURE_NOTIFY | EventMask::PROPERTY_CHANGE),
1976
                )?;
1977
1978
                let selected_tags = self
1979
                    .monitors
1980
                    .get(self.selected_monitor)
1981
                    .map(|m| m.selected_tags)
1982
                    .unwrap_or(tag_mask(0));
1983
1984
                self.windows.push(event.window);
1985
                self.window_tags.insert(event.window, selected_tags);
1986
                self.window_monitor
1987
                    .insert(event.window, self.selected_monitor);
1988
                self.set_wm_state(event.window, 1)?;
1989
                let _ = self.save_client_tag(event.window, selected_tags);
1990
1991
                let is_transient = self.is_transient_window(event.window);
1992
                if is_transient {
1993
                    self.floating_windows.insert(event.window);
1994
                }
1995
1996
                let is_normie_layout = self.layout.name() == "normie";
1997
                if is_normie_layout && !is_transient {
1998
                    let mut pointer_x: i32 = 0;
1999
                    let mut pointer_y: i32 = 0;
2000
                    let mut root_return: x11::xlib::Window = 0;
2001
                    let mut child_return: x11::xlib::Window = 0;
2002
                    let mut win_x: i32 = 0;
2003
                    let mut win_y: i32 = 0;
2004
                    let mut mask_return: u32 = 0;
2005
2006
                    let pointer_queried = unsafe {
2007
                        x11::xlib::XQueryPointer(
2008
                            self.display,
2009
                            self.root as x11::xlib::Window,
2010
                            &mut root_return,
2011
                            &mut child_return,
2012
                            &mut pointer_x,
2013
                            &mut pointer_y,
2014
                            &mut win_x,
2015
                            &mut win_y,
2016
                            &mut mask_return,
2017
                        ) != 0
2018
                    };
2019
2020
                    if pointer_queried {
2021
                        let window_width = (self.screen.width_in_pixels as f32 * 0.6) as u32;
2022
                        let window_height = (self.screen.height_in_pixels as f32 * 0.6) as u32;
2023
2024
                        let spawn_x = pointer_x - (window_width as i32 / 2);
2025
                        let spawn_y = pointer_y - (window_height as i32 / 2);
2026
2027
                        self.connection.configure_window(
2028
                            event.window,
2029
                            &ConfigureWindowAux::new()
2030
                                .x(spawn_x)
2031
                                .y(spawn_y)
2032
                                .width(window_width)
2033
                                .height(window_height)
2034
                                .border_width(self.config.border_width)
2035
                                .stack_mode(StackMode::ABOVE),
2036
                        )?;
2037
2038
                        self.floating_windows.insert(event.window);
2039
                    }
2040
                }
2041
2042
                self.apply_layout()?;
2043
                self.update_bar()?;
2044
                self.set_focus(event.window)?;
2045
            }
2046
            Event::UnmapNotify(event) => {
2047
                if self.windows.contains(&event.window) && self.is_window_visible(event.window) {
2048
                    self.remove_window(event.window)?;
2049
                }
2050
            }
2051
            Event::DestroyNotify(event) => {
2052
                if self.windows.contains(&event.window) {
2053
                    self.remove_window(event.window)?;
2054
                }
2055
            }
2056
            Event::EnterNotify(event) => {
2057
                if event.mode != x11rb::protocol::xproto::NotifyMode::NORMAL {
2058
                    return Ok(None);
2059
                }
2060
                if self.windows.contains(&event.event) {
2061
                    self.set_focus(event.event)?;
2062
                }
2063
            }
2064
            Event::MotionNotify(event) => {
2065
                if event.event != self.root {
2066
                    return Ok(None);
2067
                }
2068
2069
                if let Some(monitor_index) =
2070
                    self.get_monitor_at_point(event.root_x as i32, event.root_y as i32)
2071
                {
2072
                    if monitor_index != self.selected_monitor {
2073
                        self.selected_monitor = monitor_index;
2074
                        self.update_bar()?;
2075
2076
                        let visible = self.visible_windows_on_monitor(monitor_index);
2077
                        if let Some(&win) = visible.first() {
2078
                            self.set_focus(win)?;
2079
                        }
2080
                    }
2081
                }
2082
            }
2083
            Event::KeyPress(event) => {
2084
                let result = keyboard::handle_key_press(
2085
                    event,
2086
                    &self.config.keybindings,
2087
                    &self.keychord_state,
2088
                    &self.connection,
2089
                )?;
2090
2091
                match result {
2092
                    keyboard::handlers::KeychordResult::Completed(action, arg) => {
2093
                        self.keychord_state = keyboard::handlers::KeychordState::Idle;
2094
                        self.ungrab_chord_keys()?;
2095
                        self.update_bar()?;
2096
2097
                        match action {
2098
                            KeyAction::Quit => return Ok(Some(false)),
2099
                            KeyAction::Restart => match self.try_reload_config() {
2100
                                Ok(()) => {
2101
                                    self.gaps_enabled = self.config.gaps_enabled;
2102
                                    self.error_message = None;
2103
                                    let _ = self.overlay.hide(&self.connection);
2104
                                    self.apply_layout()?;
2105
                                    self.update_bar()?;
2106
                                }
2107
                                Err(err) => {
2108
                                    eprintln!("Config reload error: {}", err);
2109
                                    self.error_message = Some(err.clone());
2110
                                    let screen_width = self.screen.width_in_pixels;
2111
                                    let screen_height = self.screen.height_in_pixels;
2112
                                    match self.overlay.show_error(
2113
                                        &self.connection,
2114
                                        &self.font,
2115
                                        &err,
2116
                                        screen_width,
2117
                                        screen_height,
2118
                                    ) {
2119
                                        Ok(()) => eprintln!("Error modal displayed"),
2120
                                        Err(e) => eprintln!("Failed to show error modal: {:?}", e),
2121
                                    }
2122
                                }
2123
                            },
2124
                            _ => self.handle_key_action(action, &arg)?,
2125
                        }
2126
                    }
2127
                    keyboard::handlers::KeychordResult::InProgress(candidates) => {
2128
                        let keys_pressed = match &self.keychord_state {
2129
                            keyboard::handlers::KeychordState::Idle => 1,
2130
                            keyboard::handlers::KeychordState::InProgress {
2131
                                keys_pressed, ..
2132
                            } => keys_pressed + 1,
2133
                        };
2134
2135
                        self.keychord_state = keyboard::handlers::KeychordState::InProgress {
2136
                            candidates: candidates.clone(),
2137
                            keys_pressed,
2138
                        };
2139
2140
                        self.grab_next_keys(&candidates, keys_pressed)?;
2141
                        self.update_bar()?;
2142
                    }
2143
                    keyboard::handlers::KeychordResult::Cancelled
2144
                    | keyboard::handlers::KeychordResult::None => {
2145
                        self.keychord_state = keyboard::handlers::KeychordState::Idle;
2146
                        self.ungrab_chord_keys()?;
2147
                        self.update_bar()?;
2148
                    }
2149
                }
2150
            }
2151
            Event::ButtonPress(event) => {
2152
                let is_bar_click = self
2153
                    .bars
2154
                    .iter()
2155
                    .enumerate()
2156
                    .find(|(_, bar)| bar.window() == event.event);
2157
2158
                if let Some((monitor_index, bar)) = is_bar_click {
2159
                    if let Some(tag_index) = bar.handle_click(event.event_x) {
2160
                        if monitor_index != self.selected_monitor {
2161
                            self.selected_monitor = monitor_index;
2162
                        }
2163
                        self.view_tag(tag_index)?;
2164
                    }
2165
                } else if event.child != x11rb::NONE {
2166
                    self.set_focus(event.child)?;
2167
2168
                    if event.detail == ButtonIndex::M1.into() {
2169
                        self.move_mouse(event.child)?;
2170
                    } else if event.detail == ButtonIndex::M3.into() {
2171
                        self.resize_mouse(event.child)?;
2172
                    }
2173
                }
2174
            }
2175
            Event::Expose(event) => {
2176
                for bar in &mut self.bars {
2177
                    if event.window == bar.window() {
2178
                        bar.invalidate();
2179
                        self.update_bar()?;
2180
                        break;
2181
                    }
2182
                }
2183
            }
2184
            Event::ClientMessage(event) => {
2185
                if event.type_ == self.atoms.net_wm_state {
2186
                    if let Some(data) = event.data.as_data32().get(1) {
2187
                        if *data == self.atoms.net_wm_state_fullscreen {
2188
                            let action = event.data.as_data32()[0];
2189
                            let fullscreen = match action {
2190
                                1 => true,
2191
                                0 => false,
2192
                                2 => !self.fullscreen_windows.contains(&event.window),
2193
                                _ => return Ok(None),
2194
                            };
2195
                            self.set_window_fullscreen(event.window, fullscreen)?;
2196
                        }
2197
                    }
2198
                }
2199
            }
2200
            _ => {}
2201
        }
2202
        Ok(None)
2203
    }
2204
2205
    fn apply_layout(&self) -> WmResult<()> {
2206
        if self.layout.name() == LayoutType::Normie.as_str() {
2207
            return Ok(());
2208
        }
2209
2210
        for (monitor_index, monitor) in self.monitors.iter().enumerate() {
2211
            let border_width = self.config.border_width;
2212
2213
            let gaps = if self.gaps_enabled {
2214
                GapConfig {
2215
                    inner_horizontal: self.config.gap_inner_horizontal,
2216
                    inner_vertical: self.config.gap_inner_vertical,
2217
                    outer_horizontal: self.config.gap_outer_horizontal,
2218
                    outer_vertical: self.config.gap_outer_vertical,
2219
                }
2220
            } else {
2221
                GapConfig {
2222
                    inner_horizontal: 0,
2223
                    inner_vertical: 0,
2224
                    outer_horizontal: 0,
2225
                    outer_vertical: 0,
2226
                }
2227
            };
2228
2229
            let visible: Vec<Window> = self
2230
                .windows
2231
                .iter()
2232
                .filter(|&&w| {
2233
                    let window_mon = self.window_monitor.get(&w).copied().unwrap_or(0);
2234
                    if window_mon != monitor_index {
2235
                        return false;
2236
                    }
2237
                    if self.floating_windows.contains(&w) {
2238
                        return false;
2239
                    }
2240
                    if self.fullscreen_windows.contains(&w) {
2241
                        return false;
2242
                    }
2243
                    if let Some(&tags) = self.window_tags.get(&w) {
2244
                        (tags & monitor.selected_tags) != 0
2245
                    } else {
2246
                        false
2247
                    }
2248
                })
2249
                .copied()
2250
                .collect();
2251
2252
            let bar_height = if self.show_bar {
2253
                self.bars
2254
                    .get(monitor_index)
2255
                    .map(|b| b.height() as u32)
2256
                    .unwrap_or(0)
2257
            } else {
2258
                0
2259
            };
2260
            let usable_height = monitor.height.saturating_sub(bar_height);
2261
2262
            let geometries = self
2263
                .layout
2264
                .arrange(&visible, monitor.width, usable_height, &gaps);
2265
2266
            for (window, geometry) in visible.iter().zip(geometries.iter()) {
2267
                let adjusted_width = geometry.width.saturating_sub(2 * border_width);
2268
                let adjusted_height = geometry.height.saturating_sub(2 * border_width);
2269
2270
                let adjusted_x = geometry.x_coordinate + monitor.x;
2271
                let adjusted_y = geometry.y_coordinate + monitor.y + bar_height as i32;
2272
2273
                self.connection.configure_window(
2274
                    *window,
2275
                    &ConfigureWindowAux::new()
2276
                        .x(adjusted_x)
2277
                        .y(adjusted_y)
2278
                        .width(adjusted_width)
2279
                        .height(adjusted_height)
2280
                        .border_width(border_width),
2281
                )?;
2282
            }
2283
        }
2284
2285
        self.connection.flush()?;
2286
        Ok(())
2287
    }
2288
2289
    pub fn change_layout<L: Layout + 'static>(&mut self, new_layout: L) -> WmResult<()> {
2290
        self.layout = Box::new(new_layout);
2291
        self.apply_layout()?;
2292
        Ok(())
2293
    }
2294
2295
    fn remove_window(&mut self, window: Window) -> WmResult<()> {
2296
        let initial_count = self.windows.len();
2297
2298
        self.windows.retain(|&w| w != window);
2299
        self.window_tags.remove(&window);
2300
        self.window_monitor.remove(&window);
2301
        self.floating_windows.remove(&window);
2302
2303
        if self.windows.len() < initial_count {
2304
            let focused = self
2305
                .monitors
2306
                .get(self.selected_monitor)
2307
                .and_then(|m| m.focused_window);
2308
            if focused == Some(window) {
2309
                let visible = self.visible_windows_on_monitor(self.selected_monitor);
2310
                if let Some(&new_win) = visible.last() {
2311
                    self.set_focus(new_win)?;
2312
                } else if let Some(monitor) = self.monitors.get_mut(self.selected_monitor) {
2313
                    monitor.focused_window = None;
2314
                }
2315
            }
2316
2317
            self.apply_layout()?;
2318
            self.update_bar()?;
2319
        }
2320
        Ok(())
2321
    }
2322
2323
    fn run_autostart_commands(&self) -> Result<(), WmError> {
2324
        for command in &self.config.autostart {
2325
            Command::new("sh")
2326
                .arg("-c")
2327
                .arg(command)
2328
                .spawn()
2329
                .map_err(|e| WmError::Autostart(command.clone(), e))?;
2330
            eprintln!("[autostart] Spawned: {}", command);
2331
        }
2332
        Ok(())
2333
    }
2334
}