Create a basic 24h clock
This commit is contained in:
159
src/main.rs
159
src/main.rs
@@ -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: There’s 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);
|
||||
|
Reference in New Issue
Block a user