Minimal Wayland surface to attach the SLint UI
- The slint UI is yet to be attached
This commit is contained in:
parent
50948197ba
commit
d8fdcab52d
@ -7,7 +7,8 @@ authors = ["candifloss <candifloss.cc>"]
|
||||
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"
|
||||
|
||||
197
src/main.rs
197
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<wl_compositor::WlCompositor>,
|
||||
shm: Option<wl_shm::WlShm>,
|
||||
layer_shell: Option<zwlr_layer_shell_v1::ZwlrLayerShellV1>,
|
||||
|
||||
surface: Option<wl_surface::WlSurface>,
|
||||
layer_surface: Option<zwlr_layer_surface_v1::ZwlrLayerSurfaceV1>,
|
||||
buffer: Option<wl_buffer::WlBuffer>,
|
||||
|
||||
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<Self>,
|
||||
) {
|
||||
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<wl_registry::WlRegistry, ()> for State {
|
||||
state.compositor = Some(compositor);
|
||||
state.try_create_layer_surface(qh);
|
||||
}
|
||||
"wl_shm" => {
|
||||
let shm =
|
||||
registry.bind::<wl_shm::WlShm, _, _>(name, 1, qh, ());
|
||||
state.shm = Some(shm);
|
||||
state.try_create_layer_surface(qh);
|
||||
}
|
||||
"zwlr_layer_shell_v1" => {
|
||||
let layer_shell =
|
||||
registry.bind::<zwlr_layer_shell_v1::ZwlrLayerShellV1, _, _>(
|
||||
@ -203,8 +145,8 @@ impl Dispatch<wl_registry::WlRegistry, ()> for State {
|
||||
}
|
||||
wl_registry::Event::GlobalRemove { .. } => {
|
||||
// ignore for this minimal example
|
||||
},
|
||||
_ => todo!()
|
||||
}
|
||||
_ => todo!("Unhandled wl_registry event"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -231,25 +173,31 @@ impl Dispatch<zwlr_layer_surface_v1::ZwlrLayerSurfaceV1, LayerSurfaceData> for S
|
||||
event: zwlr_layer_surface_v1::Event,
|
||||
_: &LayerSurfaceData,
|
||||
_: &Connection,
|
||||
qh: &QueueHandle<Self>,
|
||||
_: &QueueHandle<Self>,
|
||||
) {
|
||||
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<zwlr_layer_surface_v1::ZwlrLayerSurfaceV1, LayerSurfaceData> 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<dyn std::error::Error>> {
|
||||
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<State> = 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(())
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user