oxwm

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