oxwm

https://git.tonybtw.com/oxwm.git git://git.tonybtw.com/oxwm.git
8,362 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, String)],
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, ref title)) 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 display_title = if title.is_empty() {
154
                format!("Window {}", index + 1)
155
            } else {
156
                title.clone()
157
            };
158
159
            let text_width = font.text_width(&display_title);
160
            let text_x = x_position + ((tab_width.saturating_sub(text_width)) / 2) as i16;
161
162
            let top_padding = 6;
163
            let text_y = top_padding + font.ascent();
164
165
            self.font_draw
166
                .draw_text(font, scheme.foreground, text_x, text_y, &display_title);
167
168
            if is_focused {
169
                let underline_height = 3;
170
                let underline_y = self.height as i16 - underline_height;
171
172
                unsafe {
173
                    let gc =
174
                        x11::xlib::XCreateGC(self.display, self.pixmap, 0, std::ptr::null_mut());
175
                    x11::xlib::XSetForeground(self.display, gc, scheme.underline as u64);
176
                    x11::xlib::XFillRectangle(
177
                        self.display,
178
                        self.pixmap,
179
                        gc,
180
                        x_position as i32,
181
                        underline_y as i32,
182
                        tab_width as u32,
183
                        underline_height as u32,
184
                    );
185
                    x11::xlib::XFreeGC(self.display, gc);
186
                }
187
            }
188
189
            x_position += tab_width as i16;
190
        }
191
192
        self.copy_pixmap_to_window();
193
        Ok(())
194
    }
195
196
    fn copy_pixmap_to_window(&self) {
197
        unsafe {
198
            let gc = x11::xlib::XCreateGC(self.display, self.window as u64, 0, std::ptr::null_mut());
199
            x11::xlib::XCopyArea(
200
                self.display,
201
                self.pixmap,
202
                self.window as u64,
203
                gc,
204
                0,
205
                0,
206
                self.width as u32,
207
                self.height as u32,
208
                0,
209
                0,
210
            );
211
            x11::xlib::XFreeGC(self.display, gc);
212
        }
213
    }
214
215
    pub fn get_clicked_window(
216
        &self,
217
        windows: &[(Window, String)],
218
        click_x: i16,
219
    ) -> Option<Window> {
220
        if windows.is_empty() {
221
            return None;
222
        }
223
224
        let tab_width = self.width / windows.len() as u16;
225
        let tab_index = (click_x as u16 / tab_width) as usize;
226
227
        windows.get(tab_index).map(|&(win, _)| win)
228
    }
229
230
    pub fn reposition(
231
        &mut self,
232
        connection: &RustConnection,
233
        x: i16,
234
        y: i16,
235
        width: u16,
236
    ) -> Result<(), X11Error> {
237
        self.x_offset = x;
238
        self.y_offset = y;
239
        self.width = width;
240
241
        connection.configure_window(
242
            self.window,
243
            &ConfigureWindowAux::new()
244
                .x(x as i32)
245
                .y(y as i32)
246
                .width(width as u32),
247
        )?;
248
249
        unsafe {
250
            x11::xlib::XFreePixmap(self.display, self.pixmap);
251
        }
252
253
        let depth = unsafe { x11::xlib::XDefaultDepth(self.display, 0) };
254
        self.pixmap = unsafe {
255
            x11::xlib::XCreatePixmap(
256
                self.display,
257
                self.window as x11::xlib::Drawable,
258
                width as u32,
259
                self.height as u32,
260
                depth as u32,
261
            )
262
        };
263
264
        let visual = unsafe { x11::xlib::XDefaultVisual(self.display, 0) };
265
        let colormap = unsafe { x11::xlib::XDefaultColormap(self.display, 0) };
266
        self.font_draw = FontDraw::new(self.display, self.pixmap, visual, colormap)?;
267
268
        connection.flush()?;
269
        Ok(())
270
    }
271
272
    pub fn hide(&self, connection: &RustConnection) -> Result<(), X11Error> {
273
        connection.unmap_window(self.window)?;
274
        connection.flush()?;
275
        Ok(())
276
    }
277
278
    pub fn show(&self, connection: &RustConnection) -> Result<(), X11Error> {
279
        connection.map_window(self.window)?;
280
        connection.flush()?;
281
        Ok(())
282
    }
283
}
284
285
impl Drop for TabBar {
286
    fn drop(&mut self) {
287
        unsafe {
288
            x11::xlib::XFreePixmap(self.display, self.pixmap);
289
        }
290
    }
291
}