cargo fmt
Getty Ritter
5 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 | } |