oxwm

https://git.tonybtw.com/oxwm.git git://git.tonybtw.com/oxwm.git
8,284 bytes raw
1
use crate::ColorScheme;
2
use crate::bar::font::{Font, FontDraw};
3
use crate::errors::X11Error;
4
use crate::layout::tabbed::TAB_BAR_HEIGHT;
5
use x11rb::COPY_DEPTH_FROM_PARENT;
6
use x11rb::connection::Connection;
7
use x11rb::protocol::xproto::*;
8
use x11rb::rust_connection::RustConnection;
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(self.display, gc, self.scheme_normal.background as u64);
121
            x11::xlib::XFillRectangle(
122
                self.display,
123
                self.pixmap,
124
                gc,
125
                0,
126
                0,
127
                self.width as u32,
128
                self.height as u32,
129
            );
130
            x11::xlib::XFreeGC(self.display, gc);
131
        }
132
133
        if windows.is_empty() {
134
            self.copy_pixmap_to_window();
135
            return Ok(());
136
        }
137
138
        let tab_width = self.width / windows.len() as u16;
139
        let mut x_position: i16 = 0;
140
141
        for (index, &(window, ref title)) in windows.iter().enumerate() {
142
            let is_focused = Some(window) == focused_window;
143
            let scheme = if is_focused {
144
                &self.scheme_selected
145
            } else {
146
                &self.scheme_normal
147
            };
148
149
            let display_title = if title.is_empty() {
150
                format!("Window {}", index + 1)
151
            } else {
152
                title.clone()
153
            };
154
155
            let text_width = font.text_width(&display_title);
156
            let text_x = x_position + ((tab_width.saturating_sub(text_width)) / 2) as i16;
157
158
            let top_padding = 6;
159
            let text_y = top_padding + font.ascent();
160
161
            self.font_draw
162
                .draw_text(font, scheme.foreground, text_x, text_y, &display_title);
163
164
            if is_focused {
165
                let underline_height = 3;
166
                let underline_y = self.height as i16 - underline_height;
167
168
                unsafe {
169
                    let gc =
170
                        x11::xlib::XCreateGC(self.display, self.pixmap, 0, std::ptr::null_mut());
171
                    x11::xlib::XSetForeground(self.display, gc, scheme.underline as u64);
172
                    x11::xlib::XFillRectangle(
173
                        self.display,
174
                        self.pixmap,
175
                        gc,
176
                        x_position as i32,
177
                        underline_y as i32,
178
                        tab_width as u32,
179
                        underline_height as u32,
180
                    );
181
                    x11::xlib::XFreeGC(self.display, gc);
182
                }
183
            }
184
185
            x_position += tab_width as i16;
186
        }
187
188
        self.copy_pixmap_to_window();
189
        Ok(())
190
    }
191
192
    fn copy_pixmap_to_window(&self) {
193
        unsafe {
194
            let gc =
195
                x11::xlib::XCreateGC(self.display, self.window as u64, 0, std::ptr::null_mut());
196
            x11::xlib::XCopyArea(
197
                self.display,
198
                self.pixmap,
199
                self.window as u64,
200
                gc,
201
                0,
202
                0,
203
                self.width as u32,
204
                self.height as u32,
205
                0,
206
                0,
207
            );
208
            x11::xlib::XFreeGC(self.display, gc);
209
        }
210
    }
211
212
    pub fn get_clicked_window(&self, windows: &[(Window, String)], click_x: i16) -> Option<Window> {
213
        if windows.is_empty() {
214
            return None;
215
        }
216
217
        let tab_width = self.width / windows.len() as u16;
218
        let tab_index = (click_x as u16 / tab_width) as usize;
219
220
        windows.get(tab_index).map(|&(win, _)| win)
221
    }
222
223
    pub fn reposition(
224
        &mut self,
225
        connection: &RustConnection,
226
        x: i16,
227
        y: i16,
228
        width: u16,
229
    ) -> Result<(), X11Error> {
230
        self.x_offset = x;
231
        self.y_offset = y;
232
        self.width = width;
233
234
        connection.configure_window(
235
            self.window,
236
            &ConfigureWindowAux::new()
237
                .x(x as i32)
238
                .y(y as i32)
239
                .width(width as u32),
240
        )?;
241
242
        unsafe {
243
            x11::xlib::XFreePixmap(self.display, self.pixmap);
244
        }
245
246
        let depth = unsafe { x11::xlib::XDefaultDepth(self.display, 0) };
247
        self.pixmap = unsafe {
248
            x11::xlib::XCreatePixmap(
249
                self.display,
250
                self.window as x11::xlib::Drawable,
251
                width as u32,
252
                self.height as u32,
253
                depth as u32,
254
            )
255
        };
256
257
        let visual = unsafe { x11::xlib::XDefaultVisual(self.display, 0) };
258
        let colormap = unsafe { x11::xlib::XDefaultColormap(self.display, 0) };
259
        self.font_draw = FontDraw::new(self.display, self.pixmap, visual, colormap)?;
260
261
        connection.flush()?;
262
        Ok(())
263
    }
264
265
    pub fn hide(&self, connection: &RustConnection) -> Result<(), X11Error> {
266
        connection.unmap_window(self.window)?;
267
        connection.flush()?;
268
        Ok(())
269
    }
270
271
    pub fn show(&self, connection: &RustConnection) -> Result<(), X11Error> {
272
        connection.map_window(self.window)?;
273
        connection.flush()?;
274
        Ok(())
275
    }
276
}
277
278
impl Drop for TabBar {
279
    fn drop(&mut self) {
280
        unsafe {
281
            x11::xlib::XFreePixmap(self.display, self.pixmap);
282
        }
283
    }
284
}