oxwm

https://git.tonybtw.com/oxwm.git git://git.tonybtw.com/oxwm.git
12,766 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() {
162
                if block.content().is_ok() {
163
                    self.block_last_updates[i] = now;
164
                    changed = true;
165
                }
166
            }
167
        }
168
169
        if changed {
170
            let mut parts = Vec::new();
171
            for block in &mut self.blocks {
172
                if let Ok(text) = block.content() {
173
                    parts.push(text);
174
                }
175
            }
176
            self.status_text = parts.join("");
177
            self.needs_redraw = true;
178
        }
179
    }
180
181
    pub fn draw(
182
        &mut self,
183
        connection: &RustConnection,
184
        font: &Font,
185
        display: *mut x11::xlib::Display,
186
        current_tags: u32,
187
        occupied_tags: u32,
188
        draw_blocks: bool,
189
        layout_symbol: &str,
190
        keychord_indicator: Option<&str>,
191
    ) -> Result<(), X11Error> {
192
        if !self.needs_redraw {
193
            return Ok(());
194
        }
195
196
        connection.change_gc(
197
            self.graphics_context,
198
            &ChangeGCAux::new().foreground(self.scheme_normal.background),
199
        )?;
200
        connection.flush()?;
201
202
        unsafe {
203
            let gc = x11::xlib::XCreateGC(display, self.pixmap, 0, std::ptr::null_mut());
204
            x11::xlib::XSetForeground(display, gc, self.scheme_normal.background as u64);
205
            x11::xlib::XFillRectangle(
206
                display,
207
                self.pixmap,
208
                gc,
209
                0,
210
                0,
211
                self.width as u32,
212
                self.height as u32,
213
            );
214
            x11::xlib::XFreeGC(display, gc);
215
        }
216
217
        let mut x_position: i16 = 0;
218
219
        for (tag_index, tag) in self.tags.iter().enumerate() {
220
            let tag_mask = 1 << tag_index;
221
            let is_selected = (current_tags & tag_mask) != 0;
222
            let is_occupied = (occupied_tags & tag_mask) != 0;
223
224
            let tag_width = self.tag_widths[tag_index];
225
226
            let scheme = if is_selected {
227
                &self.scheme_selected
228
            } else if is_occupied {
229
                &self.scheme_occupied
230
            } else {
231
                &self.scheme_normal
232
            };
233
234
            let text_width = font.text_width(tag);
235
            let text_x = x_position + ((tag_width - text_width) / 2) as i16;
236
237
            let top_padding = 4;
238
            let text_y = top_padding + font.ascent();
239
240
            self.font_draw
241
                .draw_text(font, scheme.foreground, text_x, text_y, tag);
242
243
            if is_selected {
244
                let font_height = font.height();
245
                let underline_height = font_height / 8;
246
                let bottom_gap = 3;
247
                let underline_y = self.height as i16 - underline_height as i16 - bottom_gap;
248
249
                let underline_padding = 4;
250
                let underline_width = tag_width - underline_padding;
251
                let underline_x = x_position + (underline_padding / 2) as i16;
252
253
                unsafe {
254
                    let gc = x11::xlib::XCreateGC(display, self.pixmap, 0, std::ptr::null_mut());
255
                    x11::xlib::XSetForeground(display, gc, scheme.underline as u64);
256
                    x11::xlib::XFillRectangle(
257
                        display,
258
                        self.pixmap,
259
                        gc,
260
                        underline_x as i32,
261
                        underline_y as i32,
262
                        underline_width as u32,
263
                        underline_height as u32,
264
                    );
265
                    x11::xlib::XFreeGC(display, gc);
266
                }
267
            }
268
269
            x_position += tag_width as i16;
270
        }
271
272
        x_position += 10;
273
274
        let text_x = x_position;
275
        let top_padding = 4;
276
        let text_y = top_padding + font.ascent();
277
278
        self.font_draw.draw_text(
279
            font,
280
            self.scheme_normal.foreground,
281
            text_x,
282
            text_y,
283
            layout_symbol,
284
        );
285
286
        x_position += font.text_width(layout_symbol) as i16;
287
288
        if let Some(indicator) = keychord_indicator {
289
            x_position += 10;
290
291
            let text_x = x_position;
292
            let text_y = top_padding + font.ascent();
293
294
            self.font_draw.draw_text(
295
                font,
296
                self.scheme_selected.foreground,
297
                text_x,
298
                text_y,
299
                indicator,
300
            );
301
        }
302
303
        if draw_blocks && !self.status_text.is_empty() {
304
            let padding = 10;
305
            let mut x_position = self.width as i16 - padding;
306
307
            for (i, block) in self.blocks.iter_mut().enumerate().rev() {
308
                if let Ok(text) = block.content() {
309
                    let text_width = font.text_width(&text);
310
                    x_position -= text_width as i16;
311
312
                    let top_padding = 4;
313
                    let text_y = top_padding + font.ascent();
314
315
                    self.font_draw
316
                        .draw_text(font, block.color(), x_position, text_y, &text);
317
318
                    if self.block_underlines[i] {
319
                        let font_height = font.height();
320
                        let underline_height = font_height / 8;
321
                        let bottom_gap = 3;
322
                        let underline_y = self.height as i16 - underline_height as i16 - bottom_gap;
323
324
                        let underline_padding = 8;
325
                        let underline_width = text_width + underline_padding;
326
                        let underline_x = x_position - (underline_padding / 2) as i16;
327
328
                        unsafe {
329
                            let gc = x11::xlib::XCreateGC(display, self.pixmap, 0, std::ptr::null_mut());
330
                            x11::xlib::XSetForeground(display, gc, block.color() as u64);
331
                            x11::xlib::XFillRectangle(
332
                                display,
333
                                self.pixmap,
334
                                gc,
335
                                underline_x as i32,
336
                                underline_y as i32,
337
                                underline_width as u32,
338
                                underline_height as u32,
339
                            );
340
                            x11::xlib::XFreeGC(display, gc);
341
                        }
342
                    }
343
                }
344
            }
345
        }
346
347
        unsafe {
348
            let gc = x11::xlib::XCreateGC(display, self.window as x11::xlib::Drawable, 0, std::ptr::null_mut());
349
            x11::xlib::XCopyArea(
350
                display,
351
                self.pixmap,
352
                self.window as x11::xlib::Drawable,
353
                gc,
354
                0,
355
                0,
356
                self.width as u32,
357
                self.height as u32,
358
                0,
359
                0,
360
            );
361
            x11::xlib::XFreeGC(display, gc);
362
            x11::xlib::XSync(display, 0);
363
        }
364
365
        self.needs_redraw = false;
366
367
        Ok(())
368
    }
369
370
    pub fn handle_click(&self, click_x: i16) -> Option<usize> {
371
        let mut current_x_position = 0;
372
373
        for (tag_index, &tag_width) in self.tag_widths.iter().enumerate() {
374
            if click_x >= current_x_position && click_x < current_x_position + tag_width as i16 {
375
                return Some(tag_index);
376
            }
377
            current_x_position += tag_width as i16;
378
        }
379
        None
380
    }
381
382
    pub fn needs_redraw(&self) -> bool {
383
        self.needs_redraw
384
    }
385
386
    pub fn update_from_config(&mut self, config: &Config) {
387
        self.blocks = config
388
            .status_blocks
389
            .iter()
390
            .map(|block_config| block_config.to_block())
391
            .collect();
392
393
        self.block_underlines = config
394
            .status_blocks
395
            .iter()
396
            .map(|block_config| block_config.underline)
397
            .collect();
398
399
        self.block_last_updates = vec![Instant::now(); self.blocks.len()];
400
401
        self.tags = config.tags.clone();
402
        self.scheme_normal = config.scheme_normal;
403
        self.scheme_occupied = config.scheme_occupied;
404
        self.scheme_selected = config.scheme_selected;
405
406
        self.status_text.clear();
407
        self.needs_redraw = true;
408
    }
409
}
410
411
impl Drop for Bar {
412
    fn drop(&mut self) {
413
        unsafe {
414
            x11::xlib::XFreePixmap(self.display, self.pixmap);
415
        }
416
    }
417
}