cargo fmt
Getty Ritter
4 years ago
66 | 66 | } |
67 | 67 | |
68 | 68 | if let Some(color) = table.get("background") { |
69 |
conf.bg_color = color_from_hex( |
|
69 | conf.bg_color = color_from_hex( | |
70 | color | |
71 | .as_str() | |
72 | .ok_or(format_err!("`background` not a str"))?, | |
73 | )?; | |
70 | 74 | } |
71 | 75 | if let Some(color) = table.get("foreground") { |
72 |
conf.fg_color = color_from_hex( |
|
76 | conf.fg_color = color_from_hex( | |
77 | color | |
78 | .as_str() | |
79 | .ok_or(format_err!("`foreground` not a str"))?, | |
80 | )?; | |
73 | 81 | } |
74 | 82 | if let Some(font) = table.get("font") { |
75 |
conf.font = font |
|
83 | conf.font = font | |
84 | .as_str() | |
85 | .ok_or(format_err!("`font` not a str"))? | |
86 | .to_string(); | |
76 | 87 | } |
77 | 88 | conf.right.reverse(); |
78 | 89 | |
96 | 107 | Err(format_err!("Unable to find `knurling.toml`")) |
97 | 108 | } |
98 | 109 | |
99 |
pub fn draw( |
|
110 | pub fn draw( | |
111 | &self, | |
112 | ctx: &cairo::Context, | |
113 | layout: &pango::Layout, | |
114 | stdin: &str, | |
115 | size: w::Size, | |
116 | ) -> Result<(), failure::Error> { | |
100 | 117 | // paint the background |
101 | 118 | { |
102 | 119 | let (r, g, b) = self.bg_color; |
135 | 152 | &self.font |
136 | 153 | } |
137 | 154 | |
138 |
pub fn get_height(&self) -> i32 |
|
155 | pub fn get_height(&self) -> i32 { | |
139 | 156 | self.height |
140 | 157 | } |
141 | 158 | |
144 | 161 | |
145 | 162 | // we get the height here by making a fake surface, rendering |
146 | 163 | // some text using our chosen font to it, and seeing how big it ends up being |
147 | let surf = cairo::ImageSurface::create( | |
148 | cairo::Format::Rgb24, 0, 0).unwrap(); | |
164 | let surf = cairo::ImageSurface::create(cairo::Format::Rgb24, 0, 0).unwrap(); | |
149 | 165 | let ctx = cairo::Context::new(&surf); |
150 | 166 | let layout = pangocairo::functions::create_layout(&ctx).unwrap(); |
151 | 167 | layout.set_width(800 * pango::SCALE); |
5 | 5 | mod widgets; |
6 | 6 | mod window; |
7 | 7 | |
8 | use pango::LayoutExt; | |
8 | 9 | use std::os::unix::io::AsRawFd; |
9 | use pango::LayoutExt; | |
10 | 10 | |
11 | 11 | use widgets::Size; |
12 |
use window::{Display, |
|
12 | use window::{Display, Event, Window}; | |
13 | 13 | |
14 | 14 | fn main() -> Result<(), failure::Error> { |
15 | 15 | // set up the display and the window |
20 | 20 | let mut ws = Vec::new(); |
21 | 21 | |
22 | 22 | for (x_off, wd) in d.get_widths()? { |
23 |
let size = Size { |
|
23 | let size = Size { | |
24 | wd, | |
25 | ht: height, | |
26 | xo: x_off, | |
27 | yo: 0, | |
28 | }; | |
24 | 29 | let mut w = Window::create(&d, size)?; |
25 | 30 | // set some window-manager properties: this is a dock |
26 | 31 | w.change_property("_NET_WM_WINDOW_TYPE", &["_NET_WM_WINDOW_TYPE_DOCK"])?; |
28 | 33 | w.change_property("_NET_WM_STRUT", &[x_off as i64, 0, size.ht as i64, 0])?; |
29 | 34 | w.change_property( |
30 | 35 | "_NET_WM_STRUT_PARTIAL", |
31 | &[ 0, 0, size.ht as i64, 0, | |
32 | 0, 0, 0, 0, | |
33 | 0, size.wd as i64, 0, 0, | |
34 | ], | |
36 | &[0, 0, size.ht as i64, 0, 0, 0, 0, 0, 0, size.wd as i64, 0, 0], | |
35 | 37 | )?; |
36 | 38 | |
37 | 39 | // we won't ever see this, but for good measure. |
69 | 71 | let surf = w.get_cairo_surface(); |
70 | 72 | let ctx = cairo::Context::new(&surf); |
71 | 73 | |
72 | ||
73 | 74 | let layout = pangocairo::functions::create_layout(&ctx) |
74 | 75 | .ok_or(format_err!("unable to create layout"))?; |
75 | 76 | |
85 | 86 | |
86 | 87 | ctxs.push((ctx, layout, w.size())); |
87 | 88 | } |
88 | ||
89 | 89 | |
90 | 90 | let max_fd = window_fds.iter().max().unwrap_or(&0) + 1; |
91 | 91 | // we're gonna keep looping until we don't |
1 | 1 | use pango::LayoutExt; |
2 | 2 | |
3 |
#[derive(Debug, |
|
3 | #[derive(Debug, Clone, Copy)] | |
4 | 4 | pub struct Size { |
5 | 5 | pub wd: i32, |
6 | 6 | pub ht: i32, |
8 | 8 | pub yo: i32, |
9 | 9 | } |
10 | 10 | |
11 |
#[derive(Debug, |
|
11 | #[derive(Debug, Clone, Copy)] | |
12 | 12 | pub enum Located { |
13 | 13 | FromLeft(i32), |
14 | 14 | FromRight(i32), |
51 | 51 | impl Time { |
52 | 52 | pub fn new() -> Time { |
53 | 53 | Time { |
54 |
fmt: format!("%a %b %d %H:%M") |
|
54 | fmt: format!("%a %b %d %H:%M"), | |
55 | 55 | } |
56 | 56 | } |
57 | 57 | } |
62 | 62 | loc.draw_text(d, &format!("{}", &now.format(&self.fmt))) |
63 | 63 | } |
64 | 64 | } |
65 | ||
66 | 65 | |
67 | 66 | #[derive(Debug)] |
68 | 67 | pub struct Stdin; |
78 | 77 | loc.draw_text(d, &d.stdin) |
79 | 78 | } |
80 | 79 | } |
81 | ||
82 | 80 | |
83 | 81 | pub struct SmallBox; |
84 | 82 | |
106 | 104 | |
107 | 105 | pub struct Battery { |
108 | 106 | file_list: Vec<std::path::PathBuf>, |
109 |
charging: Option<std::path::PathBuf> |
|
107 | charging: Option<std::path::PathBuf>, | |
110 | 108 | } |
111 | 109 | |
112 | 110 | impl Battery { |
136 | 134 | |
137 | 135 | fn is_charging(&self) -> Result<bool, failure::Error> { |
138 | 136 | if let Some(path) = &self.charging { |
139 | let is_connected: i32 = | |
140 | std::fs::read_to_string(path)?.trim().parse()?; | |
137 | let is_connected: i32 = std::fs::read_to_string(path)?.trim().parse()?; | |
141 | 138 | Ok(is_connected != 0) |
142 | 139 | } else { |
143 | 140 | Ok(false) |
145 | 142 | } |
146 | 143 | |
147 | 144 | fn read_status(&self) -> Result<f64, failure::Error> { |
148 | let charges: Result<Vec<i32>, failure::Error> = | |
149 | self.file_list.iter().map(|path| { | |
150 | Ok(std::fs::read_to_string(path)?.trim().parse()?) | |
151 | }).collect(); | |
145 | let charges: Result<Vec<i32>, failure::Error> = self | |
146 | .file_list | |
147 | .iter() | |
148 | .map(|path| Ok(std::fs::read_to_string(path)?.trim().parse()?)) | |
149 | .collect(); | |
152 | 150 | let charges = charges?; |
153 | 151 | |
154 | 152 | let len = charges.len() as f64; |
163 | 161 | let sz = d.size.ht - (d.buffer as i32 * 2); |
164 | 162 | let x = loc.target_x(d, sz); |
165 | 163 | match amt { |
166 | _ if self.is_charging().unwrap_or(false) => | |
167 | d.ctx.set_source_rgb(0.5, 0.5, 1.0), | |
168 | Ok(x) if x < 0.1 => | |
169 | d.ctx.set_source_rgb(1.0, 0.0, 0.0), | |
170 | Ok(x) if x < 0.5 => | |
171 | d.ctx.set_source_rgb(1.0, 1.0, 0.0), | |
172 | Ok(_) => | |
173 | d.ctx.set_source_rgb(0.0, 1.0, 0.5), | |
174 | Err(_) => | |
175 | d.ctx.set_source_rgb(0.0, 0.0, 0.0), | |
164 | _ if self.is_charging().unwrap_or(false) => d.ctx.set_source_rgb(0.5, 0.5, 1.0), | |
165 | Ok(x) if x < 0.1 => d.ctx.set_source_rgb(1.0, 0.0, 0.0), | |
166 | Ok(x) if x < 0.5 => d.ctx.set_source_rgb(1.0, 1.0, 0.0), | |
167 | Ok(_) => d.ctx.set_source_rgb(0.0, 1.0, 0.5), | |
168 | Err(_) => d.ctx.set_source_rgb(0.0, 0.0, 0.0), | |
176 | 169 | } |
177 | 170 | |
178 | 171 | d.ctx.rectangle( |
184 | 177 | d.ctx.fill(); |
185 | 178 | |
186 | 179 | d.ctx.set_source_rgb(1.0, 1.0, 1.0); |
187 |
d.ctx |
|
180 | d.ctx | |
181 | .rectangle(x, d.buffer * 2.0, sz as f64, sz as f64 - (d.buffer * 2.0)); | |
188 | 182 | d.ctx.stroke(); |
189 | 183 | |
190 | 184 | sz |
1 |
use x11::{x |
|
1 | use x11::{xinput2, xlib}; | |
2 | 2 | |
3 | 3 | use std::ffi::CString; |
4 | use std::{mem,ptr}; | |
5 | use std::os::raw::{c_int,c_uchar}; | |
4 | use std::os::raw::{c_int, c_uchar}; | |
5 | use std::{mem, ptr}; | |
6 | 6 | |
7 | 7 | use crate::widgets::Size; |
8 | 8 | |
28 | 28 | } |
29 | 29 | } |
30 | 30 | |
31 |
pub fn get_widths(&mut self) -> Result<Vec<(i32, |
|
31 | pub fn get_widths(&mut self) -> Result<Vec<(i32, i32)>, failure::Error> { | |
32 | 32 | if unsafe { x11::xinerama::XineramaIsActive(self.display) != 0 } { |
33 | 33 | let mut screens = 0; |
34 |
let screen_info = |
|
34 | let screen_info = | |
35 | unsafe { x11::xinerama::XineramaQueryScreens(self.display, &mut screens) }; | |
35 | 36 | let mut widths = Vec::new(); |
36 | 37 | for i in 0..screens { |
37 | 38 | unsafe { |
38 |
let si = screen_info |
|
39 | let si = screen_info | |
40 | .offset(i as isize) | |
41 | .as_ref() | |
42 | .ok_or(format_err!("bad pointer"))?; | |
39 | 43 | widths.push((si.x_org as i32, si.width as i32)); |
40 | 44 | } |
41 | 45 | } |
75 | 79 | /// width and height |
76 | 80 | pub fn create( |
77 | 81 | display: &'t Display, |
78 |
Size { |
|
82 | Size { | |
83 | wd: width, | |
84 | ht: height, | |
85 | xo, | |
86 | yo, | |
87 | }: Size, | |
79 | 88 | ) -> Result<Window<'t>, failure::Error> { |
80 | 89 | unsafe { |
81 | 90 | let screen = display.screen; |
128 | 137 | ); |
129 | 138 | } |
130 | 139 | |
131 |
let mut mask: [c_uchar; |
|
140 | let mut mask: [c_uchar; 1] = [0]; | |
132 | 141 | let mut input_event_mask = xinput2::XIEventMask { |
133 | 142 | deviceid: xinput2::XIAllMasterDevices, |
134 | 143 | mask_len: mask.len() as i32, |
135 | 144 | mask: mask.as_mut_ptr(), |
136 | 145 | }; |
137 | let events = &[ | |
138 | xinput2::XI_ButtonPress, | |
139 | xinput2::XI_ButtonRelease, | |
140 | ]; | |
146 | let events = &[xinput2::XI_ButtonPress, xinput2::XI_ButtonRelease]; | |
141 | 147 | for &event in events { |
142 | 148 | xinput2::XISetMask(&mut mask, event); |
143 | 149 | } |
144 | 150 | |
145 | 151 | match unsafe { |
146 | xinput2::XISelectEvents( | |
147 | self.display.display, | |
148 | self.window, | |
149 | &mut input_event_mask, | |
150 | 1, | |
151 | ) | |
152 | xinput2::XISelectEvents(self.display.display, self.window, &mut input_event_mask, 1) | |
152 | 153 | } { |
153 | 154 | status if status as u8 == xlib::Success => (), |
154 |
err => bail!("Failed to select events {:?}", err) |
|
155 | err => bail!("Failed to select events {:?}", err), | |
155 | 156 | } |
156 | 157 | |
157 | 158 | Ok(()) |
201 | 202 | pub fn change_property<T: XProperty>( |
202 | 203 | &mut self, |
203 | 204 | prop: &str, |
204 | val: &[T] | |
205 | ) -> Result<(), failure::Error> | |
206 |
|
|
205 | val: &[T], | |
206 | ) -> Result<(), failure::Error> { | |
207 | 207 | let prop = self.intern(prop)?; |
208 | 208 | unsafe { |
209 | 209 | let len = val.len(); |
268 | 268 | xlib::GenericEvent => { |
269 | 269 | let mut cookie: xlib::XGenericEventCookie = unsafe { From::from(*e.as_ptr()) }; |
270 | 270 | unsafe { xlib::XGetEventData(self.display.display, &mut cookie) }; |
271 | match cookie.evtype { | |
272 | xinput2::XI_ButtonPress => { | |
273 | let data: &xinput2::XIDeviceEvent = | |
274 | unsafe { mem::transmute(cookie.data) }; | |
275 | return Some(Event::MouseEvent { x: data.event_x, y: data.event_y }); | |
276 | } | |
277 |
|
|
271 | match cookie.evtype { | |
272 | xinput2::XI_ButtonPress => { | |
273 | let data: &xinput2::XIDeviceEvent = unsafe { mem::transmute(cookie.data) }; | |
274 | return Some(Event::MouseEvent { | |
275 | x: data.event_x, | |
276 | y: data.event_y, | |
277 | }); | |
278 | 278 | } |
279 | _ => (), | |
280 | } | |
279 | 281 | } |
280 | 282 | _ => (), |
281 | 283 | } |
285 | 287 | |
286 | 288 | /// True if there are any pending events. |
287 | 289 | pub fn has_events(&mut self) -> bool { |
288 | unsafe { | |
289 | xlib::XPending(self.display.display) != 0 | |
290 |
|
|
290 | unsafe { xlib::XPending(self.display.display) != 0 } | |
291 | 291 | } |
292 | 292 | |
293 | 293 | /// Did you know that X11 uses a file descriptor underneath the |
294 | 294 | /// surface to wait on events? This lets us use select on it! |
295 | 295 | pub fn get_fd(&mut self) -> i32 { |
296 | unsafe { | |
297 | xlib::XConnectionNumber(self.display.display) | |
298 |
|
|
296 | unsafe { xlib::XConnectionNumber(self.display.display) } | |
299 | 297 | } |
300 | 298 | |
301 | 299 | pub fn size(&self) -> Size { |
302 |
Size { |
|
300 | Size { | |
301 | wd: self.width, | |
302 | ht: self.height, | |
303 | xo: 0, | |
304 | yo: 0, | |
305 | } | |
303 | 306 | } |
304 | 307 | } |
305 | 308 | |
306 | 309 | /// A trait for abstracting over different values which are allowed |
307 | 310 | /// for xlib properties |
308 |
pub trait XProperty |
|
311 | pub trait XProperty: Sized { | |
309 | 312 | fn with_ptr( |
310 | 313 | xs: &[Self], |
311 | 314 | w: &mut Window, |
312 | 315 | f: impl FnOnce(&mut Window, u64, *const u8), |
313 |
) -> Result<(), failure::Error> |
|
316 | ) -> Result<(), failure::Error>; | |
314 | 317 | } |
315 | 318 | |
316 | 319 | impl XProperty for i64 { |
330 | 333 | w: &mut Window, |
331 | 334 | f: impl FnOnce(&mut Window, u64, *const u8), |
332 | 335 | ) -> Result<(), failure::Error> { |
333 | let xs: Result<Vec<u64>, failure::Error> = | |
334 | xs.iter().map(|s| w.intern(s)).collect(); | |
336 | let xs: Result<Vec<u64>, failure::Error> = xs.iter().map(|s| w.intern(s)).collect(); | |
335 | 337 | f(w, xlib::XA_ATOM, unsafe { mem::transmute(xs?.as_ptr()) }); |
336 | 338 | Ok(()) |
337 | 339 | } |
341 | 343 | /// way |
342 | 344 | #[derive(Debug)] |
343 | 345 | pub enum Event { |
344 |
MouseEvent { x: |
|
346 | MouseEvent { x: f64, y: f64 }, | |
345 | 347 | ShowEvent, |
346 | 348 | QuitEvent, |
347 | 349 | } |