diff --git a/Cargo.toml b/Cargo.toml index c0f2350..ee8956a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,8 @@ authors = ["candifloss "] readme = "README.md" [dependencies] -slint = {version="1.14.1", features = ["backend-winit"]} +i-slint-backend-testing = "1.14.1" +slint = { version= "1.14.1", default-features = false, features = ["std", "compat-1-2"] } smithay-client-toolkit = { version = "0.20.0", features = ["calloop"] } tempfile = "3.23.0" wayland-client = "0.31.11" diff --git a/src/main.rs b/src/main.rs index b3508c1..6e7c3f7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,49 +1,54 @@ -use std::fs::File; -use std::io::{BufWriter, Write}; -use std::os::unix::io::AsFd; - -use tempfile::tempfile; - +use slint::LogicalSize; use wayland_client::{ - protocol::{wl_buffer, wl_compositor, wl_registry, wl_shm, wl_shm_pool, wl_surface}, + protocol::{wl_compositor, wl_registry, wl_shm, wl_surface}, Connection, Dispatch, EventQueue, QueueHandle, }; - use wayland_protocols_wlr::layer_shell::v1::client::{ zwlr_layer_shell_v1, zwlr_layer_surface_v1, }; +// This backend sets a Slint platform that uses the software renderer, +// but does NOT provide its own event loop. +use i_slint_backend_testing as slint_backend; + +slint::include_modules!(); + // ---------- client state ---------- struct State { running: bool, bar_height: u32, + bar_width: u32, compositor: Option, - shm: Option, layer_shell: Option, surface: Option, layer_surface: Option, - buffer: Option, - current_width: u32, - current_height: u32, + // Slint UI + ui: TopBar, } impl State { - fn new(bar_height: u32, default_width: u32) -> Self { + fn new(bar_height: u32, bar_width: u32) -> Self { + // Slint platform MUST already be initialized before this + let ui = TopBar::new().expect("Failed to create Slint UI"); + + ui.set_bar_width(bar_width as i32); + ui.set_bar_height(bar_height as i32); + ui.window() + .set_size(LogicalSize::new(bar_width as f32, bar_height as f32)); + Self { running: true, bar_height, + bar_width, compositor: None, - shm: None, layer_shell: None, surface: None, layer_surface: None, - buffer: None, - current_width: default_width, - current_height: bar_height, + ui, } } @@ -55,23 +60,17 @@ impl State { Some(c) => c, None => return, }; - let shm = match &self.shm { - Some(s) => s, - None => return, - }; let layer_shell = match &self.layer_shell { Some(ls) => ls, None => return, }; - // Make sure shm exists so when configure comes we can create a buffer - let _ = shm; // only to express the dependency, not strictly needed - + // Wayland surface let surface = compositor.create_surface(qh, ()); self.surface = Some(surface.clone()); + // layer-shell surface let namespace = "chocobar".to_string(); - let layer_surface = layer_shell.get_layer_surface( &surface, None, // all outputs @@ -81,15 +80,18 @@ impl State { LayerSurfaceData, ); - // Anchor top, left and right, ask compositor to decide width (pass zero) layer_surface.set_anchor( zwlr_layer_surface_v1::Anchor::Top | zwlr_layer_surface_v1::Anchor::Left | zwlr_layer_surface_v1::Anchor::Right, ); layer_surface.set_exclusive_zone(self.bar_height as i32); - layer_surface.set_margin(0, 0, 0, 0); - layer_surface.set_size(0, self.bar_height as u32); + + // Ask compositor for width (0) but constrain height to bar_height + // or set explicit width if you want a fixed 1366-wide bar: + // layer_surface.set_size(self.bar_width, self.bar_height); + layer_surface.set_size(0, self.bar_height); + layer_surface.set_keyboard_interactivity( zwlr_layer_surface_v1::KeyboardInteractivity::OnDemand, ); @@ -97,63 +99,9 @@ impl State { self.layer_surface = Some(layer_surface); surface.commit(); } - - fn ensure_buffer( - &mut self, - width: u32, - height: u32, - qh: &QueueHandle, - ) { - let need_new = - self.buffer.is_none() || self.current_width != width || self.current_height != height; - - if !need_new { - return; - } - - let shm = self - .shm - .as_ref() - .expect("wl_shm not bound before buffer creation"); - - let mut tmp = tempfile().expect("failed to create temp file"); - draw_bar(&mut tmp, width, height).expect("drawing bar failed"); - - let size = (width * height * 4) as i32; - let pool: wl_shm_pool::WlShmPool = shm.create_pool(tmp.as_fd(), size, qh, ()); - let buffer = pool.create_buffer( - 0, - width as i32, - height as i32, - (width * 4) as i32, - wl_shm::Format::Argb8888, - qh, - (), - ); - - self.buffer = Some(buffer); - self.current_width = width; - self.current_height = height; - - // pool is dropped here, that is fine, pattern matches smithay examples - } - - fn repaint(&mut self) { - let surface = match &self.surface { - Some(s) => s, - None => return, - }; - let buffer = match &self.buffer { - Some(b) => b, - None => return, - }; - - surface.attach(Some(buffer), 0, 0); - surface.commit(); - } } -// This is the user data type for the layer surface, we do not need anything in it +// This is the user data type for the layer surface #[derive(Debug, Clone, Copy)] struct LayerSurfaceData; @@ -181,12 +129,6 @@ impl Dispatch for State { state.compositor = Some(compositor); state.try_create_layer_surface(qh); } - "wl_shm" => { - let shm = - registry.bind::(name, 1, qh, ()); - state.shm = Some(shm); - state.try_create_layer_surface(qh); - } "zwlr_layer_shell_v1" => { let layer_shell = registry.bind::( @@ -203,8 +145,8 @@ impl Dispatch for State { } wl_registry::Event::GlobalRemove { .. } => { // ignore for this minimal example - }, - _ => todo!() + } + _ => todo!("Unhandled wl_registry event"), } } } @@ -231,25 +173,31 @@ impl Dispatch for S event: zwlr_layer_surface_v1::Event, _: &LayerSurfaceData, _: &Connection, - qh: &QueueHandle, + _: &QueueHandle, ) { match event { zwlr_layer_surface_v1::Event::Configure { serial, width, height } => { surface_role.ack_configure(serial); - let w = if width == 0 { - state.current_width - } else { - width as u32 - }; - let h = if height == 0 { - state.bar_height - } else { - height as u32 - }; + // Use compositor-provided size if nonzero, otherwise our defaults + let w = if width == 0 { state.bar_width } else { width as u32 }; + let h = if height == 0 { state.bar_height } else { height as u32 }; - state.ensure_buffer(w, h, qh); - state.repaint(); + state.bar_width = w; + state.bar_height = h; + + // Resize Slint component + state.ui.set_bar_width(w as i32); + state.ui.set_bar_height(h as i32); + state + .ui + .window() + .set_size(LogicalSize::new(w as f32, h as f32)); + + // NOTE: at this point you still need to render the Slint + // window into a wl_shm buffer and attach it to `state.surface`. + // That is the next step: use `ui.window().take_snapshot()` + // + wl_shm buffer instead of a flat `draw_bar`. } zwlr_layer_surface_v1::Event::Closed => { state.running = false; @@ -259,52 +207,43 @@ impl Dispatch for S } } -// ---------- ignore events from other objects we do not care about ---------- +// ---------- ignore events from other objects ---------- wayland_client::delegate_noop!(State: ignore wl_compositor::WlCompositor); wayland_client::delegate_noop!(State: ignore wl_surface::WlSurface); wayland_client::delegate_noop!(State: ignore wl_shm::WlShm); -wayland_client::delegate_noop!(State: ignore wl_shm_pool::WlShmPool); -wayland_client::delegate_noop!(State: ignore wl_buffer::WlBuffer); - -// ---------- simple software drawing for the bar ---------- - -fn draw_bar(file: &mut File, width: u32, height: u32) -> std::io::Result<()> { - let mut buf = BufWriter::new(file); - - for _y in 0..height { - for _x in 0..width { - let a: u8 = 0xFF; - let r: u8 = 0x20; - let g: u8 = 0x20; - let b: u8 = 0x20; - - buf.write_all(&[b, g, r, a])?; - } - } - - buf.flush() -} // ---------- main loop ---------- fn main() -> Result<(), Box> { let bar_height = 25; - let default_width = 1366; + let bar_width = 1366; + // 1. Initialize Slint platform using the testing backend (software renderer, + // no event loop). This fixes the "No default Slint platform" panic. + slint_backend::init_no_event_loop(); + + // 2. Wayland connection + event loop let conn = Connection::connect_to_env()?; let display = conn.display(); let mut event_queue: EventQueue = conn.new_event_queue(); let qh = event_queue.handle(); + // Create Slint UI and Wayland state, now that the platform is set + let mut state = State::new(bar_height, bar_width); + display.get_registry(&qh, ()); - let mut state = State::new(bar_height, default_width); - + // 3. Run Wayland loop until layer surface is closed while state.running { event_queue.blocking_dispatch(&mut state)?; + // Later: call slint::platform::update_timers_and_animations() here + // and trigger re-render into wl_shm when needed. } + // Optionally show the Slint window normally (not needed for layer-shell) + // state.ui.run().expect("Failed to run Slint UI"); + Ok(()) }