oxwm

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