Create a basic 24h clock

This commit is contained in:
2022-05-17 16:45:01 +02:00
parent 642fa1e416
commit d4eef759be
3 changed files with 595 additions and 14 deletions

View File

@@ -1,13 +1,93 @@
extern crate smithay_client_toolkit as sctk;
use std::time::SystemTime;
use chrono::Timelike;
use chrono::prelude::Local;
use sctk::reexports::client::protocol::{wl_shm, wl_surface};
use sctk::shm::AutoMemPool;
use sctk::window::{Event as WEvent, FallbackFrame};
use svg::Document;
use svg::node::Text as TextNode;
use svg::node::element::{Group, Line, Rectangle, Style, Text};
sctk::default_environment!(SeasonalClock, desktop);
fn seconds_to_degrees(seconds: i32) -> f32 {
seconds as f32 * 360.0 / 86400.0
}
fn time_to_degrees(
timestamp: i32 // should be time/timestamp
) -> f32 {
seconds_to_degrees(timestamp)
}
fn gen_svg() -> Document {
// These should be calculated
let local_timestamp = Local::now();
let local_time = local_timestamp.time().num_seconds_from_midnight() as i32;
// TODO: These should be calculated instead of hardcoded
let image_width = 700u32;
let local_hour_font_size = 16.5;
let hour_name_font_size = 13.37699;
let outer_r = (image_width as f32) / 2.0 - 3.0 * hour_name_font_size;
let ring_width = hour_name_font_size * 3.0;
let border = Rectangle::new()
.set("x", 0i32)
.set("y", 0i32)
.set("width", 700i32)
.set("height", 700i32)
.set("id", "border");
let stylesheet = Style::new("\
#border {stroke: none; fill: rgb(19, 17, 30); }
.local-hour {stroke: none; fill: rgb(238, 187, 85);}
.dial {stroke-width: 2px; stroke: rgb(238, 187, 85);}");
let mut local_clock = Group::new()
.set("id", "local-clock");
for hour in 0i32..24 {
let hour_str = match hour {
0 => "Midnight".to_string(),
12 => "Noon".to_string(),
_ => hour.to_string(),
};
let rotation = hour * 15;
let hour_name = TextNode::new(hour_str);
let hour_node = Text::new()
.set("class", "local-hour")
.set("transform", format!("rotate({}, 350, 350)", 180 + rotation))
.set("x", (image_width as f32) / 2.0)
.set("y", (image_width as f32) / 2.0 - outer_r - local_hour_font_size / 2.0)
.set("text-anchor", "middle")
.set("font-size", local_hour_font_size as f32)
.add(hour_name);
local_clock = local_clock.add(hour_node);
}
let dial = Line::new()
.set("id", "dial")
.set("class", "dial")
.set("transform", format!("rotate({}, {}, {})", time_to_degrees(local_time), image_width / 2, image_width / 2))
.set("x1", image_width / 2)
.set("y1", image_width as f32 / 2.0 + outer_r * 0.5)
.set("x2", image_width / 2)
.set("y2", image_width as f32 / 2.0 + outer_r - ring_width + hour_name_font_size);
Document::new()
.set("viewBox", (0i32, 0i32, 700i32, 700i32))
.set("width", 700i32)
.set("height", 700i32)
.set("xmlns:xlink", "http://www.w3.org/1999/xlink")
.add(stylesheet)
.add(border)
.add(local_clock)
.add(dial)
}
fn main() {
let (env, _display, mut queue) = sctk::new_default_environment!(SeasonalClock, desktop)
let (env, display, mut queue) = sctk::new_default_environment!(SeasonalClock, desktop)
.expect("Unable to connect to a Wayland compositor");
let surface = env
@@ -44,7 +124,7 @@ fn main() {
let mut pool = env.create_auto_pool().expect("Failed to create the memory pool.");
let mut need_redraw = false;
let mut dimensions = (100, 100);
let mut dimensions = (700, 700);
if !env.get_shell().unwrap().needs_configure() {
redraw(&mut pool, window.surface(), dimensions)
@@ -52,7 +132,25 @@ fn main() {
window.refresh()
}
let now = SystemTime::now();
let mut last_elapsed = 0;
loop {
// Update every second
// TODO: Theres probably a better way to do this…
match now.elapsed() {
Ok(elapsed) => {
let new_elapsed = elapsed.as_secs();
if new_elapsed != last_elapsed {
need_redraw = true;
last_elapsed = new_elapsed;
}
}
Err(e) => {
println!("Error: {:?}", e);
}
}
match next_action.take() {
Some(WEvent::Close) => break,
Some(WEvent::Refresh) => {
@@ -75,15 +173,26 @@ fn main() {
if need_redraw {
need_redraw = false;
redraw(
&mut pool,
window.surface(),
dimensions,
)
redraw(&mut pool, window.surface(), dimensions)
.expect("Failed to draw")
}
queue.dispatch(&mut next_action, |_, _, _| {}).unwrap();
if let Err(e) = display.flush() {
if e.kind() != ::std::io::ErrorKind::WouldBlock {
eprintln!("Error while trying to flush the wayland socket: {:?}", e);
}
}
if let Some(guard) = queue.prepare_read() {
if let Err(e) = guard.read_events() {
if e.kind() != ::std::io::ErrorKind::WouldBlock {
eprintln!("Error while trying to read from the wayland socked: {:?}", e);
}
}
}
queue.dispatch_pending(&mut next_action, |_, _, _| {})
.expect("Failed to dispatch all messages.");
}
}
@@ -92,6 +201,14 @@ fn redraw(
surface: &wl_surface::WlSurface,
(buf_x, buf_y): (u32, u32),
) -> Result<(), ::std::io::Error> {
let document = gen_svg();
let bytes = document.to_string();
let mut opt = usvg::Options::default();
opt.fontdb.load_system_fonts();
opt.font_family = "Liberation Serif".to_string();
let svg_tree = usvg::Tree::from_str(&bytes, &opt.to_ref()).unwrap();
let (canvas, new_buffer) = pool.buffer(
buf_x as i32,
buf_y as i32,
@@ -99,12 +216,28 @@ fn redraw(
wl_shm::Format::Argb8888,
)?;
let image_size = ::std::cmp::min(buf_x, buf_y);
let move_x = (buf_x - image_size) as f32 / 2.0;
let move_y = (buf_y - image_size) as f32 / 2.0;
let mut pixmap = tiny_skia::Pixmap::new(buf_x, buf_y).unwrap();
resvg::render(&svg_tree, usvg::FitTo::Size(image_size, image_size), tiny_skia::Transform::from_translate(move_x, move_y), pixmap.as_mut()).unwrap();
// We do not have anything to draw yet, so draw an empty surface
for dst_pixel in canvas.chunks_exact_mut(4) {
dst_pixel[0] = 0x00;
dst_pixel[1] = 0x00;
dst_pixel[2] = 0x00;
dst_pixel[3] = 0xFF;
for (dst_pixel, src_pixel) in canvas.chunks_exact_mut(4).zip(pixmap.pixels()) {
let r = src_pixel.red() as u32;
let g = src_pixel.green() as u32;
let b = src_pixel.blue() as u32;
let a = src_pixel.alpha() as u32;
let r = ::std::cmp::min(0xFF, (0xFF * (0xFF - a) + a * r) / 0xFF);
let g = ::std::cmp::min(0xFF, (0xFF * (0xFF - a) + a * g) / 0xFF);
let b = ::std::cmp::min(0xFF, (0xFF * (0xFF - a) + a * b) / 0xFF);
let pixel: [u8; 4] = ((0xFF << 24) + (r << 16) + (g << 8) + b).to_ne_bytes();
dst_pixel[0] = pixel[0];
dst_pixel[1] = pixel[1];
dst_pixel[2] = pixel[2];
dst_pixel[3] = pixel[3];
}
surface.attach(Some(&new_buffer), 0, 0);