oxwm

https://git.tonybtw.com/oxwm.git git://git.tonybtw.com/oxwm.git
8,921 bytes raw
1
use crate::bar::font::{Font, FontDraw};
2
use crate::errors::X11Error;
3
use crate::layout::tabbed::TAB_BAR_HEIGHT;
4
use crate::ColorScheme;
5
use x11rb::connection::Connection;
6
use x11rb::protocol::xproto::*;
7
use x11rb::rust_connection::RustConnection;
8
use x11rb::COPY_DEPTH_FROM_PARENT;
9
10
pub struct TabBar {
11
    window: Window,
12
    width: u16,
13
    height: u16,
14
    x_offset: i16,
15
    y_offset: i16,
16
    graphics_context: Gcontext,
17
    pixmap: x11::xlib::Pixmap,
18
    display: *mut x11::xlib::Display,
19
    font_draw: FontDraw,
20
    scheme_normal: ColorScheme,
21
    scheme_selected: ColorScheme,
22
}
23
24
impl TabBar {
25
    pub fn new(
26
        connection: &RustConnection,
27
        screen: &Screen,
28
        screen_num: usize,
29
        display: *mut x11::xlib::Display,
30
        _font: &Font,
31
        x: i16,
32
        y: i16,
33
        width: u16,
34
        scheme_normal: ColorScheme,
35
        scheme_selected: ColorScheme,
36
    ) -> Result<Self, X11Error> {
37
        let window = connection.generate_id()?;
38
        let graphics_context = connection.generate_id()?;
39
40
        let height = TAB_BAR_HEIGHT as u16;
41
42
        connection.create_window(
43
            COPY_DEPTH_FROM_PARENT,
44
            window,
45
            screen.root,
46
            x,
47
            y,
48
            width,
49
            height,
50
            0,
51
            WindowClass::INPUT_OUTPUT,
52
            screen.root_visual,
53
            &CreateWindowAux::new()
54
                .background_pixel(scheme_normal.background)
55
                .event_mask(EventMask::EXPOSURE | EventMask::BUTTON_PRESS)
56
                .override_redirect(1),
57
        )?;
58
59
        connection.create_gc(
60
            graphics_context,
61
            window,
62
            &CreateGCAux::new()
63
                .foreground(scheme_normal.foreground)
64
                .background(scheme_normal.background),
65
        )?;
66
67
        connection.map_window(window)?;
68
        connection.flush()?;
69
70
        let visual = unsafe { x11::xlib::XDefaultVisual(display, screen_num as i32) };
71
        let colormap = unsafe { x11::xlib::XDefaultColormap(display, screen_num as i32) };
72
        let depth = unsafe { x11::xlib::XDefaultDepth(display, screen_num as i32) };
73
74
        let pixmap = unsafe {
75
            x11::xlib::XCreatePixmap(
76
                display,
77
                window as x11::xlib::Drawable,
78
                width as u32,
79
                height as u32,
80
                depth as u32,
81
            )
82
        };
83
84
        let font_draw = FontDraw::new(display, pixmap, visual, colormap)?;
85
86
        Ok(Self {
87
            window,
88
            width,
89
            height,
90
            x_offset: x,
91
            y_offset: y,
92
            graphics_context,
93
            pixmap,
94
            display,
95
            font_draw,
96
            scheme_normal,
97
            scheme_selected,
98
        })
99
    }
100
101
    pub fn window(&self) -> Window {
102
        self.window
103
    }
104
105
    pub fn draw(
106
        &mut self,
107
        connection: &RustConnection,
108
        font: &Font,
109
        windows: &[Window],
110
        focused_window: Option<Window>,
111
    ) -> Result<(), X11Error> {
112
        connection.change_gc(
113
            self.graphics_context,
114
            &ChangeGCAux::new().foreground(self.scheme_normal.background),
115
        )?;
116
        connection.flush()?;
117
118
        unsafe {
119
            let gc = x11::xlib::XCreateGC(self.display, self.pixmap, 0, std::ptr::null_mut());
120
            x11::xlib::XSetForeground(
121
                self.display,
122
                gc,
123
                self.scheme_normal.background as u64,
124
            );
125
            x11::xlib::XFillRectangle(
126
                self.display,
127
                self.pixmap,
128
                gc,
129
                0,
130
                0,
131
                self.width as u32,
132
                self.height as u32,
133
            );
134
            x11::xlib::XFreeGC(self.display, gc);
135
        }
136
137
        if windows.is_empty() {
138
            self.copy_pixmap_to_window();
139
            return Ok(());
140
        }
141
142
        let tab_width = self.width / windows.len() as u16;
143
        let mut x_position: i16 = 0;
144
145
        for (index, &window) in windows.iter().enumerate() {
146
            let is_focused = Some(window) == focused_window;
147
            let scheme = if is_focused {
148
                &self.scheme_selected
149
            } else {
150
                &self.scheme_normal
151
            };
152
153
            let title = self.get_window_title(connection, window);
154
            let display_title = if title.is_empty() {
155
                format!("Window {}", index + 1)
156
            } else {
157
                title
158
            };
159
160
            let text_width = font.text_width(&display_title);
161
            let text_x = x_position + ((tab_width.saturating_sub(text_width)) / 2) as i16;
162
163
            let top_padding = 6;
164
            let text_y = top_padding + font.ascent();
165
166
            self.font_draw
167
                .draw_text(font, scheme.foreground, text_x, text_y, &display_title);
168
169
            if is_focused {
170
                let underline_height = 3;
171
                let underline_y = self.height as i16 - underline_height;
172
173
                unsafe {
174
                    let gc =
175
                        x11::xlib::XCreateGC(self.display, self.pixmap, 0, std::ptr::null_mut());
176
                    x11::xlib::XSetForeground(self.display, gc, scheme.underline as u64);
177
                    x11::xlib::XFillRectangle(
178
                        self.display,
179
                        self.pixmap,
180
                        gc,
181
                        x_position as i32,
182
                        underline_y as i32,
183
                        tab_width as u32,
184
                        underline_height as u32,
185
                    );
186
                    x11::xlib::XFreeGC(self.display, gc);
187
                }
188
            }
189
190
            x_position += tab_width as i16;
191
        }
192
193
        self.copy_pixmap_to_window();
194
        Ok(())
195
    }
196
197
    fn copy_pixmap_to_window(&self) {
198
        unsafe {
199
            let gc = x11::xlib::XCreateGC(self.display, self.window as u64, 0, std::ptr::null_mut());
200
            x11::xlib::XCopyArea(
201
                self.display,
202
                self.pixmap,
203
                self.window as u64,
204
                gc,
205
                0,
206
                0,
207
                self.width as u32,
208
                self.height as u32,
209
                0,
210
                0,
211
            );
212
            x11::xlib::XFreeGC(self.display, gc);
213
        }
214
    }
215
216
    fn get_window_title(&self, connection: &RustConnection, window: Window) -> String {
217
        connection
218
            .get_property(false, window, AtomEnum::WM_NAME, AtomEnum::STRING, 0, 1024)
219
            .ok()
220
            .and_then(|cookie| cookie.reply().ok())
221
            .and_then(|reply| {
222
                if reply.value.is_empty() {
223
                    None
224
                } else {
225
                    std::str::from_utf8(&reply.value).ok().map(|s| s.to_string())
226
                }
227
            })
228
            .unwrap_or_default()
229
    }
230
231
    pub fn get_clicked_window(
232
        &self,
233
        windows: &[Window],
234
        click_x: i16,
235
    ) -> Option<Window> {
236
        if windows.is_empty() {
237
            return None;
238
        }
239
240
        let tab_width = self.width / windows.len() as u16;
241
        let tab_index = (click_x as u16 / tab_width) as usize;
242
243
        windows.get(tab_index).copied()
244
    }
245
246
    pub fn reposition(
247
        &mut self,
248
        connection: &RustConnection,
249
        x: i16,
250
        y: i16,
251
        width: u16,
252
    ) -> Result<(), X11Error> {
253
        self.x_offset = x;
254
        self.y_offset = y;
255
        self.width = width;
256
257
        connection.configure_window(
258
            self.window,
259
            &ConfigureWindowAux::new()
260
                .x(x as i32)
261
                .y(y as i32)
262
                .width(width as u32),
263
        )?;
264
265
        unsafe {
266
            x11::xlib::XFreePixmap(self.display, self.pixmap);
267
        }
268
269
        let depth = unsafe { x11::xlib::XDefaultDepth(self.display, 0) };
270
        self.pixmap = unsafe {
271
            x11::xlib::XCreatePixmap(
272
                self.display,
273
                self.window as x11::xlib::Drawable,
274
                width as u32,
275
                self.height as u32,
276
                depth as u32,
277
            )
278
        };
279
280
        let visual = unsafe { x11::xlib::XDefaultVisual(self.display, 0) };
281
        let colormap = unsafe { x11::xlib::XDefaultColormap(self.display, 0) };
282
        self.font_draw = FontDraw::new(self.display, self.pixmap, visual, colormap)?;
283
284
        connection.flush()?;
285
        Ok(())
286
    }
287
288
    pub fn hide(&self, connection: &RustConnection) -> Result<(), X11Error> {
289
        connection.unmap_window(self.window)?;
290
        connection.flush()?;
291
        Ok(())
292
    }
293
294
    pub fn show(&self, connection: &RustConnection) -> Result<(), X11Error> {
295
        connection.map_window(self.window)?;
296
        connection.flush()?;
297
        Ok(())
298
    }
299
}
300
301
impl Drop for TabBar {
302
    fn drop(&mut self) {
303
        unsafe {
304
            x11::xlib::XFreePixmap(self.display, self.pixmap);
305
        }
306
    }
307
}