oxwm

https://git.tonybtw.com/oxwm.git git://git.tonybtw.com/oxwm.git
7,973 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
        cursor: u32,
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
        unsafe {
68
            x11::xlib::XDefineCursor(display, window as u64, cursor as u64);
69
        }
70
71
        connection.map_window(window)?;
72
        connection.flush()?;
73
74
        let visual = unsafe { x11::xlib::XDefaultVisual(display, screen_num as i32) };
75
        let colormap = unsafe { x11::xlib::XDefaultColormap(display, screen_num as i32) };
76
77
        let surface = DrawingSurface::new(
78
            display,
79
            window as x11::xlib::Drawable,
80
            width as u32,
81
            height as u32,
82
            visual,
83
            colormap,
84
        )?;
85
86
        Ok(Self {
87
            window,
88
            width,
89
            height,
90
            x_offset: x,
91
            y_offset: y,
92
            graphics_context,
93
            display,
94
            surface,
95
            scheme_normal,
96
            scheme_selected,
97
        })
98
    }
99
100
    pub fn window(&self) -> Window {
101
        self.window
102
    }
103
104
    pub fn draw(
105
        &mut self,
106
        connection: &RustConnection,
107
        font: &Font,
108
        windows: &[(Window, String)],
109
        focused_window: Option<Window>,
110
    ) -> Result<(), X11Error> {
111
        connection.change_gc(
112
            self.graphics_context,
113
            &ChangeGCAux::new().foreground(self.scheme_normal.background),
114
        )?;
115
        connection.flush()?;
116
117
        unsafe {
118
            let gc =
119
                x11::xlib::XCreateGC(self.display, self.surface.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.surface.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.surface.font_draw().draw_text(
162
                font,
163
                scheme.foreground,
164
                text_x,
165
                text_y,
166
                &display_title,
167
            );
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 = x11::xlib::XCreateGC(
175
                        self.display,
176
                        self.surface.pixmap(),
177
                        0,
178
                        std::ptr::null_mut(),
179
                    );
180
                    x11::xlib::XSetForeground(self.display, gc, scheme.underline as u64);
181
                    x11::xlib::XFillRectangle(
182
                        self.display,
183
                        self.surface.pixmap(),
184
                        gc,
185
                        x_position as i32,
186
                        underline_y as i32,
187
                        tab_width as u32,
188
                        underline_height as u32,
189
                    );
190
                    x11::xlib::XFreeGC(self.display, gc);
191
                }
192
            }
193
194
            x_position += tab_width as i16;
195
        }
196
197
        self.copy_pixmap_to_window();
198
        Ok(())
199
    }
200
201
    fn copy_pixmap_to_window(&self) {
202
        unsafe {
203
            let gc =
204
                x11::xlib::XCreateGC(self.display, self.window as u64, 0, std::ptr::null_mut());
205
            x11::xlib::XCopyArea(
206
                self.display,
207
                self.surface.pixmap(),
208
                self.window as u64,
209
                gc,
210
                0,
211
                0,
212
                self.width as u32,
213
                self.height as u32,
214
                0,
215
                0,
216
            );
217
            x11::xlib::XFreeGC(self.display, gc);
218
        }
219
    }
220
221
    pub fn get_clicked_window(&self, windows: &[(Window, String)], click_x: i16) -> Option<Window> {
222
        if windows.is_empty() {
223
            return None;
224
        }
225
226
        let tab_width = self.width / windows.len() as u16;
227
        let tab_index = (click_x as u16 / tab_width) as usize;
228
229
        windows.get(tab_index).map(|&(win, _)| win)
230
    }
231
232
    pub fn reposition(
233
        &mut self,
234
        connection: &RustConnection,
235
        x: i16,
236
        y: i16,
237
        width: u16,
238
    ) -> Result<(), X11Error> {
239
        self.x_offset = x;
240
        self.y_offset = y;
241
        self.width = width;
242
243
        connection.configure_window(
244
            self.window,
245
            &ConfigureWindowAux::new()
246
                .x(x as i32)
247
                .y(y as i32)
248
                .width(width as u32),
249
        )?;
250
251
        let visual = unsafe { x11::xlib::XDefaultVisual(self.display, 0) };
252
        let colormap = unsafe { x11::xlib::XDefaultColormap(self.display, 0) };
253
254
        self.surface = DrawingSurface::new(
255
            self.display,
256
            self.window as x11::xlib::Drawable,
257
            width as u32,
258
            self.height as u32,
259
            visual,
260
            colormap,
261
        )?;
262
263
        connection.flush()?;
264
        Ok(())
265
    }
266
267
    pub fn hide(&self, connection: &RustConnection) -> Result<(), X11Error> {
268
        connection.unmap_window(self.window)?;
269
        connection.flush()?;
270
        Ok(())
271
    }
272
273
    pub fn show(&self, connection: &RustConnection) -> Result<(), X11Error> {
274
        connection.map_window(self.window)?;
275
        connection.flush()?;
276
        Ok(())
277
    }
278
}