oxwm

https://git.tonybtw.com/oxwm.git git://git.tonybtw.com/oxwm.git
12,833 bytes raw
1
use super::blocks::Block;
2
use super::font::{Font, FontDraw};
3
use crate::Config;
4
use crate::errors::X11Error;
5
use std::time::Instant;
6
use x11rb::COPY_DEPTH_FROM_PARENT;
7
use x11rb::connection::Connection;
8
use x11rb::protocol::xproto::*;
9
use x11rb::rust_connection::RustConnection;
10
11
pub struct Bar {
12
    window: Window,
13
    width: u16,
14
    height: u16,
15
    graphics_context: Gcontext,
16
    pixmap: x11::xlib::Pixmap,
17
    display: *mut x11::xlib::Display,
18
19
    font_draw: FontDraw,
20
21
    tag_widths: Vec<u16>,
22
    needs_redraw: bool,
23
24
    blocks: Vec<Box<dyn Block>>,
25
    block_last_updates: Vec<Instant>,
26
    block_underlines: Vec<bool>,
27
    status_text: String,
28
29
    tags: Vec<String>,
30
    scheme_normal: crate::ColorScheme,
31
    scheme_occupied: crate::ColorScheme,
32
    scheme_selected: crate::ColorScheme,
33
}
34
35
impl Bar {
36
    pub fn new(
37
        connection: &RustConnection,
38
        screen: &Screen,
39
        screen_num: usize,
40
        config: &Config,
41
        display: *mut x11::xlib::Display,
42
        font: &Font,
43
        x: i16,
44
        y: i16,
45
        width: u16,
46
    ) -> Result<Self, X11Error> {
47
        let window = connection.generate_id()?;
48
        let graphics_context = connection.generate_id()?;
49
50
        let height = (font.height() as f32 * 1.4) as u16;
51
52
        connection.create_window(
53
            COPY_DEPTH_FROM_PARENT,
54
            window,
55
            screen.root,
56
            x,
57
            y,
58
            width,
59
            height,
60
            0,
61
            WindowClass::INPUT_OUTPUT,
62
            screen.root_visual,
63
            &CreateWindowAux::new()
64
                .background_pixel(config.scheme_normal.background)
65
                .event_mask(EventMask::EXPOSURE | EventMask::BUTTON_PRESS)
66
                .override_redirect(1),
67
        )?;
68
69
        connection.create_gc(
70
            graphics_context,
71
            window,
72
            &CreateGCAux::new()
73
                .foreground(config.scheme_normal.foreground)
74
                .background(config.scheme_normal.background),
75
        )?;
76
77
        connection.map_window(window)?;
78
        connection.flush()?;
79
80
        let visual = unsafe { x11::xlib::XDefaultVisual(display, screen_num as i32) };
81
        let colormap = unsafe { x11::xlib::XDefaultColormap(display, screen_num as i32) };
82
        let depth = unsafe { x11::xlib::XDefaultDepth(display, screen_num as i32) };
83
84
        let pixmap = unsafe {
85
            x11::xlib::XCreatePixmap(
86
                display,
87
                window as x11::xlib::Drawable,
88
                width as u32,
89
                height as u32,
90
                depth as u32,
91
            )
92
        };
93
94
        let font_draw = FontDraw::new(display, pixmap, visual, colormap)?;
95
96
        let horizontal_padding = (font.height() as f32 * 0.4) as u16;
97
98
        let tag_widths = config
99
            .tags
100
            .iter()
101
            .map(|tag| {
102
                let text_width = font.text_width(tag);
103
                text_width + (horizontal_padding * 2)
104
            })
105
            .collect();
106
107
        let blocks: Vec<Box<dyn Block>> = config
108
            .status_blocks
109
            .iter()
110
            .map(|block_config| block_config.to_block())
111
            .collect();
112
113
        let block_underlines: Vec<bool> = config
114
            .status_blocks
115
            .iter()
116
            .map(|block_config| block_config.underline)
117
            .collect();
118
119
        let block_last_updates = vec![Instant::now(); blocks.len()];
120
121
        Ok(Bar {
122
            window,
123
            width,
124
            height,
125
            graphics_context,
126
            pixmap,
127
            display,
128
            font_draw,
129
            tag_widths,
130
            needs_redraw: true,
131
            blocks,
132
            block_last_updates,
133
            block_underlines,
134
            status_text: String::new(),
135
            tags: config.tags.clone(),
136
            scheme_normal: config.scheme_normal,
137
            scheme_occupied: config.scheme_occupied,
138
            scheme_selected: config.scheme_selected,
139
        })
140
    }
141
142
    pub fn window(&self) -> Window {
143
        self.window
144
    }
145
146
    pub fn height(&self) -> u16 {
147
        self.height
148
    }
149
150
    pub fn invalidate(&mut self) {
151
        self.needs_redraw = true;
152
    }
153
154
    pub fn update_blocks(&mut self) {
155
        let now = Instant::now();
156
        let mut changed = false;
157
158
        for (i, block) in self.blocks.iter_mut().enumerate() {
159
            let elapsed = now.duration_since(self.block_last_updates[i]);
160
161
            if elapsed >= block.interval() && block.content().is_ok() {
162
                self.block_last_updates[i] = now;
163
                changed = true;
164
            }
165
        }
166
167
        if changed {
168
            let mut parts = Vec::new();
169
            for block in &mut self.blocks {
170
                if let Ok(text) = block.content() {
171
                    parts.push(text);
172
                }
173
            }
174
            self.status_text = parts.join("");
175
            self.needs_redraw = true;
176
        }
177
    }
178
179
    pub fn draw(
180
        &mut self,
181
        connection: &RustConnection,
182
        font: &Font,
183
        display: *mut x11::xlib::Display,
184
        current_tags: u32,
185
        occupied_tags: u32,
186
        draw_blocks: bool,
187
        layout_symbol: &str,
188
        keychord_indicator: Option<&str>,
189
    ) -> Result<(), X11Error> {
190
        if !self.needs_redraw {
191
            return Ok(());
192
        }
193
194
        connection.change_gc(
195
            self.graphics_context,
196
            &ChangeGCAux::new().foreground(self.scheme_normal.background),
197
        )?;
198
        connection.flush()?;
199
200
        unsafe {
201
            let gc = x11::xlib::XCreateGC(display, self.pixmap, 0, std::ptr::null_mut());
202
            x11::xlib::XSetForeground(display, gc, self.scheme_normal.background as u64);
203
            x11::xlib::XFillRectangle(
204
                display,
205
                self.pixmap,
206
                gc,
207
                0,
208
                0,
209
                self.width as u32,
210
                self.height as u32,
211
            );
212
            x11::xlib::XFreeGC(display, gc);
213
        }
214
215
        let mut x_position: i16 = 0;
216
217
        for (tag_index, tag) in self.tags.iter().enumerate() {
218
            let tag_mask = 1 << tag_index;
219
            let is_selected = (current_tags & tag_mask) != 0;
220
            let is_occupied = (occupied_tags & tag_mask) != 0;
221
222
            let tag_width = self.tag_widths[tag_index];
223
224
            let scheme = if is_selected {
225
                &self.scheme_selected
226
            } else if is_occupied {
227
                &self.scheme_occupied
228
            } else {
229
                &self.scheme_normal
230
            };
231
232
            let text_width = font.text_width(tag);
233
            let text_x = x_position + ((tag_width - text_width) / 2) as i16;
234
235
            let top_padding = 4;
236
            let text_y = top_padding + font.ascent();
237
238
            self.font_draw
239
                .draw_text(font, scheme.foreground, text_x, text_y, tag);
240
241
            if is_selected {
242
                let font_height = font.height();
243
                let underline_height = font_height / 8;
244
                let bottom_gap = 3;
245
                let underline_y = self.height as i16 - underline_height as i16 - bottom_gap;
246
247
                let underline_padding = 4;
248
                let underline_width = tag_width - underline_padding;
249
                let underline_x = x_position + (underline_padding / 2) as i16;
250
251
                unsafe {
252
                    let gc = x11::xlib::XCreateGC(display, self.pixmap, 0, std::ptr::null_mut());
253
                    x11::xlib::XSetForeground(display, gc, scheme.underline as u64);
254
                    x11::xlib::XFillRectangle(
255
                        display,
256
                        self.pixmap,
257
                        gc,
258
                        underline_x as i32,
259
                        underline_y as i32,
260
                        underline_width as u32,
261
                        underline_height as u32,
262
                    );
263
                    x11::xlib::XFreeGC(display, gc);
264
                }
265
            }
266
267
            x_position += tag_width as i16;
268
        }
269
270
        x_position += 10;
271
272
        let text_x = x_position;
273
        let top_padding = 4;
274
        let text_y = top_padding + font.ascent();
275
276
        self.font_draw.draw_text(
277
            font,
278
            self.scheme_normal.foreground,
279
            text_x,
280
            text_y,
281
            layout_symbol,
282
        );
283
284
        x_position += font.text_width(layout_symbol) as i16;
285
286
        if let Some(indicator) = keychord_indicator {
287
            x_position += 10;
288
289
            let text_x = x_position;
290
            let text_y = top_padding + font.ascent();
291
292
            self.font_draw.draw_text(
293
                font,
294
                self.scheme_selected.foreground,
295
                text_x,
296
                text_y,
297
                indicator,
298
            );
299
        }
300
301
        if draw_blocks && !self.status_text.is_empty() {
302
            let padding = 10;
303
            let mut x_position = self.width as i16 - padding;
304
305
            for (i, block) in self.blocks.iter_mut().enumerate().rev() {
306
                if let Ok(text) = block.content() {
307
                    let text_width = font.text_width(&text);
308
                    x_position -= text_width as i16;
309
310
                    let top_padding = 4;
311
                    let text_y = top_padding + font.ascent();
312
313
                    self.font_draw
314
                        .draw_text(font, block.color(), x_position, text_y, &text);
315
316
                    if self.block_underlines[i] {
317
                        let font_height = font.height();
318
                        let underline_height = font_height / 8;
319
                        let bottom_gap = 3;
320
                        let underline_y = self.height as i16 - underline_height as i16 - bottom_gap;
321
322
                        let underline_padding = 8;
323
                        let underline_width = text_width + underline_padding;
324
                        let underline_x = x_position - (underline_padding / 2) as i16;
325
326
                        unsafe {
327
                            let gc =
328
                                x11::xlib::XCreateGC(display, self.pixmap, 0, std::ptr::null_mut());
329
                            x11::xlib::XSetForeground(display, gc, block.color() as u64);
330
                            x11::xlib::XFillRectangle(
331
                                display,
332
                                self.pixmap,
333
                                gc,
334
                                underline_x as i32,
335
                                underline_y as i32,
336
                                underline_width as u32,
337
                                underline_height as u32,
338
                            );
339
                            x11::xlib::XFreeGC(display, gc);
340
                        }
341
                    }
342
                }
343
            }
344
        }
345
346
        unsafe {
347
            let gc = x11::xlib::XCreateGC(
348
                display,
349
                self.window as x11::xlib::Drawable,
350
                0,
351
                std::ptr::null_mut(),
352
            );
353
            x11::xlib::XCopyArea(
354
                display,
355
                self.pixmap,
356
                self.window as x11::xlib::Drawable,
357
                gc,
358
                0,
359
                0,
360
                self.width as u32,
361
                self.height as u32,
362
                0,
363
                0,
364
            );
365
            x11::xlib::XFreeGC(display, gc);
366
            x11::xlib::XSync(display, 0);
367
        }
368
369
        self.needs_redraw = false;
370
371
        Ok(())
372
    }
373
374
    pub fn handle_click(&self, click_x: i16) -> Option<usize> {
375
        let mut current_x_position = 0;
376
377
        for (tag_index, &tag_width) in self.tag_widths.iter().enumerate() {
378
            if click_x >= current_x_position && click_x < current_x_position + tag_width as i16 {
379
                return Some(tag_index);
380
            }
381
            current_x_position += tag_width as i16;
382
        }
383
        None
384
    }
385
386
    pub fn needs_redraw(&self) -> bool {
387
        self.needs_redraw
388
    }
389
390
    pub fn update_from_config(&mut self, config: &Config) {
391
        self.blocks = config
392
            .status_blocks
393
            .iter()
394
            .map(|block_config| block_config.to_block())
395
            .collect();
396
397
        self.block_underlines = config
398
            .status_blocks
399
            .iter()
400
            .map(|block_config| block_config.underline)
401
            .collect();
402
403
        self.block_last_updates = vec![Instant::now(); self.blocks.len()];
404
405
        self.tags = config.tags.clone();
406
        self.scheme_normal = config.scheme_normal;
407
        self.scheme_occupied = config.scheme_occupied;
408
        self.scheme_selected = config.scheme_selected;
409
410
        self.status_text.clear();
411
        self.needs_redraw = true;
412
    }
413
}
414
415
impl Drop for Bar {
416
    fn drop(&mut self) {
417
        unsafe {
418
            x11::xlib::XFreePixmap(self.display, self.pixmap);
419
        }
420
    }
421
}