Add "hello world" clock application

This commit is contained in:
Albert Armea 2025-12-22 23:50:06 -05:00
parent 6d7d032ee0
commit 2a5a0b7443
3 changed files with 1326 additions and 2 deletions

1047
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -4,3 +4,7 @@ version = "0.1.0"
edition = "2024"
[dependencies]
smithay-client-toolkit = "0.19"
wayland-client = "0.31"
cairo-rs = { version = "0.20", features = ["v1_16"] }
chrono = "0.4"

View file

@ -1,3 +1,276 @@
fn main() {
println!("Hello, world!");
use chrono::Local;
use smithay_client_toolkit::{
compositor::{CompositorHandler, CompositorState},
delegate_compositor, delegate_layer, delegate_output, delegate_registry, delegate_shm,
output::{OutputHandler, OutputState},
registry::{ProvidesRegistryState, RegistryState},
registry_handlers,
shell::{
wlr_layer::{
Anchor, KeyboardInteractivity, Layer, LayerShell, LayerShellHandler, LayerSurface,
LayerSurfaceConfigure,
},
WaylandSurface,
},
shm::{slot::SlotPool, Shm, ShmHandler},
};
use wayland_client::{
globals::registry_queue_init,
protocol::{wl_output, wl_shm, wl_surface},
Connection, QueueHandle,
};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let conn = Connection::connect_to_env()?;
let (globals, mut event_queue) = registry_queue_init(&conn)?;
let qh = event_queue.handle();
let mut app = ClockApp {
registry_state: RegistryState::new(&globals),
output_state: OutputState::new(&globals, &qh),
compositor_state: CompositorState::bind(&globals, &qh)?,
shm_state: Shm::bind(&globals, &qh)?,
layer_shell: LayerShell::bind(&globals, &qh)?,
pool: None,
width: 400,
height: 200,
layer_surface: None,
configured: false,
};
// Create the layer surface
let surface = app.compositor_state.create_surface(&qh);
let layer_surface = app.layer_shell.create_layer_surface(
&qh,
surface,
Layer::Top,
Some("clock"),
None,
);
layer_surface.set_anchor(Anchor::TOP | Anchor::LEFT | Anchor::RIGHT);
layer_surface.set_size(app.width, app.height);
layer_surface.set_exclusive_zone(app.height as i32);
layer_surface.set_keyboard_interactivity(KeyboardInteractivity::None);
layer_surface.commit();
app.layer_surface = Some(layer_surface);
loop {
event_queue.blocking_dispatch(&mut app)?;
if app.configured {
app.draw(&qh)?;
// Sleep briefly to reduce CPU usage
std::thread::sleep(std::time::Duration::from_millis(500));
}
}
}
struct ClockApp {
registry_state: RegistryState,
output_state: OutputState,
compositor_state: CompositorState,
shm_state: Shm,
layer_shell: LayerShell,
pool: Option<SlotPool>,
width: u32,
height: u32,
layer_surface: Option<LayerSurface>,
configured: bool,
}
impl ClockApp {
fn draw(&mut self, _qh: &QueueHandle<Self>) -> Result<(), Box<dyn std::error::Error>> {
if let Some(layer_surface) = &self.layer_surface {
let width = self.width;
let height = self.height;
let stride = width as i32 * 4;
let pool = self.pool.get_or_insert_with(|| {
SlotPool::new((width * height * 4) as usize, &self.shm_state).unwrap()
});
let (buffer, canvas) = pool
.create_buffer(width as i32, height as i32, stride, wl_shm::Format::Argb8888)
.unwrap();
// Get current time
let now = Local::now();
let time_str = now.format("%H:%M:%S").to_string();
let date_str = now.format("%A, %B %d, %Y").to_string();
// Draw using cairo
// Safety: We ensure the buffer lifetime is valid for the cairo surface
unsafe {
let surface = cairo::ImageSurface::create_for_data_unsafe(
canvas.as_mut_ptr(),
cairo::Format::ARgb32,
width as i32,
height as i32,
stride,
)?;
let ctx = cairo::Context::new(&surface)?;
// Background
ctx.set_source_rgb(0.1, 0.1, 0.15);
ctx.paint()?;
// Draw time
ctx.set_source_rgb(1.0, 1.0, 1.0);
ctx.select_font_face("Sans", cairo::FontSlant::Normal, cairo::FontWeight::Bold);
ctx.set_font_size(60.0);
let time_extents = ctx.text_extents(&time_str)?;
let time_x = (width as f64 - time_extents.width()) / 2.0 - time_extents.x_bearing();
let time_y = height as f64 / 2.0 - 10.0;
ctx.move_to(time_x, time_y);
ctx.show_text(&time_str)?;
// Draw date
ctx.set_font_size(20.0);
ctx.select_font_face("Sans", cairo::FontSlant::Normal, cairo::FontWeight::Normal);
let date_extents = ctx.text_extents(&date_str)?;
let date_x = (width as f64 - date_extents.width()) / 2.0 - date_extents.x_bearing();
let date_y = height as f64 / 2.0 + 35.0;
ctx.move_to(date_x, date_y);
ctx.show_text(&date_str)?;
}
layer_surface
.wl_surface()
.attach(Some(buffer.wl_buffer()), 0, 0);
layer_surface.wl_surface().damage_buffer(0, 0, width as i32, height as i32);
layer_surface.wl_surface().commit();
}
Ok(())
}
}
impl CompositorHandler for ClockApp {
fn scale_factor_changed(
&mut self,
_conn: &Connection,
_qh: &QueueHandle<Self>,
_surface: &wl_surface::WlSurface,
_new_factor: i32,
) {
}
fn transform_changed(
&mut self,
_conn: &Connection,
_qh: &QueueHandle<Self>,
_surface: &wl_surface::WlSurface,
_new_transform: wl_output::Transform,
) {
}
fn frame(
&mut self,
_conn: &Connection,
qh: &QueueHandle<Self>,
_surface: &wl_surface::WlSurface,
_time: u32,
) {
let _ = self.draw(qh);
}
fn surface_enter(
&mut self,
_conn: &Connection,
_qh: &QueueHandle<Self>,
_surface: &wl_surface::WlSurface,
_output: &wl_output::WlOutput,
) {
}
fn surface_leave(
&mut self,
_conn: &Connection,
_qh: &QueueHandle<Self>,
_surface: &wl_surface::WlSurface,
_output: &wl_output::WlOutput,
) {
}
}
impl OutputHandler for ClockApp {
fn output_state(&mut self) -> &mut OutputState {
&mut self.output_state
}
fn new_output(
&mut self,
_conn: &Connection,
_qh: &QueueHandle<Self>,
_output: wl_output::WlOutput,
) {
}
fn update_output(
&mut self,
_conn: &Connection,
_qh: &QueueHandle<Self>,
_output: wl_output::WlOutput,
) {
}
fn output_destroyed(
&mut self,
_conn: &Connection,
_qh: &QueueHandle<Self>,
_output: wl_output::WlOutput,
) {
}
}
impl LayerShellHandler for ClockApp {
fn closed(&mut self, _conn: &Connection, _qh: &QueueHandle<Self>, _layer: &LayerSurface) {
std::process::exit(0);
}
fn configure(
&mut self,
_conn: &Connection,
qh: &QueueHandle<Self>,
_layer: &LayerSurface,
configure: LayerSurfaceConfigure,
_serial: u32,
) {
if configure.new_size.0 != 0 {
self.width = configure.new_size.0;
}
if configure.new_size.1 != 0 {
self.height = configure.new_size.1;
}
self.configured = true;
let _ = self.draw(qh);
}
}
impl ShmHandler for ClockApp {
fn shm_state(&mut self) -> &mut Shm {
&mut self.shm_state
}
}
delegate_compositor!(ClockApp);
delegate_output!(ClockApp);
delegate_shm!(ClockApp);
delegate_layer!(ClockApp);
delegate_registry!(ClockApp);
impl ProvidesRegistryState for ClockApp {
fn registry(&mut self) -> &mut RegistryState {
&mut self.registry_state
}
registry_handlers![OutputState];
}