oxwm

https://git.tonybtw.com/oxwm.git git://git.tonybtw.com/oxwm.git
4,635 bytes raw
1
use crate::client::TagMask;
2
use crate::errors::WmError;
3
use x11rb::protocol::xinerama::ConnectionExt as _;
4
use x11rb::protocol::xproto::{Screen, Window};
5
use x11rb::rust_connection::RustConnection;
6
7
type WmResult<T> = Result<T, WmError>;
8
9
#[derive(Debug, Clone)]
10
pub struct Monitor {
11
    pub layout_symbol: String,
12
    pub master_factor: f32,
13
    pub num_master: i32,
14
    pub monitor_number: usize,
15
    pub bar_y_position: i32,
16
    pub screen_x: i32,
17
    pub screen_y: i32,
18
    pub screen_width: i32,
19
    pub screen_height: i32,
20
    pub window_area_x: i32,
21
    pub window_area_y: i32,
22
    pub window_area_width: i32,
23
    pub window_area_height: i32,
24
    pub gap_inner_horizontal: i32,
25
    pub gap_inner_vertical: i32,
26
    pub gap_outer_horizontal: i32,
27
    pub gap_outer_vertical: i32,
28
    pub selected_tags_index: usize,
29
    pub selected_layout_index: usize,
30
    pub tagset: [u32; 2],
31
    pub show_bar: bool,
32
    pub top_bar: bool,
33
    pub clients_head: Option<Window>,
34
    pub selected_client: Option<Window>,
35
    pub stack_head: Option<Window>,
36
    pub bar_window: Option<Window>,
37
    pub layout_indices: [usize; 2],
38
}
39
40
impl Monitor {
41
    pub fn new(x: i32, y: i32, width: u32, height: u32) -> Self {
42
        Self {
43
            layout_symbol: String::from("[]"),
44
            master_factor: 0.55,
45
            num_master: 1,
46
            monitor_number: 0,
47
            bar_y_position: 0,
48
            screen_x: x,
49
            screen_y: y,
50
            screen_width: width as i32,
51
            screen_height: height as i32,
52
            window_area_x: x,
53
            window_area_y: y,
54
            window_area_width: width as i32,
55
            window_area_height: height as i32,
56
            gap_inner_horizontal: 3,
57
            gap_inner_vertical: 3,
58
            gap_outer_horizontal: 3,
59
            gap_outer_vertical: 3,
60
            selected_tags_index: 0,
61
            selected_layout_index: 0,
62
            tagset: [1, 1],
63
            show_bar: true,
64
            top_bar: true,
65
            clients_head: None,
66
            selected_client: None,
67
            stack_head: None,
68
            bar_window: None,
69
            layout_indices: [0, 1],
70
        }
71
    }
72
73
    pub fn contains_point(&self, x: i32, y: i32) -> bool {
74
        x >= self.screen_x
75
            && x < self.screen_x + self.screen_width
76
            && y >= self.screen_y
77
            && y < self.screen_y + self.screen_height
78
    }
79
80
    pub fn get_selected_tag(&self) -> TagMask {
81
        self.tagset[self.selected_tags_index]
82
    }
83
}
84
85
pub fn detect_monitors(
86
    connection: &RustConnection,
87
    screen: &Screen,
88
    _root: Window,
89
) -> WmResult<Vec<Monitor>> {
90
    let fallback_monitors = || {
91
        vec![Monitor::new(
92
            0,
93
            0,
94
            screen.width_in_pixels as u32,
95
            screen.height_in_pixels as u32,
96
        )]
97
    };
98
99
    let mut monitors = Vec::<Monitor>::new();
100
101
    let xinerama_active = connection
102
        .xinerama_is_active()
103
        .ok()
104
        .and_then(|cookie| cookie.reply().ok())
105
        .is_some_and(|reply| reply.state != 0);
106
107
    if xinerama_active {
108
        let Ok(xinerama_cookie) = connection.xinerama_query_screens() else {
109
            return Ok(fallback_monitors());
110
        };
111
        let Ok(xinerama_reply) = xinerama_cookie.reply() else {
112
            return Ok(fallback_monitors());
113
        };
114
115
        for screen_info in &xinerama_reply.screen_info {
116
            let has_valid_dimensions = screen_info.width > 0 && screen_info.height > 0;
117
            if !has_valid_dimensions {
118
                continue;
119
            }
120
121
            let x_position = screen_info.x_org as i32;
122
            let y_position = screen_info.y_org as i32;
123
            let width_in_pixels = screen_info.width as u32;
124
            let height_in_pixels = screen_info.height as u32;
125
126
            let is_duplicate_monitor = monitors.iter().any(|monitor| {
127
                monitor.screen_x == x_position
128
                    && monitor.screen_y == y_position
129
                    && monitor.screen_width == width_in_pixels as i32
130
                    && monitor.screen_height == height_in_pixels as i32
131
            });
132
133
            if !is_duplicate_monitor {
134
                monitors.push(Monitor::new(
135
                    x_position,
136
                    y_position,
137
                    width_in_pixels,
138
                    height_in_pixels,
139
                ));
140
            }
141
        }
142
    }
143
144
    if monitors.is_empty() {
145
        monitors = fallback_monitors();
146
    }
147
148
    monitors.sort_by(|a, b| match a.screen_y.cmp(&b.screen_y) {
149
        std::cmp::Ordering::Equal => a.screen_x.cmp(&b.screen_x),
150
        other => other,
151
    });
152
153
    Ok(monitors)
154
}