oxwm

https://git.tonybtw.com/oxwm.git git://git.tonybtw.com/oxwm.git
64,132 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 std::collections::HashSet;
10
use std::process::Command;
11
use x11rb::cursor::Handle as CursorHandle;
12
13
use x11rb::connection::Connection;
14
use x11rb::protocol::Event;
15
use x11rb::protocol::xproto::*;
16
use x11rb::rust_connection::RustConnection;
17
18
pub type TagMask = u32;
19
pub fn tag_mask(tag: usize) -> TagMask {
20
    1 << tag
21
}
22
23
struct AtomCache {
24
    net_current_desktop: Atom,
25
    net_client_info: Atom,
26
    wm_state: Atom,
27
}
28
29
impl AtomCache {
30
    fn new(connection: &RustConnection) -> WmResult<Self> {
31
        let net_current_desktop = connection
32
            .intern_atom(false, b"_NET_CURRENT_DESKTOP")?
33
            .reply()?
34
            .atom;
35
36
        let net_client_info = connection
37
            .intern_atom(false, b"_NET_CLIENT_INFO")?
38
            .reply()?
39
            .atom;
40
41
        let wm_state = connection.intern_atom(false, b"WM_STATE")?.reply()?.atom;
42
43
        Ok(Self {
44
            net_current_desktop,
45
            net_client_info,
46
            wm_state,
47
        })
48
    }
49
}
50
51
pub struct WindowManager {
52
    config: Config,
53
    connection: RustConnection,
54
    screen_number: usize,
55
    root: Window,
56
    screen: Screen,
57
    windows: Vec<Window>,
58
    layout: LayoutBox,
59
    window_tags: std::collections::HashMap<Window, TagMask>,
60
    window_monitor: std::collections::HashMap<Window, usize>,
61
    window_geometries: std::collections::HashMap<Window, (i16, i16, u16, u16)>,
62
    gaps_enabled: bool,
63
    floating_windows: HashSet<Window>,
64
    bars: Vec<Bar>,
65
    monitors: Vec<Monitor>,
66
    selected_monitor: usize,
67
    atoms: AtomCache,
68
    previous_focused: Option<Window>,
69
    display: *mut x11::xlib::Display,
70
    font: crate::bar::font::Font,
71
    keychord_state: keyboard::handlers::KeychordState,
72
}
73
74
type WmResult<T> = Result<T, WmError>;
75
76
impl WindowManager {
77
    pub fn new(config: Config) -> WmResult<Self> {
78
        let (connection, screen_number) = x11rb::connect(None)?;
79
        let root = connection.setup().roots[screen_number].root;
80
        let screen = connection.setup().roots[screen_number].clone();
81
82
        let normal_cursor = CursorHandle::new(
83
            &connection,
84
            screen_number,
85
            &x11rb::resource_manager::new_from_default(&connection)?,
86
        )?
87
        .reply()?
88
        .load_cursor(&connection, "left_ptr")?;
89
90
        connection
91
            .change_window_attributes(
92
                root,
93
                &ChangeWindowAttributesAux::new()
94
                    .cursor(normal_cursor)
95
                    .event_mask(
96
                        EventMask::SUBSTRUCTURE_REDIRECT
97
                            | EventMask::SUBSTRUCTURE_NOTIFY
98
                            | EventMask::PROPERTY_CHANGE
99
                            | EventMask::KEY_PRESS
100
                            | EventMask::BUTTON_PRESS
101
                            | EventMask::POINTER_MOTION,
102
                    ),
103
            )?
104
            .check()?;
105
106
        connection.grab_button(
107
            false,
108
            root,
109
            EventMask::BUTTON_PRESS | EventMask::BUTTON_RELEASE,
110
            GrabMode::SYNC,
111
            GrabMode::ASYNC,
112
            x11rb::NONE,
113
            x11rb::NONE,
114
            ButtonIndex::M1,
115
            u16::from(config.modkey).into(),
116
        )?;
117
118
        connection.grab_button(
119
            false,
120
            root,
121
            EventMask::BUTTON_PRESS | EventMask::BUTTON_RELEASE,
122
            GrabMode::SYNC,
123
            GrabMode::ASYNC,
124
            x11rb::NONE,
125
            x11rb::NONE,
126
            ButtonIndex::M3,
127
            u16::from(config.modkey).into(),
128
        )?;
129
130
        let monitors = detect_monitors(&connection, &screen, root)?;
131
132
        let display = unsafe { x11::xlib::XOpenDisplay(std::ptr::null()) };
133
        if display.is_null() {
134
            return Err(WmError::X11(crate::errors::X11Error::DisplayOpenFailed));
135
        }
136
137
        let font = crate::bar::font::Font::new(display, screen_number as i32, &config.font)?;
138
139
        let mut bars = Vec::new();
140
        for monitor in monitors.iter() {
141
            let bar = Bar::new(
142
                &connection,
143
                &screen,
144
                screen_number,
145
                &config,
146
                display,
147
                &font,
148
                monitor.x as i16,
149
                monitor.y as i16,
150
                monitor.width as u16,
151
            )?;
152
            bars.push(bar);
153
        }
154
155
        let gaps_enabled = config.gaps_enabled;
156
157
        let atoms = AtomCache::new(&connection)?;
158
159
        let mut window_manager = Self {
160
            config,
161
            connection,
162
            screen_number,
163
            root,
164
            screen,
165
            windows: Vec::new(),
166
            layout: Box::new(TilingLayout),
167
            window_tags: std::collections::HashMap::new(),
168
            window_monitor: std::collections::HashMap::new(),
169
            window_geometries: std::collections::HashMap::new(),
170
            gaps_enabled,
171
            floating_windows: HashSet::new(),
172
            bars,
173
            monitors,
174
            selected_monitor: 0,
175
            atoms,
176
            previous_focused: None,
177
            display,
178
            font,
179
            keychord_state: keyboard::handlers::KeychordState::Idle,
180
        };
181
182
        window_manager.scan_existing_windows()?;
183
        window_manager.update_bar()?;
184
        window_manager.run_autostart_commands()?;
185
186
        Ok(window_manager)
187
    }
188
189
    /**
190
     *
191
     * This function is deprecated for now, but will potentially be used in the future.
192
     *
193
     */
194
    fn _get_saved_selected_tags(
195
        connection: &RustConnection,
196
        root: Window,
197
        tag_count: usize,
198
    ) -> WmResult<TagMask> {
199
        let net_current_desktop = connection
200
            .intern_atom(false, b"_NET_CURRENT_DESKTOP")?
201
            .reply()?
202
            .atom;
203
204
        match connection
205
            .get_property(false, root, net_current_desktop, AtomEnum::CARDINAL, 0, 1)?
206
            .reply()
207
        {
208
            Ok(prop) if prop.value.len() >= 4 => {
209
                let desktop = u32::from_ne_bytes([
210
                    prop.value[0],
211
                    prop.value[1],
212
                    prop.value[2],
213
                    prop.value[3],
214
                ]);
215
                if desktop < tag_count as u32 {
216
                    let mask = tag_mask(desktop as usize);
217
                    return Ok(mask);
218
                }
219
            }
220
            Ok(_) => {}
221
            Err(e) => {
222
                eprintln!("No _NET_CURRENT_DESKTOP property ({})", e);
223
            }
224
        }
225
        Ok(tag_mask(0))
226
    }
227
228
    fn scan_existing_windows(&mut self) -> WmResult<()> {
229
        let tree = self.connection.query_tree(self.root)?.reply()?;
230
        let net_client_info = self.atoms.net_client_info;
231
        let wm_state_atom = self.atoms.wm_state;
232
233
        for &window in &tree.children {
234
            if self.bars.iter().any(|bar| bar.window() == window) {
235
                continue;
236
            }
237
238
            let Ok(attrs) = self.connection.get_window_attributes(window)?.reply() else {
239
                continue;
240
            };
241
242
            if attrs.override_redirect {
243
                continue;
244
            }
245
246
            if attrs.map_state == MapState::VIEWABLE {
247
                let tag = self.get_saved_tag(window, net_client_info)?;
248
                self.windows.push(window);
249
                self.window_tags.insert(window, tag);
250
                self.window_monitor.insert(window, self.selected_monitor);
251
                continue;
252
            }
253
254
            if attrs.map_state == MapState::UNMAPPED {
255
                let has_wm_state = self
256
                    .connection
257
                    .get_property(false, window, wm_state_atom, AtomEnum::ANY, 0, 2)?
258
                    .reply()
259
                    .is_ok_and(|prop| !prop.value.is_empty());
260
261
                if !has_wm_state {
262
                    continue;
263
                }
264
265
                let has_wm_class = self
266
                    .connection
267
                    .get_property(false, window, AtomEnum::WM_CLASS, AtomEnum::STRING, 0, 1024)?
268
                    .reply()
269
                    .is_ok_and(|prop| !prop.value.is_empty());
270
271
                if has_wm_class {
272
                    let tag = self.get_saved_tag(window, net_client_info)?;
273
                    self.connection.map_window(window)?;
274
                    self.windows.push(window);
275
                    self.window_tags.insert(window, tag);
276
                    self.window_monitor.insert(window, self.selected_monitor);
277
                }
278
            }
279
        }
280
281
        if let Some(&first) = self.windows.first() {
282
            self.set_focus(first)?;
283
        }
284
285
        self.apply_layout()?;
286
        Ok(())
287
    }
288
289
    fn get_saved_tag(&self, window: Window, net_client_info: Atom) -> WmResult<TagMask> {
290
        match self
291
            .connection
292
            .get_property(false, window, net_client_info, AtomEnum::CARDINAL, 0, 2)?
293
            .reply()
294
        {
295
            Ok(prop) if prop.value.len() >= 4 => {
296
                let tags = u32::from_ne_bytes([
297
                    prop.value[0],
298
                    prop.value[1],
299
                    prop.value[2],
300
                    prop.value[3],
301
                ]);
302
303
                if tags != 0 && tags < (1 << self.config.tags.len()) {
304
                    return Ok(tags);
305
                }
306
            }
307
            Ok(_) => {}
308
            Err(e) => {
309
                eprintln!("No _NET_CLIENT_INFO property ({})", e);
310
            }
311
        }
312
313
        Ok(self
314
            .monitors
315
            .get(self.selected_monitor)
316
            .map(|m| m.selected_tags)
317
            .unwrap_or(tag_mask(0)))
318
    }
319
320
    fn save_client_tag(&self, window: Window, tag: TagMask) -> WmResult<()> {
321
        let net_client_info = self.atoms.net_client_info;
322
323
        let bytes = tag.to_ne_bytes().to_vec();
324
325
        self.connection.change_property(
326
            PropMode::REPLACE,
327
            window,
328
            net_client_info,
329
            AtomEnum::CARDINAL,
330
            32,
331
            1,
332
            &bytes,
333
        )?;
334
335
        self.connection.flush()?;
336
        Ok(())
337
    }
338
339
    fn set_wm_state(&self, window: Window, state: u32) -> WmResult<()> {
340
        let wm_state_atom = self.atoms.wm_state;
341
342
        let data = [state, 0u32];
343
        let bytes: Vec<u8> = data.iter().flat_map(|&v| v.to_ne_bytes()).collect();
344
345
        self.connection.change_property(
346
            PropMode::REPLACE,
347
            window,
348
            wm_state_atom,
349
            wm_state_atom,
350
            32,
351
            2,
352
            &bytes,
353
        )?;
354
355
        self.connection.flush()?;
356
        Ok(())
357
    }
358
359
    pub fn run(&mut self) -> WmResult<bool> {
360
        println!("oxwm started on display {}", self.screen_number);
361
362
        keyboard::setup_keybinds(&self.connection, self.root, &self.config.keybindings)?;
363
        self.update_bar()?;
364
365
        loop {
366
            while let Some(event) = self.connection.poll_for_event()? {
367
                if let Some(should_restart) = self.handle_event(event)? {
368
                    return Ok(should_restart);
369
                }
370
            }
371
372
            if let Some(bar) = self.bars.get_mut(self.selected_monitor) {
373
                bar.update_blocks();
374
            }
375
376
            if self.bars.iter().any(|bar| bar.needs_redraw()) {
377
                self.update_bar()?;
378
            }
379
380
            std::thread::sleep(std::time::Duration::from_millis(100));
381
        }
382
    }
383
384
    fn toggle_floating(&mut self) -> WmResult<()> {
385
        if let Some(focused) = self
386
            .monitors
387
            .get(self.selected_monitor)
388
            .and_then(|m| m.focused_window)
389
        {
390
            if self.floating_windows.contains(&focused) {
391
                self.floating_windows.remove(&focused);
392
393
                let selected_tags = self
394
                    .monitors
395
                    .get(self.selected_monitor)
396
                    .map(|m| m.selected_tags)
397
                    .unwrap_or(tag_mask(0));
398
399
                self.window_tags.insert(focused, selected_tags);
400
                self.window_monitor.insert(focused, self.selected_monitor);
401
                let _ = self.save_client_tag(focused, selected_tags);
402
403
                self.apply_layout()?;
404
            } else {
405
                let float_width = (self.screen.width_in_pixels / 2) as u32;
406
                let float_height = (self.screen.height_in_pixels / 2) as u32;
407
408
                let border_width = self.config.border_width;
409
410
                let center_width = ((self.screen.width_in_pixels - float_width as u16) / 2) as i32;
411
                let center_height =
412
                    ((self.screen.height_in_pixels - float_height as u16) / 2) as i32;
413
414
                self.connection.configure_window(
415
                    focused,
416
                    &ConfigureWindowAux::new()
417
                        .x(center_width)
418
                        .y(center_height)
419
                        .width(float_width)
420
                        .height(float_height)
421
                        .border_width(border_width)
422
                        .stack_mode(StackMode::ABOVE),
423
                )?;
424
425
                self.floating_windows.insert(focused);
426
                self.apply_layout()?;
427
                self.connection.flush()?;
428
            }
429
        }
430
        Ok(())
431
    }
432
433
    fn smart_move_window(&mut self, direction: i32) -> WmResult<()> {
434
        let focused = match self
435
            .monitors
436
            .get(self.selected_monitor)
437
            .and_then(|m| m.focused_window)
438
        {
439
            Some(win) => win,
440
            None => return Ok(()),
441
        };
442
443
        if !self.floating_windows.contains(&focused) {
444
            let float_width = (self.screen.width_in_pixels / 2) as u32;
445
            let float_height = (self.screen.height_in_pixels / 2) as u32;
446
            let border_width = self.config.border_width;
447
            let center_width = ((self.screen.width_in_pixels - float_width as u16) / 2) as i32;
448
            let center_height = ((self.screen.height_in_pixels - float_height as u16) / 2) as i32;
449
450
            self.connection.configure_window(
451
                focused,
452
                &ConfigureWindowAux::new()
453
                    .x(center_width)
454
                    .y(center_height)
455
                    .width(float_width)
456
                    .height(float_height)
457
                    .border_width(border_width)
458
                    .stack_mode(StackMode::ABOVE),
459
            )?;
460
            self.floating_windows.insert(focused);
461
        }
462
463
        let current_geom = match self.connection.get_geometry(focused)?.reply() {
464
            Ok(geom) => geom,
465
            Err(_) => return Ok(()),
466
        };
467
468
        let c_x = current_geom.x as i32;
469
        let c_y = current_geom.y as i32;
470
        let c_width = current_geom.width as i32;
471
        let c_height = current_geom.height as i32;
472
473
        let monitor = match self.monitors.get(self.selected_monitor) {
474
            Some(m) => m,
475
            None => return Ok(()),
476
        };
477
478
        let (gap_ih, gap_iv, gap_oh, gap_ov) = if self.gaps_enabled {
479
            (
480
                self.config.gap_inner_horizontal as i32,
481
                self.config.gap_inner_vertical as i32,
482
                self.config.gap_outer_horizontal as i32,
483
                self.config.gap_outer_vertical as i32,
484
            )
485
        } else {
486
            (0, 0, 0, 0)
487
        };
488
489
        let (new_x, new_y) = match direction {
490
            0 => {
491
                // UP
492
                let mut target = i32::MIN;
493
                let top = c_y;
494
                let mut ny = c_y - (monitor.height as i32 / 4);
495
496
                for &other_window in &self.windows {
497
                    if other_window == focused {
498
                        continue;
499
                    }
500
                    if !self.floating_windows.contains(&other_window) {
501
                        continue;
502
                    }
503
                    if !self.is_window_visible(other_window) {
504
                        continue;
505
                    }
506
                    let other_mon = self.window_monitor.get(&other_window).copied().unwrap_or(0);
507
                    if other_mon != self.selected_monitor {
508
                        continue;
509
                    }
510
511
                    let other_geom = match self.connection.get_geometry(other_window)?.reply() {
512
                        Ok(geom) => geom,
513
                        Err(_) => continue,
514
                    };
515
516
                    let o_x = other_geom.x as i32;
517
                    let o_y = other_geom.y as i32;
518
                    let o_width = other_geom.width as i32;
519
                    let o_height = other_geom.height as i32;
520
521
                    if c_x + c_width < o_x || c_x > o_x + o_width {
522
                        continue;
523
                    }
524
525
                    let bottom = o_y + o_height + gap_iv;
526
                    if top > bottom && ny < bottom {
527
                        target = target.max(bottom);
528
                    }
529
                }
530
531
                if target != i32::MIN {
532
                    ny = target;
533
                }
534
                ny = ny.max(monitor.y as i32 + gap_ov);
535
                (c_x, ny)
536
            }
537
            1 => {
538
                // DOWN
539
                let mut target = i32::MAX;
540
                let bottom = c_y + c_height;
541
                let mut ny = c_y + (monitor.height as i32 / 4);
542
543
                for &other_window in &self.windows {
544
                    if other_window == focused {
545
                        continue;
546
                    }
547
                    if !self.floating_windows.contains(&other_window) {
548
                        continue;
549
                    }
550
                    if !self.is_window_visible(other_window) {
551
                        continue;
552
                    }
553
                    let other_mon = self.window_monitor.get(&other_window).copied().unwrap_or(0);
554
                    if other_mon != self.selected_monitor {
555
                        continue;
556
                    }
557
558
                    let other_geom = match self.connection.get_geometry(other_window)?.reply() {
559
                        Ok(geom) => geom,
560
                        Err(_) => continue,
561
                    };
562
563
                    let o_x = other_geom.x as i32;
564
                    let o_y = other_geom.y as i32;
565
                    let o_width = other_geom.width as i32;
566
567
                    if c_x + c_width < o_x || c_x > o_x + o_width {
568
                        continue;
569
                    }
570
571
                    let top = o_y - gap_iv;
572
                    if bottom < top && (ny + c_height) > top {
573
                        target = target.min(top - c_height);
574
                    }
575
                }
576
577
                if target != i32::MAX {
578
                    ny = target;
579
                }
580
                ny = ny.min(monitor.y as i32 + monitor.height as i32 - c_height - gap_ov);
581
                (c_x, ny)
582
            }
583
            2 => {
584
                // LEFT
585
                let mut target = i32::MIN;
586
                let left = c_x;
587
                let mut nx = c_x - (monitor.width as i32 / 6);
588
589
                for &other_window in &self.windows {
590
                    if other_window == focused {
591
                        continue;
592
                    }
593
                    if !self.floating_windows.contains(&other_window) {
594
                        continue;
595
                    }
596
                    if !self.is_window_visible(other_window) {
597
                        continue;
598
                    }
599
                    let other_mon = self.window_monitor.get(&other_window).copied().unwrap_or(0);
600
                    if other_mon != self.selected_monitor {
601
                        continue;
602
                    }
603
604
                    let other_geom = match self.connection.get_geometry(other_window)?.reply() {
605
                        Ok(geom) => geom,
606
                        Err(_) => continue,
607
                    };
608
609
                    let o_x = other_geom.x as i32;
610
                    let o_y = other_geom.y as i32;
611
                    let o_width = other_geom.width as i32;
612
                    let o_height = other_geom.height as i32;
613
614
                    if c_y + c_height < o_y || c_y > o_y + o_height {
615
                        continue;
616
                    }
617
618
                    let right = o_x + o_width + gap_ih;
619
                    if left > right && nx < right {
620
                        target = target.max(right);
621
                    }
622
                }
623
624
                if target != i32::MIN {
625
                    nx = target;
626
                }
627
                nx = nx.max(monitor.x as i32 + gap_oh);
628
                (nx, c_y)
629
            }
630
            3 => {
631
                // RIGHT
632
                let mut target = i32::MAX;
633
                let right = c_x + c_width;
634
                let mut nx = c_x + (monitor.width as i32 / 6);
635
636
                for &other_window in &self.windows {
637
                    if other_window == focused {
638
                        continue;
639
                    }
640
                    if !self.floating_windows.contains(&other_window) {
641
                        continue;
642
                    }
643
                    if !self.is_window_visible(other_window) {
644
                        continue;
645
                    }
646
                    let other_mon = self.window_monitor.get(&other_window).copied().unwrap_or(0);
647
                    if other_mon != self.selected_monitor {
648
                        continue;
649
                    }
650
651
                    let other_geom = match self.connection.get_geometry(other_window)?.reply() {
652
                        Ok(geom) => geom,
653
                        Err(_) => continue,
654
                    };
655
656
                    let o_x = other_geom.x as i32;
657
                    let o_y = other_geom.y as i32;
658
                    let o_height = other_geom.height as i32;
659
660
                    if c_y + c_height < o_y || c_y > o_y + o_height {
661
                        continue;
662
                    }
663
664
                    let left = o_x - gap_ih;
665
                    if right < left && (nx + c_width) > left {
666
                        target = target.min(left - c_width);
667
                    }
668
                }
669
670
                if target != i32::MAX {
671
                    nx = target;
672
                }
673
                nx = nx.min(monitor.x as i32 + monitor.width as i32 - c_width - gap_oh);
674
                (nx, c_y)
675
            }
676
            _ => return Ok(()),
677
        };
678
679
        self.connection.configure_window(
680
            focused,
681
            &ConfigureWindowAux::new()
682
                .x(new_x)
683
                .y(new_y)
684
                .stack_mode(StackMode::ABOVE),
685
        )?;
686
687
        self.connection.flush()?;
688
        Ok(())
689
    }
690
691
    fn exchange_client(&mut self, direction: i32) -> WmResult<()> {
692
        let focused = match self
693
            .monitors
694
            .get(self.selected_monitor)
695
            .and_then(|m| m.focused_window)
696
        {
697
            Some(win) => win,
698
            None => return Ok(()),
699
        };
700
701
        if self.floating_windows.contains(&focused) {
702
            return Ok(());
703
        }
704
705
        let visible = self.visible_windows();
706
        if visible.len() < 2 {
707
            return Ok(());
708
        }
709
710
        let current_idx = match visible.iter().position(|&w| w == focused) {
711
            Some(idx) => idx,
712
            None => return Ok(()),
713
        };
714
715
        let target_idx = match direction {
716
            0 | 2 => {
717
                // UP or LEFT - previous in stack
718
                if current_idx == 0 {
719
                    visible.len() - 1
720
                } else {
721
                    current_idx - 1
722
                }
723
            }
724
            1 | 3 => {
725
                // DOWN or RIGHT - next in stack
726
                (current_idx + 1) % visible.len()
727
            }
728
            _ => return Ok(()),
729
        };
730
731
        let target = visible[target_idx];
732
733
        let focused_pos = self.windows.iter().position(|&w| w == focused);
734
        let target_pos = self.windows.iter().position(|&w| w == target);
735
736
        if let (Some(f_pos), Some(t_pos)) = (focused_pos, target_pos) {
737
            self.windows.swap(f_pos, t_pos);
738
739
            self.apply_layout()?;
740
741
            self.set_focus(focused)?;
742
743
            if let Ok(geometry) = self.connection.get_geometry(focused)?.reply() {
744
                self.connection.warp_pointer(
745
                    x11rb::NONE,
746
                    focused,
747
                    0,
748
                    0,
749
                    0,
750
                    0,
751
                    geometry.width as i16 / 2,
752
                    geometry.height as i16 / 2,
753
                )?;
754
            }
755
        }
756
757
        Ok(())
758
    }
759
760
    fn toggle_fullscreen(&mut self) -> WmResult<()> {
761
        if let Some(monitor) = self.monitors.get_mut(self.selected_monitor) {
762
            monitor.fullscreen_enabled = !monitor.fullscreen_enabled;
763
764
            if let Some(bar) = self.bars.get(self.selected_monitor) {
765
                if monitor.fullscreen_enabled {
766
                    self.connection.unmap_window(bar.window())?;
767
                } else {
768
                    self.connection.map_window(bar.window())?;
769
                }
770
            }
771
772
            self.apply_layout()?;
773
        }
774
        Ok(())
775
    }
776
777
    fn get_layout_symbol(&self) -> String {
778
        let layout_name = self.layout.name();
779
        self.config
780
            .layout_symbols
781
            .iter()
782
            .find(|l| l.name == layout_name)
783
            .map(|l| l.symbol.clone())
784
            .unwrap_or_else(|| self.layout.symbol().to_string())
785
    }
786
787
    fn get_keychord_indicator(&self) -> Option<String> {
788
        match &self.keychord_state {
789
            keyboard::handlers::KeychordState::Idle => None,
790
            keyboard::handlers::KeychordState::InProgress {
791
                candidates,
792
                keys_pressed,
793
            } => {
794
                if candidates.is_empty() {
795
                    return None;
796
                }
797
798
                let binding = &self.config.keybindings[candidates[0]];
799
                let mut indicator = String::new();
800
801
                for (i, key_press) in binding.keys.iter().take(*keys_pressed).enumerate() {
802
                    if i > 0 {
803
                        indicator.push(' ');
804
                    }
805
806
                    for modifier in &key_press.modifiers {
807
                        indicator.push_str(Self::format_modifier(*modifier));
808
                        indicator.push('+');
809
                    }
810
811
                    indicator.push_str(&self.format_keysym(key_press.keysym));
812
                }
813
814
                indicator.push('-');
815
                Some(indicator)
816
            }
817
        }
818
    }
819
820
    fn format_modifier(modifier: KeyButMask) -> &'static str {
821
        match modifier {
822
            KeyButMask::MOD1 => "Alt",
823
            KeyButMask::MOD4 => "Super",
824
            KeyButMask::SHIFT => "Shift",
825
            KeyButMask::CONTROL => "Ctrl",
826
            _ => "Mod",
827
        }
828
    }
829
830
    fn format_keysym(&self, keysym: keyboard::keysyms::Keysym) -> String {
831
        use keyboard::keysyms::*;
832
833
        match keysym {
834
            XK_RETURN => "Return",
835
            XK_ESCAPE => "Esc",
836
            XK_SPACE => "Space",
837
            XK_TAB => "Tab",
838
            XK_BACKSPACE => "Backspace",
839
            XK_DELETE => "Del",
840
            XK_F1 => "F1",
841
            XK_F2 => "F2",
842
            XK_F3 => "F3",
843
            XK_F4 => "F4",
844
            XK_F5 => "F5",
845
            XK_F6 => "F6",
846
            XK_F7 => "F7",
847
            XK_F8 => "F8",
848
            XK_F9 => "F9",
849
            XK_F10 => "F10",
850
            XK_F11 => "F11",
851
            XK_F12 => "F12",
852
            XK_A..=XK_Z | XK_0..=XK_9 => {
853
                return char::from_u32(keysym).unwrap_or('?').to_string();
854
            }
855
            XK_LEFT => "Left",
856
            XK_RIGHT => "Right",
857
            XK_UP => "Up",
858
            XK_DOWN => "Down",
859
            XK_HOME => "Home",
860
            XK_END => "End",
861
            XK_PAGE_UP => "PgUp",
862
            XK_PAGE_DOWN => "PgDn",
863
            XK_INSERT => "Ins",
864
            XK_MINUS => "-",
865
            XK_EQUAL => "=",
866
            XK_LEFT_BRACKET => "[",
867
            XK_RIGHT_BRACKET => "]",
868
            XK_SEMICOLON => ";",
869
            XK_APOSTROPHE => "'",
870
            XK_GRAVE => "`",
871
            XK_BACKSLASH => "\\",
872
            XK_COMMA => ",",
873
            XK_PERIOD => ".",
874
            XK_SLASH => "/",
875
            XF86_AUDIO_RAISE_VOLUME => "Vol+",
876
            XF86_AUDIO_LOWER_VOLUME => "Vol-",
877
            XF86_AUDIO_MUTE => "Mute",
878
            XF86_MON_BRIGHTNESS_UP => "Bri+",
879
            XF86_MON_BRIGHTNESS_DOWN => "Bri-",
880
            _ => "?",
881
        }
882
        .to_string()
883
    }
884
885
    fn update_bar(&mut self) -> WmResult<()> {
886
        let layout_symbol = self.get_layout_symbol();
887
        let keychord_indicator = self.get_keychord_indicator();
888
889
        for (monitor_index, monitor) in self.monitors.iter().enumerate() {
890
            if let Some(bar) = self.bars.get_mut(monitor_index) {
891
                let mut occupied_tags: TagMask = 0;
892
                for (&window, &tags) in &self.window_tags {
893
                    if self.window_monitor.get(&window).copied().unwrap_or(0) == monitor_index {
894
                        occupied_tags |= tags;
895
                    }
896
                }
897
898
                let draw_blocks = monitor_index == self.selected_monitor;
899
                bar.invalidate();
900
                bar.draw(
901
                    &self.connection,
902
                    &self.font,
903
                    self.display,
904
                    monitor.selected_tags,
905
                    occupied_tags,
906
                    draw_blocks,
907
                    &layout_symbol,
908
                    keychord_indicator.as_deref(),
909
                )?;
910
            }
911
        }
912
        Ok(())
913
    }
914
915
    fn handle_key_action(&mut self, action: KeyAction, arg: &Arg) -> WmResult<()> {
916
        match action {
917
            KeyAction::Spawn => handlers::handle_spawn_action(action, arg)?,
918
            KeyAction::KillClient => {
919
                if let Some(focused) = self
920
                    .monitors
921
                    .get(self.selected_monitor)
922
                    .and_then(|m| m.focused_window)
923
                {
924
                    match self.connection.kill_client(focused) {
925
                        Ok(_) => {
926
                            self.connection.flush()?;
927
                        }
928
                        Err(e) => {
929
                            eprintln!("Failed to kill window {}: {}", focused, e);
930
                        }
931
                    }
932
                }
933
            }
934
            KeyAction::ToggleFullScreen => {
935
                self.toggle_fullscreen()?;
936
            }
937
            KeyAction::ChangeLayout => {
938
                if let Arg::Str(layout_name) = arg {
939
                    match layout_from_str(layout_name) {
940
                        Ok(layout) => {
941
                            self.layout = layout;
942
                            self.apply_layout()?;
943
                            self.update_bar()?;
944
                        }
945
                        Err(e) => eprintln!("Failed to change layout: {}", e),
946
                    }
947
                }
948
            }
949
            KeyAction::CycleLayout => {
950
                let current_name = self.layout.name();
951
                let next_name = next_layout(current_name);
952
                match layout_from_str(next_name) {
953
                    Ok(layout) => {
954
                        self.layout = layout;
955
                        self.apply_layout()?;
956
                        self.update_bar()?;
957
                    }
958
                    Err(e) => eprintln!("Failed to cycle layout: {}", e),
959
                }
960
            }
961
            KeyAction::ToggleFloating => {
962
                self.toggle_floating()?;
963
            }
964
965
            KeyAction::SmartMoveWin => {
966
                if let Arg::Int(direction) = arg {
967
                    self.smart_move_window(*direction)?;
968
                }
969
            }
970
971
            KeyAction::ExchangeClient => {
972
                if let Arg::Int(direction) = arg {
973
                    self.exchange_client(*direction)?;
974
                }
975
            }
976
977
            KeyAction::FocusStack => {
978
                if let Arg::Int(direction) = arg {
979
                    self.cycle_focus(*direction)?;
980
                }
981
            }
982
            KeyAction::FocusDirection => {
983
                if let Arg::Int(direction) = arg {
984
                    self.focus_direction(*direction)?;
985
                }
986
            }
987
            KeyAction::SwapDirection => {
988
                if let Arg::Int(direction) = arg {
989
                    self.swap_direction(*direction)?;
990
                }
991
            }
992
            KeyAction::Quit | KeyAction::Restart => {
993
                // Handled in handle_event
994
            }
995
            KeyAction::Recompile => {
996
                match std::process::Command::new("oxwm")
997
                    .arg("--recompile")
998
                    .spawn()
999
                {
1000
                    Ok(_) => eprintln!("Recompiling in background"),
1001
                    Err(e) => eprintln!("Failed to spawn recompile: {}", e),
1002
                }
1003
            }
1004
            KeyAction::ViewTag => {
1005
                if let Arg::Int(tag_index) = arg {
1006
                    self.view_tag(*tag_index as usize)?;
1007
                }
1008
            }
1009
            KeyAction::MoveToTag => {
1010
                if let Arg::Int(tag_index) = arg {
1011
                    self.move_to_tag(*tag_index as usize)?;
1012
                }
1013
            }
1014
            KeyAction::ToggleGaps => {
1015
                self.gaps_enabled = !self.gaps_enabled;
1016
                self.apply_layout()?;
1017
            }
1018
            KeyAction::FocusMonitor => {
1019
                if let Arg::Int(direction) = arg {
1020
                    self.focus_monitor(*direction)?;
1021
                }
1022
            }
1023
            KeyAction::None => {}
1024
        }
1025
        Ok(())
1026
    }
1027
1028
    fn focus_monitor(&mut self, direction: i32) -> WmResult<()> {
1029
        if self.monitors.is_empty() {
1030
            return Ok(());
1031
        }
1032
1033
        let new_monitor = if direction > 0 {
1034
            (self.selected_monitor + 1) % self.monitors.len()
1035
        } else {
1036
            (self.selected_monitor + self.monitors.len() - 1) % self.monitors.len()
1037
        };
1038
1039
        if new_monitor == self.selected_monitor {
1040
            return Ok(());
1041
        }
1042
1043
        self.selected_monitor = new_monitor;
1044
1045
        self.update_bar()?;
1046
1047
        let visible = self.visible_windows_on_monitor(new_monitor);
1048
        if let Some(&win) = visible.first() {
1049
            self.set_focus(win)?;
1050
        }
1051
1052
        Ok(())
1053
    }
1054
1055
    fn is_window_visible(&self, window: Window) -> bool {
1056
        let window_mon = self.window_monitor.get(&window).copied().unwrap_or(0);
1057
1058
        if let Some(&tags) = self.window_tags.get(&window) {
1059
            let monitor = self.monitors.get(window_mon);
1060
            let selected_tags = monitor.map(|m| m.selected_tags).unwrap_or(0);
1061
            (tags & selected_tags) != 0
1062
        } else {
1063
            false
1064
        }
1065
    }
1066
1067
    fn visible_windows(&self) -> Vec<Window> {
1068
        self.windows
1069
            .iter()
1070
            .filter(|&&w| self.is_window_visible(w))
1071
            .copied()
1072
            .collect()
1073
    }
1074
1075
    fn visible_windows_on_monitor(&self, monitor_index: usize) -> Vec<Window> {
1076
        self.windows
1077
            .iter()
1078
            .filter(|&&w| {
1079
                let window_mon = self.window_monitor.get(&w).copied().unwrap_or(0);
1080
                if window_mon != monitor_index {
1081
                    return false;
1082
                }
1083
                if let Some(&tags) = self.window_tags.get(&w) {
1084
                    let monitor = self.monitors.get(monitor_index);
1085
                    let selected_tags = monitor.map(|m| m.selected_tags).unwrap_or(0);
1086
                    (tags & selected_tags) != 0
1087
                } else {
1088
                    false
1089
                }
1090
            })
1091
            .copied()
1092
            .collect()
1093
    }
1094
1095
    fn get_monitor_at_point(&self, x: i32, y: i32) -> Option<usize> {
1096
        self.monitors
1097
            .iter()
1098
            .position(|mon| mon.contains_point(x, y))
1099
    }
1100
1101
    // Dwm's g-loaded approach to handling the spam alternating crash.
1102
    fn update_window_visibility(&self) -> WmResult<()> {
1103
        for &window in &self.windows {
1104
            if !self.is_window_visible(window) {
1105
                if let Ok(geom) = self.connection.get_geometry(window)?.reply() {
1106
                    self.connection.configure_window(
1107
                        window,
1108
                        &ConfigureWindowAux::new()
1109
                            .x(-(geom.width as i32 * 2))
1110
                            .y(geom.y as i32),
1111
                    )?;
1112
                }
1113
            }
1114
        }
1115
        self.connection.flush()?;
1116
        Ok(())
1117
    }
1118
1119
    pub fn view_tag(&mut self, tag_index: usize) -> WmResult<()> {
1120
        if tag_index >= self.config.tags.len() {
1121
            return Ok(());
1122
        }
1123
1124
        if let Some(monitor) = self.monitors.get_mut(self.selected_monitor) {
1125
            monitor.selected_tags = tag_mask(tag_index);
1126
        }
1127
1128
        self.save_selected_tags()?;
1129
        self.update_window_visibility()?;
1130
        self.apply_layout()?;
1131
        self.update_bar()?;
1132
1133
        let visible = self.visible_windows_on_monitor(self.selected_monitor);
1134
        if let Some(&win) = visible.first() {
1135
            self.set_focus(win)?;
1136
        }
1137
1138
        Ok(())
1139
    }
1140
1141
    fn save_selected_tags(&self) -> WmResult<()> {
1142
        let net_current_desktop = self.atoms.net_current_desktop;
1143
1144
        let selected_tags = self
1145
            .monitors
1146
            .get(self.selected_monitor)
1147
            .map(|m| m.selected_tags)
1148
            .unwrap_or(tag_mask(0));
1149
        let desktop = selected_tags.trailing_zeros();
1150
1151
        let bytes = (desktop as u32).to_ne_bytes();
1152
        self.connection.change_property(
1153
            PropMode::REPLACE,
1154
            self.root,
1155
            net_current_desktop,
1156
            AtomEnum::CARDINAL,
1157
            32,
1158
            1,
1159
            &bytes,
1160
        )?;
1161
1162
        self.connection.flush()?;
1163
        Ok(())
1164
    }
1165
1166
    pub fn move_to_tag(&mut self, tag_index: usize) -> WmResult<()> {
1167
        if tag_index >= self.config.tags.len() {
1168
            return Ok(());
1169
        }
1170
1171
        if let Some(focused) = self
1172
            .monitors
1173
            .get(self.selected_monitor)
1174
            .and_then(|m| m.focused_window)
1175
        {
1176
            let mask = tag_mask(tag_index);
1177
            self.window_tags.insert(focused, mask);
1178
1179
            let _ = self.save_client_tag(focused, mask);
1180
1181
            self.update_window_visibility()?;
1182
            self.apply_layout()?;
1183
            self.update_bar()?;
1184
        }
1185
1186
        Ok(())
1187
    }
1188
1189
    pub fn cycle_focus(&mut self, direction: i32) -> WmResult<()> {
1190
        let visible = self.visible_windows();
1191
1192
        if visible.is_empty() {
1193
            return Ok(());
1194
        }
1195
1196
        let current = self
1197
            .monitors
1198
            .get(self.selected_monitor)
1199
            .and_then(|m| m.focused_window);
1200
1201
        let next_window = if let Some(current) = current {
1202
            if let Some(current_index) = visible.iter().position(|&w| w == current) {
1203
                let next_index = if direction > 0 {
1204
                    (current_index + 1) % visible.len()
1205
                } else {
1206
                    (current_index + visible.len() - 1) % visible.len()
1207
                };
1208
                visible[next_index]
1209
            } else {
1210
                visible[0]
1211
            }
1212
        } else {
1213
            visible[0]
1214
        };
1215
1216
        self.set_focus(next_window)?;
1217
        Ok(())
1218
    }
1219
1220
    pub fn focus_direction(&mut self, direction: i32) -> WmResult<()> {
1221
        let focused = match self
1222
            .monitors
1223
            .get(self.selected_monitor)
1224
            .and_then(|m| m.focused_window)
1225
        {
1226
            Some(win) => win,
1227
            None => return Ok(()),
1228
        };
1229
1230
        let visible = self.visible_windows();
1231
        if visible.len() < 2 {
1232
            return Ok(());
1233
        }
1234
1235
        let focused_geom = match self.connection.get_geometry(focused)?.reply() {
1236
            Ok(geom) => geom,
1237
            Err(_) => return Ok(()),
1238
        };
1239
1240
        let focused_center_x = focused_geom.x + (focused_geom.width as i16 / 2);
1241
        let focused_center_y = focused_geom.y + (focused_geom.height as i16 / 2);
1242
1243
        let mut candidates = Vec::new();
1244
1245
        for &window in &visible {
1246
            if window == focused {
1247
                continue;
1248
            }
1249
1250
            let geom = match self.connection.get_geometry(window)?.reply() {
1251
                Ok(g) => g,
1252
                Err(_) => continue,
1253
            };
1254
1255
            let center_x = geom.x + (geom.width as i16 / 2);
1256
            let center_y = geom.y + (geom.height as i16 / 2);
1257
1258
            let is_valid_direction = match direction {
1259
                0 => center_y < focused_center_y,
1260
                1 => center_y > focused_center_y,
1261
                2 => center_x < focused_center_x,
1262
                3 => center_x > focused_center_x,
1263
                _ => false,
1264
            };
1265
1266
            if is_valid_direction {
1267
                let dx = (center_x - focused_center_x) as i32;
1268
                let dy = (center_y - focused_center_y) as i32;
1269
                let distance = dx * dx + dy * dy;
1270
                candidates.push((window, distance));
1271
            }
1272
        }
1273
1274
        if let Some(&(closest_window, _)) = candidates.iter().min_by_key(|&(_, dist)| dist) {
1275
            self.set_focus(closest_window)?;
1276
        }
1277
1278
        Ok(())
1279
    }
1280
1281
    pub fn swap_direction(&mut self, direction: i32) -> WmResult<()> {
1282
        let focused = match self
1283
            .monitors
1284
            .get(self.selected_monitor)
1285
            .and_then(|m| m.focused_window)
1286
        {
1287
            Some(win) => win,
1288
            None => return Ok(()),
1289
        };
1290
1291
        let visible = self.visible_windows();
1292
        if visible.len() < 2 {
1293
            return Ok(());
1294
        }
1295
1296
        let focused_geom = match self.connection.get_geometry(focused)?.reply() {
1297
            Ok(geom) => geom,
1298
            Err(_) => return Ok(()),
1299
        };
1300
1301
        let focused_center_x = focused_geom.x + (focused_geom.width as i16 / 2);
1302
        let focused_center_y = focused_geom.y + (focused_geom.height as i16 / 2);
1303
1304
        let mut candidates = Vec::new();
1305
1306
        for &window in &visible {
1307
            if window == focused {
1308
                continue;
1309
            }
1310
1311
            let geom = match self.connection.get_geometry(window)?.reply() {
1312
                Ok(g) => g,
1313
                Err(_) => continue,
1314
            };
1315
1316
            let center_x = geom.x + (geom.width as i16 / 2);
1317
            let center_y = geom.y + (geom.height as i16 / 2);
1318
1319
            let is_valid_direction = match direction {
1320
                0 => center_y < focused_center_y,
1321
                1 => center_y > focused_center_y,
1322
                2 => center_x < focused_center_x,
1323
                3 => center_x > focused_center_x,
1324
                _ => false,
1325
            };
1326
1327
            if is_valid_direction {
1328
                let dx = (center_x - focused_center_x) as i32;
1329
                let dy = (center_y - focused_center_y) as i32;
1330
                let distance = dx * dx + dy * dy;
1331
                candidates.push((window, distance));
1332
            }
1333
        }
1334
1335
        if let Some(&(target_window, _)) = candidates.iter().min_by_key(|&(_, dist)| dist) {
1336
            let focused_pos = self.windows.iter().position(|&w| w == focused);
1337
            let target_pos = self.windows.iter().position(|&w| w == target_window);
1338
1339
            if let (Some(f_pos), Some(t_pos)) = (focused_pos, target_pos) {
1340
                self.windows.swap(f_pos, t_pos);
1341
                self.apply_layout()?;
1342
                self.set_focus(focused)?;
1343
1344
                if let Ok(geometry) = self.connection.get_geometry(focused)?.reply() {
1345
                    self.connection.warp_pointer(
1346
                        x11rb::NONE,
1347
                        focused,
1348
                        0,
1349
                        0,
1350
                        0,
1351
                        0,
1352
                        geometry.width as i16 / 2,
1353
                        geometry.height as i16 / 2,
1354
                    )?;
1355
                }
1356
            }
1357
        }
1358
1359
        Ok(())
1360
    }
1361
1362
    fn grab_next_keys(&self, candidates: &[usize], keys_pressed: usize) -> WmResult<()> {
1363
        use std::collections::HashMap;
1364
        use x11rb::protocol::xproto::Keycode;
1365
1366
        let setup = self.connection.setup();
1367
        let min_keycode = setup.min_keycode;
1368
        let max_keycode = setup.max_keycode;
1369
1370
        let keyboard_mapping = self
1371
            .connection
1372
            .get_keyboard_mapping(min_keycode, max_keycode - min_keycode + 1)?
1373
            .reply()?;
1374
1375
        let mut keysym_to_keycode: HashMap<keyboard::keysyms::Keysym, Vec<Keycode>> =
1376
            HashMap::new();
1377
        let keysyms_per_keycode = keyboard_mapping.keysyms_per_keycode;
1378
1379
        for keycode in min_keycode..=max_keycode {
1380
            let index = (keycode - min_keycode) as usize * keysyms_per_keycode as usize;
1381
            for i in 0..keysyms_per_keycode as usize {
1382
                if let Some(&keysym) = keyboard_mapping.keysyms.get(index + i) {
1383
                    if keysym != 0 {
1384
                        keysym_to_keycode
1385
                            .entry(keysym)
1386
                            .or_insert_with(Vec::new)
1387
                            .push(keycode);
1388
                    }
1389
                }
1390
            }
1391
        }
1392
1393
        let mut grabbed_keys: HashSet<(u16, Keycode)> = HashSet::new();
1394
1395
        for &candidate_index in candidates {
1396
            let binding = &self.config.keybindings[candidate_index];
1397
            if keys_pressed < binding.keys.len() {
1398
                let next_key = &binding.keys[keys_pressed];
1399
                let modifier_mask = keyboard::handlers::modifiers_to_mask(&next_key.modifiers);
1400
1401
                if let Some(keycodes) = keysym_to_keycode.get(&next_key.keysym) {
1402
                    if let Some(&keycode) = keycodes.first() {
1403
                        let key_tuple = (modifier_mask, keycode);
1404
1405
                        if grabbed_keys.insert(key_tuple) {
1406
                            self.connection.grab_key(
1407
                                false,
1408
                                self.root,
1409
                                modifier_mask.into(),
1410
                                keycode,
1411
                                GrabMode::ASYNC,
1412
                                GrabMode::ASYNC,
1413
                            )?;
1414
                        }
1415
                    }
1416
                }
1417
            }
1418
        }
1419
1420
        if let Some(keycodes) = keysym_to_keycode.get(&keyboard::keysyms::XK_ESCAPE) {
1421
            if let Some(&keycode) = keycodes.first() {
1422
                self.connection.grab_key(
1423
                    false,
1424
                    self.root,
1425
                    ModMask::from(0u16),
1426
                    keycode,
1427
                    GrabMode::ASYNC,
1428
                    GrabMode::ASYNC,
1429
                )?;
1430
            }
1431
        }
1432
1433
        self.connection.flush()?;
1434
        Ok(())
1435
    }
1436
1437
    fn ungrab_chord_keys(&self) -> WmResult<()> {
1438
        self.connection
1439
            .ungrab_key(x11rb::protocol::xproto::Grab::ANY, self.root, ModMask::ANY)?;
1440
        keyboard::setup_keybinds(&self.connection, self.root, &self.config.keybindings)?;
1441
        self.connection.flush()?;
1442
        Ok(())
1443
    }
1444
1445
    pub fn set_focus(&mut self, window: Window) -> WmResult<()> {
1446
        let old_focused = self.previous_focused;
1447
1448
        if let Some(monitor) = self.monitors.get_mut(self.selected_monitor) {
1449
            monitor.focused_window = Some(window);
1450
        }
1451
1452
        self.connection
1453
            .set_input_focus(InputFocus::POINTER_ROOT, window, x11rb::CURRENT_TIME)?;
1454
        self.connection.flush()?;
1455
1456
        self.update_focus_visuals(old_focused, window)?;
1457
        self.previous_focused = Some(window);
1458
        Ok(())
1459
    }
1460
1461
    fn update_focus_visuals(
1462
        &self,
1463
        old_focused: Option<Window>,
1464
        new_focused: Window,
1465
    ) -> WmResult<()> {
1466
        if let Some(old_win) = old_focused {
1467
            if old_win != new_focused {
1468
                self.connection.configure_window(
1469
                    old_win,
1470
                    &ConfigureWindowAux::new().border_width(self.config.border_width),
1471
                )?;
1472
1473
                self.connection.change_window_attributes(
1474
                    old_win,
1475
                    &ChangeWindowAttributesAux::new().border_pixel(self.config.border_unfocused),
1476
                )?;
1477
            }
1478
        }
1479
1480
        self.connection.configure_window(
1481
            new_focused,
1482
            &ConfigureWindowAux::new().border_width(self.config.border_width),
1483
        )?;
1484
1485
        self.connection.change_window_attributes(
1486
            new_focused,
1487
            &ChangeWindowAttributesAux::new().border_pixel(self.config.border_focused),
1488
        )?;
1489
1490
        self.connection.flush()?;
1491
        Ok(())
1492
    }
1493
1494
    fn move_mouse(&mut self, window: Window) -> WmResult<()> {
1495
        self.floating_windows.insert(window);
1496
1497
        let geometry = self.connection.get_geometry(window)?.reply()?;
1498
1499
        self.connection
1500
            .grab_pointer(
1501
                false,
1502
                self.root,
1503
                (EventMask::POINTER_MOTION | EventMask::BUTTON_RELEASE).into(),
1504
                GrabMode::ASYNC,
1505
                GrabMode::ASYNC,
1506
                x11rb::NONE,
1507
                x11rb::NONE,
1508
                x11rb::CURRENT_TIME,
1509
            )?
1510
            .reply()?;
1511
1512
        let pointer = self.connection.query_pointer(self.root)?.reply()?;
1513
        let (start_x, start_y) = (pointer.root_x, pointer.root_y);
1514
1515
        self.connection.configure_window(
1516
            window,
1517
            &ConfigureWindowAux::new().stack_mode(StackMode::ABOVE),
1518
        )?;
1519
1520
        loop {
1521
            let event = self.connection.wait_for_event()?;
1522
            match event {
1523
                Event::MotionNotify(e) => {
1524
                    let new_x = geometry.x + (e.root_x - start_x);
1525
                    let new_y = geometry.y + (e.root_y - start_y);
1526
                    self.connection.configure_window(
1527
                        window,
1528
                        &ConfigureWindowAux::new().x(new_x as i32).y(new_y as i32),
1529
                    )?;
1530
                    self.connection.flush()?;
1531
                }
1532
                Event::ButtonRelease(_) => break,
1533
                _ => {}
1534
            }
1535
        }
1536
1537
        self.connection
1538
            .ungrab_pointer(x11rb::CURRENT_TIME)?
1539
            .check()?;
1540
        self.connection
1541
            .allow_events(Allow::REPLAY_POINTER, x11rb::CURRENT_TIME)?
1542
            .check()?;
1543
1544
        Ok(())
1545
    }
1546
1547
    fn resize_mouse(&mut self, window: Window) -> WmResult<()> {
1548
        self.floating_windows.insert(window);
1549
1550
        let geometry = self.connection.get_geometry(window)?.reply()?;
1551
1552
        self.connection.warp_pointer(
1553
            x11rb::NONE,
1554
            window,
1555
            0,
1556
            0,
1557
            0,
1558
            0,
1559
            geometry.width as i16,
1560
            geometry.height as i16,
1561
        )?;
1562
1563
        self.connection
1564
            .grab_pointer(
1565
                false,
1566
                self.root,
1567
                (EventMask::POINTER_MOTION | EventMask::BUTTON_RELEASE).into(),
1568
                GrabMode::ASYNC,
1569
                GrabMode::ASYNC,
1570
                x11rb::NONE,
1571
                x11rb::NONE,
1572
                x11rb::CURRENT_TIME,
1573
            )?
1574
            .reply()?;
1575
1576
        self.connection.configure_window(
1577
            window,
1578
            &ConfigureWindowAux::new().stack_mode(StackMode::ABOVE),
1579
        )?;
1580
1581
        loop {
1582
            let event = self.connection.wait_for_event()?;
1583
            match event {
1584
                Event::MotionNotify(e) => {
1585
                    let new_width = (e.root_x - geometry.x).max(1) as u32;
1586
                    let new_height = (e.root_y - geometry.y).max(1) as u32;
1587
1588
                    self.connection.configure_window(
1589
                        window,
1590
                        &ConfigureWindowAux::new()
1591
                            .width(new_width)
1592
                            .height(new_height),
1593
                    )?;
1594
                    self.connection.flush()?;
1595
                }
1596
                Event::ButtonRelease(_) => break,
1597
                _ => {}
1598
            }
1599
        }
1600
1601
        self.connection
1602
            .ungrab_pointer(x11rb::CURRENT_TIME)?
1603
            .check()?;
1604
        self.connection
1605
            .allow_events(Allow::REPLAY_POINTER, x11rb::CURRENT_TIME)?
1606
            .check()?;
1607
1608
        Ok(())
1609
    }
1610
1611
    fn handle_event(&mut self, event: Event) -> WmResult<Option<bool>> {
1612
        match event {
1613
            Event::MapRequest(event) => {
1614
                let attrs = match self.connection.get_window_attributes(event.window)?.reply() {
1615
                    Ok(attrs) => attrs,
1616
                    Err(_) => return Ok(None),
1617
                };
1618
1619
                if attrs.override_redirect {
1620
                    return Ok(None);
1621
                }
1622
1623
                if self.windows.contains(&event.window) {
1624
                    return Ok(None);
1625
                }
1626
1627
                if let Ok(geom) = self.connection.get_geometry(event.window)?.reply() {
1628
                    self.window_geometries
1629
                        .insert(event.window, (geom.x, geom.y, geom.width, geom.height));
1630
                }
1631
1632
                self.connection.map_window(event.window)?;
1633
                self.connection.change_window_attributes(
1634
                    event.window,
1635
                    &ChangeWindowAttributesAux::new().event_mask(EventMask::ENTER_WINDOW),
1636
                )?;
1637
1638
                let selected_tags = self
1639
                    .monitors
1640
                    .get(self.selected_monitor)
1641
                    .map(|m| m.selected_tags)
1642
                    .unwrap_or(tag_mask(0));
1643
1644
                self.windows.push(event.window);
1645
                self.window_tags.insert(event.window, selected_tags);
1646
                self.window_monitor
1647
                    .insert(event.window, self.selected_monitor);
1648
                self.set_wm_state(event.window, 1)?;
1649
                let _ = self.save_client_tag(event.window, selected_tags);
1650
1651
                self.apply_layout()?;
1652
                self.update_bar()?;
1653
                self.set_focus(event.window)?;
1654
            }
1655
            Event::UnmapNotify(event) => {
1656
                if self.windows.contains(&event.window) && self.is_window_visible(event.window) {
1657
                    self.remove_window(event.window)?;
1658
                }
1659
            }
1660
            Event::DestroyNotify(event) => {
1661
                if self.windows.contains(&event.window) {
1662
                    self.remove_window(event.window)?;
1663
                }
1664
            }
1665
            Event::EnterNotify(event) => {
1666
                if event.mode != x11rb::protocol::xproto::NotifyMode::NORMAL {
1667
                    return Ok(None);
1668
                }
1669
                if self.windows.contains(&event.event) {
1670
                    self.set_focus(event.event)?;
1671
                }
1672
            }
1673
            Event::MotionNotify(event) => {
1674
                if event.event != self.root {
1675
                    return Ok(None);
1676
                }
1677
1678
                if let Some(monitor_index) =
1679
                    self.get_monitor_at_point(event.root_x as i32, event.root_y as i32)
1680
                {
1681
                    if monitor_index != self.selected_monitor {
1682
                        self.selected_monitor = monitor_index;
1683
                        self.update_bar()?;
1684
1685
                        let visible = self.visible_windows_on_monitor(monitor_index);
1686
                        if let Some(&win) = visible.first() {
1687
                            self.set_focus(win)?;
1688
                        }
1689
                    }
1690
                }
1691
            }
1692
            Event::KeyPress(event) => {
1693
                let result = keyboard::handle_key_press(
1694
                    event,
1695
                    &self.config.keybindings,
1696
                    &self.keychord_state,
1697
                    &self.connection,
1698
                )?;
1699
1700
                match result {
1701
                    keyboard::handlers::KeychordResult::Completed(action, arg) => {
1702
                        self.keychord_state = keyboard::handlers::KeychordState::Idle;
1703
                        self.ungrab_chord_keys()?;
1704
                        self.update_bar()?;
1705
1706
                        match action {
1707
                            KeyAction::Quit => return Ok(Some(false)),
1708
                            KeyAction::Restart => return Ok(Some(true)),
1709
                            _ => self.handle_key_action(action, &arg)?,
1710
                        }
1711
                    }
1712
                    keyboard::handlers::KeychordResult::InProgress(candidates) => {
1713
                        let keys_pressed = match &self.keychord_state {
1714
                            keyboard::handlers::KeychordState::Idle => 1,
1715
                            keyboard::handlers::KeychordState::InProgress {
1716
                                keys_pressed, ..
1717
                            } => keys_pressed + 1,
1718
                        };
1719
1720
                        self.keychord_state = keyboard::handlers::KeychordState::InProgress {
1721
                            candidates: candidates.clone(),
1722
                            keys_pressed,
1723
                        };
1724
1725
                        self.grab_next_keys(&candidates, keys_pressed)?;
1726
                        self.update_bar()?;
1727
                    }
1728
                    keyboard::handlers::KeychordResult::Cancelled
1729
                    | keyboard::handlers::KeychordResult::None => {
1730
                        self.keychord_state = keyboard::handlers::KeychordState::Idle;
1731
                        self.ungrab_chord_keys()?;
1732
                        self.update_bar()?;
1733
                    }
1734
                }
1735
            }
1736
            Event::ButtonPress(event) => {
1737
                let is_bar_click = self
1738
                    .bars
1739
                    .iter()
1740
                    .enumerate()
1741
                    .find(|(_, bar)| bar.window() == event.event);
1742
1743
                if let Some((monitor_index, bar)) = is_bar_click {
1744
                    if let Some(tag_index) = bar.handle_click(event.event_x) {
1745
                        if monitor_index != self.selected_monitor {
1746
                            self.selected_monitor = monitor_index;
1747
                        }
1748
                        self.view_tag(tag_index)?;
1749
                    }
1750
                } else if event.child != x11rb::NONE {
1751
                    self.set_focus(event.child)?;
1752
1753
                    if event.detail == ButtonIndex::M1.into() {
1754
                        self.move_mouse(event.child)?;
1755
                    } else if event.detail == ButtonIndex::M3.into() {
1756
                        self.resize_mouse(event.child)?;
1757
                    }
1758
                }
1759
            }
1760
            Event::Expose(event) => {
1761
                for bar in &mut self.bars {
1762
                    if event.window == bar.window() {
1763
                        bar.invalidate();
1764
                        self.update_bar()?;
1765
                        break;
1766
                    }
1767
                }
1768
            }
1769
            _ => {}
1770
        }
1771
        Ok(None)
1772
    }
1773
1774
    fn apply_layout(&self) -> WmResult<()> {
1775
        if self.layout.name() == LayoutType::Normie.as_str() {
1776
            return Ok(());
1777
        }
1778
1779
        for (monitor_index, monitor) in self.monitors.iter().enumerate() {
1780
            let border_width = if monitor.fullscreen_enabled {
1781
                0
1782
            } else {
1783
                self.config.border_width
1784
            };
1785
1786
            let gaps = if self.gaps_enabled && !monitor.fullscreen_enabled {
1787
                GapConfig {
1788
                    inner_horizontal: self.config.gap_inner_horizontal,
1789
                    inner_vertical: self.config.gap_inner_vertical,
1790
                    outer_horizontal: self.config.gap_outer_horizontal,
1791
                    outer_vertical: self.config.gap_outer_vertical,
1792
                }
1793
            } else {
1794
                GapConfig {
1795
                    inner_horizontal: 0,
1796
                    inner_vertical: 0,
1797
                    outer_horizontal: 0,
1798
                    outer_vertical: 0,
1799
                }
1800
            };
1801
1802
            let visible: Vec<Window> = self
1803
                .windows
1804
                .iter()
1805
                .filter(|&&w| {
1806
                    let window_mon = self.window_monitor.get(&w).copied().unwrap_or(0);
1807
                    if window_mon != monitor_index {
1808
                        return false;
1809
                    }
1810
                    if self.floating_windows.contains(&w) {
1811
                        return false;
1812
                    }
1813
                    if let Some(&tags) = self.window_tags.get(&w) {
1814
                        (tags & monitor.selected_tags) != 0
1815
                    } else {
1816
                        false
1817
                    }
1818
                })
1819
                .copied()
1820
                .collect();
1821
1822
            let bar_height = if monitor.fullscreen_enabled {
1823
                0
1824
            } else {
1825
                self.bars
1826
                    .get(monitor_index)
1827
                    .map(|b| b.height() as u32)
1828
                    .unwrap_or(0)
1829
            };
1830
            let usable_height = monitor.height.saturating_sub(bar_height);
1831
1832
            let geometries = self
1833
                .layout
1834
                .arrange(&visible, monitor.width, usable_height, &gaps);
1835
1836
            for (window, geometry) in visible.iter().zip(geometries.iter()) {
1837
                let adjusted_width = geometry.width.saturating_sub(2 * border_width);
1838
                let adjusted_height = geometry.height.saturating_sub(2 * border_width);
1839
1840
                let adjusted_x = geometry.x_coordinate + monitor.x;
1841
                let adjusted_y = geometry.y_coordinate + monitor.y + bar_height as i32;
1842
1843
                self.connection.configure_window(
1844
                    *window,
1845
                    &ConfigureWindowAux::new()
1846
                        .x(adjusted_x)
1847
                        .y(adjusted_y)
1848
                        .width(adjusted_width)
1849
                        .height(adjusted_height)
1850
                        .border_width(border_width),
1851
                )?;
1852
            }
1853
        }
1854
1855
        self.connection.flush()?;
1856
        Ok(())
1857
    }
1858
1859
    pub fn change_layout<L: Layout + 'static>(&mut self, new_layout: L) -> WmResult<()> {
1860
        self.layout = Box::new(new_layout);
1861
        self.apply_layout()?;
1862
        Ok(())
1863
    }
1864
1865
    fn remove_window(&mut self, window: Window) -> WmResult<()> {
1866
        let initial_count = self.windows.len();
1867
        self.windows.retain(|&w| w != window);
1868
        self.window_tags.remove(&window);
1869
        self.window_monitor.remove(&window);
1870
        self.window_geometries.remove(&window);
1871
        self.floating_windows.remove(&window);
1872
1873
        if self.windows.len() < initial_count {
1874
            let focused = self
1875
                .monitors
1876
                .get(self.selected_monitor)
1877
                .and_then(|m| m.focused_window);
1878
            if focused == Some(window) {
1879
                let visible = self.visible_windows_on_monitor(self.selected_monitor);
1880
                if let Some(&new_win) = visible.last() {
1881
                    self.set_focus(new_win)?;
1882
                } else if let Some(monitor) = self.monitors.get_mut(self.selected_monitor) {
1883
                    monitor.focused_window = None;
1884
                }
1885
            }
1886
1887
            self.apply_layout()?;
1888
            self.update_bar()?;
1889
        }
1890
        Ok(())
1891
    }
1892
1893
    fn run_autostart_commands(&self) -> Result<(), WmError> {
1894
        for command in &self.config.autostart {
1895
            Command::new("sh")
1896
                .arg("-c")
1897
                .arg(command)
1898
                .spawn()
1899
                .map_err(|e| WmError::Autostart(command.clone(), e))?;
1900
            eprintln!("[autostart] Spawned: {}", command);
1901
        }
1902
        Ok(())
1903
    }
1904
}