oxwm

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