diff --git a/src/backlight.rs b/src/backlight.rs new file mode 100644 index 0000000..055bca2 --- /dev/null +++ b/src/backlight.rs @@ -0,0 +1,135 @@ +use crate::module::{ModuleImpl, Input}; +use crate::buffer::Buffer; +use crate::color::Color; +use crate::draw::{draw_text, ROBOTO_REGULAR}; + +use std::fs::OpenOptions; +use std::path::{Path, PathBuf}; +use std::io::{Read, Write}; +use std::io::{Error, ErrorKind}; +use chrono::{DateTime, Local}; + +pub struct Backlight { + device_path: PathBuf, + cur_brightness: u64, + max_brightness: u64, + dirty: bool, +} + +fn read_file_as_u64(path: &Path) -> Result { + let mut file = OpenOptions::new() + .read(true) + .open(path)?; + let mut str = String::new(); + file.read_to_string(&mut str)?; + str.pop(); + str.parse::().map_err(|_e| {Error::new(ErrorKind::Other, "unable to parse value")}) +} + +fn write_file_as_u64(path: &Path, value: u64) -> Result<(), Error> { + let mut file = OpenOptions::new() + .write(true) + .open(path)?; + file.write_fmt(format_args!("{}", value)) +} + +impl Backlight { + pub fn update(&mut self) -> Result<(), Error> { + self.cur_brightness = read_file_as_u64(self.device_path.join("brightness").as_path())?; + self.max_brightness = read_file_as_u64(self.device_path.join("max_brightness").as_path())?; + self.dirty = true; + Ok(()) + } + + pub fn sync(&mut self) -> Result<(), Error> { + write_file_as_u64(self.device_path.join("brightness").as_path(), self.cur_brightness)?; + self.update()?; + Ok(()) + } + + pub fn brightness(&self) -> f32 { + if self.cur_brightness > self.max_brightness { + // what. + return 100.0; + } + + (self.cur_brightness as f32 / self.max_brightness as f32) * 100.0 + } + + pub fn add(&mut self, diff: f32) -> Result<(), Error> { + let inc = ((self.max_brightness as f32) / 100.0 * diff) as i64; + + self.cur_brightness = if self.cur_brightness as i64 + inc < 1 { + 1 + } else if self.cur_brightness as i64 + inc > self.max_brightness as i64 { + self.max_brightness + } else { + (self.cur_brightness as i64 + inc) as u64 + }; + + Ok(()) + } + + pub fn default() -> Result { + let devices = Path::new("/sys/class/backlight") + .read_dir()?; + + let first_device = devices.take(1).next().unwrap().unwrap(); + let mut dev = Backlight{ + device_path: first_device.path(), + cur_brightness: 0, + max_brightness: 0, + dirty: true, + }; + + dev.update()?; + + Ok(dev) + } +} + +impl ModuleImpl for Backlight { + fn draw(&self, buf: &mut Buffer, bg: &Color, _time: &DateTime) -> Result, Error> { + buf.memset(bg); + draw_text( + ROBOTO_REGULAR, + &mut buf.subdimensions((0, 0, 256, 24)), + bg, + &Color::new(1.0, 1.0, 1.0, 1.0), + 24.0, + &format!("backlight: {:}%", self.brightness() as u8), + )?; + Ok(vec![buf.get_signed_bounds()]) + } + + fn update(&mut self, _time: &DateTime, force: bool) -> Result{ + if self.dirty || force { + self.dirty = false; + Ok(true) + } else { + Ok(false) + } + } + + fn input(&mut self, input: Input) { + match input { + Input::Scroll{pos: _pos, x: _x, y} => { + self.add((y * -1.0) as f32 / 8.0).unwrap(); + self.sync().unwrap(); + }, + Input::Click{pos: _pos, button} => { + match button { + 273 => { // Right click + self.cur_brightness = if self.cur_brightness == 1 { + self.max_brightness + } else { + 1 + }; + self.sync().unwrap(); + }, + _ => {} + } + }, + } + } +} \ No newline at end of file diff --git a/src/buffer.rs b/src/buffer.rs index 4ea2354..14ca593 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -25,6 +25,19 @@ impl<'a> Buffer<'a> { } } + pub fn get_signed_bounds(&self) -> (i32, i32, i32, i32) { + if let Some(subdim) = self.subdimensions { + ( + subdim.0 as i32, + subdim.1 as i32, + subdim.2 as i32, + subdim.3 as i32, + ) + } else { + (0, 0, self.dimensions.0 as i32, self.dimensions.1 as i32) + } + } + pub fn subdimensions(&mut self, subdimensions: (u32, u32, u32, u32)) -> Buffer { let bounds = self.get_bounds(); if subdimensions.0 + subdimensions.2 > bounds.2 @@ -49,10 +62,22 @@ impl<'a> Buffer<'a> { } pub fn memset(&mut self, c: &Color) { - unsafe { - let ptr = self.buf.as_mut_ptr(); - for p in 0..(self.dimensions.0 * self.dimensions.1) { - *((ptr as *mut u32).offset(p as isize)) = c.as_argb8888(); + if let Some(subdim) = self.subdimensions { + unsafe { + let ptr = self.buf.as_mut_ptr(); + for y in subdim.1..(subdim.1 + subdim.3) { + for x in subdim.0..(subdim.0 + subdim.2) { + *((ptr as *mut u32).offset((x + y * self.dimensions.0) as isize)) = + c.as_argb8888(); + } + } + } + } else { + unsafe { + let ptr = self.buf.as_mut_ptr(); + for p in 0..(self.dimensions.0 * self.dimensions.1) { + *((ptr as *mut u32).offset(p as isize)) = c.as_argb8888(); + } } } } diff --git a/src/calendar.rs b/src/calendar.rs new file mode 100644 index 0000000..1c06f2c --- /dev/null +++ b/src/calendar.rs @@ -0,0 +1,197 @@ +use crate::buffer::Buffer; +use crate::color::Color; +use crate::module::{ModuleImpl, Input}; +use crate::draw::{draw_text, ROBOTO_REGULAR, DEJAVUSANS_MONO}; +use chrono::{DateTime, Date, Datelike, Local}; + +pub struct Calendar { + cur_date: Date, + first_draw: bool, +} + +fn draw_month( + buf: &mut Buffer, + background_color: &Color, + orig: &Date, + time: &Date, +) -> Result<(i32, i32, i32, i32), ::std::io::Error> { + buf.memset(background_color); + + let mut time = time.clone(); + let mut y_off = 1; + let mut done = false; + + let month_str = match time.month() { + 1 => "January", + 2 => "February", + 3 => "March", + 4 => "April", + 5 => "May", + 6 => "June", + 7 => "July", + 8 => "August", + 9 => "September", + 10 => "October", + 11 => "November", + 12 => "December", + _ => panic!("impossible value"), + }; + + // + // Draw the of the month + // + + draw_text( + ROBOTO_REGULAR, + &mut buf.subdimensions((0, 0, 304, 72)), + background_color, + &Color::new(1.0, 1.0, 1.0, 1.0), + 68.0, + month_str, + )?; + + // + // Draw the week day + // + + for idx in 1..8 { + let wk_chr = match idx { + 1 => "MON", + 2 => "TUE", + 3 => "WED", + 4 => "THU", + 5 => "FRI", + 6 => "SUN", + 7 => "SAT", + _ => panic!("impossible value"), + }; + + draw_text( + DEJAVUSANS_MONO, + &mut buf.subdimensions((idx * 48 + 4, (y_off * 32) + 64, 32, 16)), + background_color, + &Color::new(1.0, 1.0, 1.0, 1.0), + 16.0, + &wk_chr, + )?; + } + + y_off += 1; + + while !done { + let mut x_pos = 0; + + // + // Find the start of this week + // + let mut wkday = time.weekday(); + while wkday != chrono::Weekday::Mon { + x_pos += 1; + wkday = wkday.pred(); + } + + // + // Draw the week number + // + let wk = time.iso_week(); + draw_text( + DEJAVUSANS_MONO, + &mut buf.subdimensions((0 * 48, (y_off * 32) + 64, 38, 32)), + background_color, + &Color::new(0.75, 0.75, 0.75, 1.0), + 32.0, + &format!("{:02}", wk.week()), + )?; + x_pos += 1; + + // + // Draw the dates + // + while x_pos < 8 { + let c = if time.day() == orig.day() && time.month() == orig.month() { + Color::new(1.0, 1.0, 1.0, 1.0) + } else { + Color::new(0.5, 0.5, 0.5, 1.0) + }; + draw_text( + DEJAVUSANS_MONO, + &mut buf.subdimensions((x_pos * 48, (y_off * 32) + 64, 38, 32)), + background_color, + &c, + 32.0, + &format!("{:02}", time.day()), + )?; + let t = time.with_day(time.day() + 1); + if t.is_none() { + done = true; + break; + } + time = t.unwrap(); + x_pos += 1; + } + + y_off += 1; + } + + Ok(buf.get_signed_bounds()) +} + +impl Calendar { + pub fn new() -> Calendar { + Calendar{ + cur_date: Local::now().date(), + first_draw: true, + } + } +} + +impl ModuleImpl for Calendar { + fn draw( + &self, + buf: &mut Buffer, + background_color: &Color, + time: &DateTime, + ) -> Result, ::std::io::Error> { + let time = time.date(); + let t = time.with_day(1).unwrap(); + let mut damage: Vec<(i32, i32, i32, i32)> = Vec::new(); + damage.push(draw_month( + &mut buf.subdimensions((0, 0, 384, 344)), + background_color, + &time, + &t.pred().with_day(1).unwrap(), + )?); + damage.push(draw_month( + &mut buf.subdimensions((448, 0, 384, 344)), + background_color, + &time, + &t, + )?); + let n = if t.month() == 12 { + t.with_year(t.year() + 1).unwrap().with_month(1).unwrap() + } else { + t.with_month(t.month() + 1).unwrap() + }; + damage.push(draw_month( + &mut buf.subdimensions((896, 0, 384, 344)), + background_color, + &time, + &n, + )?); + Ok(damage) + } + + fn update(&mut self, time: &DateTime, force: bool) -> Result{ + if self.first_draw { + self.first_draw = false; + Ok(true) + } else if time.date() != self.cur_date || force{ + self.cur_date = time.date(); + Ok(true) + } else { + Ok(false) + } + } + + fn input(&mut self, _input: Input) {} +} diff --git a/src/clock.rs b/src/clock.rs new file mode 100644 index 0000000..1ac05bf --- /dev/null +++ b/src/clock.rs @@ -0,0 +1,68 @@ +use crate::buffer::Buffer; +use crate::color::Color; +use crate::module::{ModuleImpl, Input}; +use crate::draw::{draw_text, draw_text_fixed_width, ROBOTO_REGULAR}; +use chrono::{DateTime, Datelike, Local, Timelike}; + +pub struct Clock { + cur_time: DateTime, +} + +impl Clock { + pub fn new() -> Clock { + Clock{ + cur_time: Local::now(), + } + } +} + +impl ModuleImpl for Clock { + fn draw(&self, + buf: &mut Buffer, + bg: &Color, + time: &DateTime, + ) -> Result, ::std::io::Error> { + buf.memset(bg); + + draw_text( + ROBOTO_REGULAR, + &mut buf.subdimensions((0, 0, 448, 64)), + bg, + &Color::new(1.0, 1.0, 1.0, 1.0), + 64.0, + &format!( + "{:?}, {:02}/{:02}/{:4}", + time.weekday(), + time.day(), + time.month(), + time.year() + ), + )?; + + draw_text_fixed_width( + ROBOTO_REGULAR, + &mut buf.subdimensions((0, 64, 288 * 2 + 64, 256)), + bg, + &Color::new(1.0, 1.0, 1.0, 1.0), + 256.0, + vec![120, 120, 64, 120, 120], + &format!("{:02}:{:02}", time.hour(), time.minute()), + )?; + + Ok(vec![buf.get_signed_bounds()]) + } + + fn update(&mut self, time: &DateTime, force: bool) -> Result { + if time.date() != self.cur_time.date() || + time.hour() != self.cur_time.hour() || + time.minute() != self.cur_time.minute() || + force { + self.cur_time = time.clone(); + Ok(true) + } else { + Ok(false) + } + } + + fn input(&mut self, _input: Input) {} +} \ No newline at end of file diff --git a/src/color.rs b/src/color.rs index 2ea6d6f..13d74fa 100644 --- a/src/color.rs +++ b/src/color.rs @@ -63,4 +63,4 @@ impl Color { | ((255.0 * self.green) as u32) << 8 | ((255.0 * self.red) as u32) } -} \ No newline at end of file +} diff --git a/src/draw.rs b/src/draw.rs index a61fab3..0ed4225 100644 --- a/src/draw.rs +++ b/src/draw.rs @@ -1,12 +1,11 @@ -use crate::color::Color; use crate::buffer::Buffer; +use crate::color::Color; use rusttype::{point, Font, Scale}; -use chrono::{Date, DateTime, Datelike, Local, Timelike}; -static DEJAVUSANS_MONO: &'static [u8] = include_bytes!("../fonts/dejavu/DejaVuSansMono.ttf"); -static ROBOTO_REGULAR: &'static [u8] = include_bytes!("../fonts/Roboto-Regular.ttf"); +pub static DEJAVUSANS_MONO: &'static [u8] = include_bytes!("../fonts/dejavu/DejaVuSansMono.ttf"); +pub static ROBOTO_REGULAR: &'static [u8] = include_bytes!("../fonts/Roboto-Regular.ttf"); -fn draw_text( +pub fn draw_text( font_data: &'static [u8], buf: &mut Buffer, background_color: &Color, @@ -25,7 +24,7 @@ fn draw_text( // layout the glyphs in a line with 20 pixels padding let glyphs: Vec<_> = font - .layout(s, scale, point(20.0, 20.0 + v_metrics.ascent)) + .layout(s, scale, point(0.0, v_metrics.ascent)) .collect(); // Loop through the glyphs in the text, positing each one on a line @@ -50,7 +49,7 @@ fn draw_text( Ok(()) } -fn draw_text_fixed_width( +pub fn draw_text_fixed_width( font_data: &'static [u8], buf: &mut Buffer, background_color: &Color, @@ -70,7 +69,7 @@ fn draw_text_fixed_width( // layout the glyphs in a line with 20 pixels padding let glyphs: Vec<_> = font - .layout(s, scale, point(20.0, 20.0 + v_metrics.ascent)) + .layout(s, scale, point(0.0, v_metrics.ascent)) .collect(); // Loop through the glyphs in the text, positing each one on a line @@ -103,193 +102,3 @@ fn draw_text_fixed_width( Ok(()) } -pub fn draw_clock( - buf: &mut Buffer, - background_color: &Color, - time: &DateTime, -) -> Result<(), ::std::io::Error> { - draw_text( - ROBOTO_REGULAR, - &mut buf.subdimensions((0, 0, 448, 96)), - background_color, - &Color::new(1.0, 1.0, 1.0, 1.0), - 64.0, - &format!( - "{:?}, {:02}/{:02}/{:4}", - time.weekday(), - time.day(), - time.month(), - time.year() - ), - )?; - - draw_text_fixed_width( - ROBOTO_REGULAR, - &mut buf.subdimensions((0, 64, 288*2+64, 256)), - background_color, - &Color::new(1.0, 1.0, 1.0, 1.0), - 256.0, - vec![120, 120, 64, 120, 120], - &format!("{:02}:{:02}", time.hour(), time.minute()), - )?; - - Ok(()) -} - -fn draw_month( - buf: &mut Buffer, - background_color: &Color, - orig: &Date, - time: &Date, -) -> Result<(), ::std::io::Error> { - let mut time = time.clone(); - let mut y_off = 1; - let mut done = false; - - let month_str = match time.month() { - 1 => "January", - 2 => "February", - 3 => "March", - 4 => "April", - 5 => "May", - 6 => "June", - 7 => "July", - 8 => "August", - 9 => "September", - 10 => "October", - 11 => "November", - 12 => "December", - _ => panic!("impossible value"), - }; - - // - // Draw the of the month - // - - draw_text( - ROBOTO_REGULAR, - &mut buf.subdimensions((0, 0, 364, 96)), - background_color, - &Color::new(1.0, 1.0, 1.0, 1.0), - 68.0, - month_str, - )?; - - // - // Draw the week day - // - - for idx in 1..8 { - let wk_chr = match idx { - 1 => "MON", - 2 => "TUE", - 3 => "WED", - 4 => "THU", - 5 => "FRI", - 6 => "SUN", - 7 => "SAT", - _ => panic!("impossible value"), - }; - - draw_text( - DEJAVUSANS_MONO, - &mut buf.subdimensions((idx * 48 + 4, (y_off * 32) + 64, 64, 64)), - background_color, - &Color::new(0.75, 0.75, 0.75, 1.0), - 16.0, - &wk_chr, - )?; - } - - y_off += 1; - - while !done { - let mut x_pos = 0; - - // - // Find the start of this week - // - let mut wkday = time.weekday(); - while wkday != chrono::Weekday::Mon { - x_pos += 1; - wkday = wkday.pred(); - } - - // - // Draw the week number - // - let wk = time.iso_week(); - draw_text( - DEJAVUSANS_MONO, - &mut buf.subdimensions((0 * 48, (y_off * 32) + 64, 64, 64)), - background_color, - &Color::new(0.75, 0.75, 0.75, 1.0), - 32.0, - &format!("{:02}", wk.week()), - )?; - x_pos += 1; - - // - // Draw the dates - // - while x_pos < 8 { - let c = if time.day() == orig.day() && time.month() == orig.month() { - Color::new(1.0, 1.0, 1.0, 1.0) - } else { - Color::new(0.5, 0.5, 0.5, 1.0) - }; - draw_text( - DEJAVUSANS_MONO, - &mut buf.subdimensions((x_pos * 48, (y_off * 32) + 64, 64, 64)), - background_color, - &c, - 32.0, - &format!("{:02}", time.day()), - )?; - let t = time.with_day(time.day() + 1); - if t.is_none() { - done = true; - break; - } - time = t.unwrap(); - x_pos += 1; - } - - y_off += 1; - } - - Ok(()) -} - -pub fn draw_calendar( - buf: &mut Buffer, - background_color: &Color, - time: &Date, -) -> Result<(), ::std::io::Error> { - // ~1546x384px - let t = time.with_day(1).unwrap(); - draw_month( - &mut buf.subdimensions((0, 0, 448, 384)), - background_color, - time, - &t.pred().with_day(1).unwrap(), - )?; - draw_month( - &mut buf.subdimensions((512, 0, 448, 384)), - background_color, - time, - &t, - )?; - let n = if t.month() == 12 { - t.with_year(t.year() + 1).unwrap().with_month(1).unwrap() - } else { - t.with_month(t.month() + 1).unwrap() - }; - draw_month( - &mut buf.subdimensions((1024, 0, 448, 384)), - background_color, - time, - &n, - )?; - Ok(()) -} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 9d0fb88..4106705 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,26 +15,32 @@ use os_pipe::pipe; use chrono::{Duration as ChronoDuration, Local, Timelike}; use sctk::keyboard::{map_keyboard_auto_with_repeat, Event as KbEvent, KeyRepeatKind}; -use sctk::reexports::client::protocol::wl_shm; +use sctk::reexports::client::protocol::{wl_shm, wl_pointer}; use sctk::reexports::client::{Display, EventQueue, NewProxy}; use sctk::utils::DoubleMemPool; use sctk::window::{ConceptFrame, Event as WEvent, Window}; use sctk::Environment; -mod color; mod buffer; +mod color; +mod module; mod draw; +mod clock; +mod calendar; +mod backlight; -use crate::color::Color; use crate::buffer::Buffer; -use crate::draw::{draw_clock, draw_calendar}; +use crate::color::Color; +use crate::module::{Module, Input}; +use crate::clock::Clock; +use crate::backlight::Backlight; +use crate::calendar::Calendar; -#[derive(Debug)] enum Cmd { Exit, Configure, - MaybeDraw, Draw, + Input{pos: (u32, u32), input: Input}, } struct App { @@ -44,10 +50,13 @@ struct App { window: Window, cmd_queue: Arc>>, dimensions: (u32, u32), + modules: Vec, } impl App { - fn redraw(&mut self) -> Result<(), ::std::io::Error> { + fn redraw(&mut self, force: bool) -> Result<(), ::std::io::Error> { + let time = Local::now(); + let pool = match self.pools.pool() { Some(pool) => pool, None => return Ok(()), @@ -62,17 +71,16 @@ impl App { let mmap = pool.mmap(); let mut buf = Buffer::new(mmap, self.dimensions); - let bg = Color::new(0.0, 0.0, 0.0, 0.8); - buf.memset(&bg); + let mut margin_buf = buf.subdimensions((20, 20, buf_x - 40, buf_y - 40)); + let bg = Color::new(0.0, 0.0, 0.0, 0.9); - let time = Local::now(); - - draw_clock(&mut buf.subdimensions((0, 0, 720, 320)), &bg, &time)?; - draw_calendar( - &mut buf.subdimensions((0, 384, 1472, 384)), - &bg, - &time.date(), - )?; + let mut damage = vec![]; + for module in self.modules.iter() { + if module.update(&time, force)? { + let mut d = module.draw(&mut margin_buf, &bg, &time)?; + damage.append(&mut d); + } + } mmap.flush().unwrap(); @@ -85,6 +93,9 @@ impl App { wl_shm::Format::Argb8888, ); self.window.surface().attach(Some(&new_buffer), 0, 0); + for d in damage { + self.window.surface().damage(d.0, d.1, d.2, d.3); + } self.window.surface().commit(); Ok(()) } @@ -109,6 +120,28 @@ impl App { &mut self.event_queue } + fn wipe(&mut self) { + let pool = match self.pools.pool() { + Some(pool) => pool, + None => return, + }; + pool.resize((4 * self.dimensions.0 * self.dimensions.1) as usize) + .expect("Failed to resize the memory pool."); + let mmap = pool.mmap(); + let mut buf = Buffer::new(mmap, self.dimensions); + let bg = Color::new(0.0, 0.0, 0.0, 0.9); + buf.memset(&bg); + } + + fn get_module(&self, pos: (u32, u32)) -> Option<&Module> { + for m in self.modules.iter() { + if m.intersect(pos) { + return Some(&m) + } + } + None + } + fn new(dimensions: (u32, u32)) -> App { let cmd_queue = Arc::new(Mutex::new(VecDeque::new())); @@ -128,7 +161,7 @@ impl App { match evt { WEvent::Close => return, WEvent::Refresh => { - event_clone.lock().unwrap().push_back(Cmd::MaybeDraw); + event_clone.lock().unwrap().push_back(Cmd::Draw); } WEvent::Configure { new_size: _, @@ -162,9 +195,67 @@ impl App { ) .expect("Failed to map keyboard"); + let pointer_clone = cmd_queue.clone(); + seat.get_pointer(move |ptr| { + let mut pos: (u32, u32) = (0, 0); + let mut vert_scroll: f64 = 0.0; + let mut horiz_scroll: f64 = 0.0; + let mut btn: u32 = 0; + let mut btn_clicked = false; + ptr.implement_closure( + move |evt, _| match evt { + wl_pointer::Event::Enter{ serial: _, surface: _, surface_x, surface_y } => { + pos = (surface_x as u32, surface_y as u32); + }, + wl_pointer::Event::Leave{ serial: _, surface: _ } => { + pos = (0, 0); + }, + wl_pointer::Event::Motion{ time: _, surface_x, surface_y } => { + pos = (surface_x as u32, surface_y as u32); + }, + wl_pointer::Event::Axis{ time: _, axis, value } => { + if axis == wl_pointer::Axis::VerticalScroll { + vert_scroll += value; + } + }, + wl_pointer::Event::Button{ serial: _, time: _, button, state } => match state { + wl_pointer::ButtonState::Released => { + btn = button; + btn_clicked = true; + }, + _ => {} + }, + wl_pointer::Event::Frame => { + if pos.0 < 20 || pos.1 < 20 { + // Ignore stuff outside our margins + return + } + let pos = (pos.0-20, pos.1-20); + if vert_scroll != 0.0 || horiz_scroll != 0.0 { + pointer_clone.lock().unwrap().push_back(Cmd::Input{pos: pos, input: Input::Scroll{pos: pos, x: horiz_scroll, y: vert_scroll}}); + vert_scroll = 0.0; + horiz_scroll = 0.0; + } + if btn_clicked { + pointer_clone.lock().unwrap().push_back(Cmd::Input{pos: pos, input: Input::Click{pos: pos, button: btn}}); + btn_clicked = false; + } + } + _ => {} + }, + (), + ) + }) + .unwrap(); + window.set_title("dashboard".to_string()); window.set_app_id("dashboard".to_string()); + let modules = vec![ + Module::new(Box::new(Clock::new()), (0, 0, 720, 320)), + Module::new(Box::new(Calendar::new()), (0, 384, 1280, 344)), + Module::new(Box::new(Backlight::default().unwrap()), (720, 0, 256, 24))]; + App { window: window, display: display, @@ -172,6 +263,7 @@ impl App { cmd_queue: cmd_queue, pools: pools, dimensions: dimensions, + modules: modules, } } } @@ -179,9 +271,7 @@ impl App { fn main() { let (mut rx_pipe, mut tx_pipe) = pipe().unwrap(); - let mut app = App::new((1600u32, 784u32)); - - let mut last_time = Local::now(); + let mut app = App::new((1320u32, 784u32)); let worker_queue = app.cmd_queue(); std::thread::spawn(move || loop { @@ -195,7 +285,7 @@ fn main() { let d = target - n; std::thread::sleep(d.to_std().unwrap()); - worker_queue.lock().unwrap().push_back(Cmd::MaybeDraw); + worker_queue.lock().unwrap().push_back(Cmd::Draw); tx_pipe.write_all(&[0x1]).unwrap(); }); @@ -212,18 +302,21 @@ fn main() { Cmd::Configure => { let d = app.dimensions(); app.window().resize(d.0, d.1); - q.lock().unwrap().push_back(Cmd::Draw); + app.wipe(); + app.redraw(true).expect("Failed to draw"); + app.flush_display(); } Cmd::Draw => { - app.redraw().expect("Failed to draw"); + app.redraw(false).expect("Failed to draw"); app.flush_display(); - last_time = Local::now(); - } - Cmd::MaybeDraw => { - let t = Local::now(); - if t.hour() != last_time.hour() || t.minute() != last_time.minute() { - q.lock().unwrap().push_back(Cmd::Draw); - } + }, + Cmd::Input{pos, input} => { + if pos.0 >= 20 || pos.1 >= 20 { // We need to deal with our margin. + if let Some(m) = app.get_module(pos) { + m.input(input); + q.lock().unwrap().push_back(Cmd::Draw); + } + } } Cmd::Exit => { std::process::exit(0); diff --git a/src/module.rs b/src/module.rs new file mode 100644 index 0000000..08c8c0b --- /dev/null +++ b/src/module.rs @@ -0,0 +1,50 @@ +use crate::buffer::Buffer; +use crate::color::Color; + +use std::sync::{Arc, Mutex}; + +use chrono::{DateTime, Local}; + +pub enum Input { + Click{pos: (u32, u32), button: u32}, + Scroll{pos: (u32, u32), x: f64, y: f64}, +} + +pub trait ModuleImpl { + fn draw(&self, buf: &mut Buffer, bg: &Color, time: &DateTime) -> Result, ::std::io::Error>; + fn update(&mut self, time: &DateTime, force: bool) -> Result; + fn input(&mut self, input: Input); +} + +pub struct Module { + m: Arc>>, + pos: (u32, u32, u32, u32), +} + +impl Module { + pub fn new(m: Box, pos: (u32, u32, u32, u32)) -> Module { + Module{ + m: Arc::new(Mutex::new(m)), + pos: pos, + } + } + + pub fn update(&self, time: &DateTime, force: bool) -> Result { + self.m.lock().unwrap().update(time, force) + } + + pub fn draw(&self, buf: &mut Buffer, bg: &Color, time: &DateTime) -> Result, ::std::io::Error> { + self.m.lock().unwrap().draw(&mut buf.subdimensions(self.pos), bg, time) + } + + pub fn intersect(&self, pos: (u32, u32)) -> bool { + pos.0 >= self.pos.0 && + pos.0 < (self.pos.0 + self.pos.2) && + pos.1 >= self.pos.1 && + pos.1 < (self.pos.1 + self.pos.3) + } + + pub fn input(&self, input: Input) { + self.m.lock().unwrap().input(input) + } +} \ No newline at end of file