Minimal working Wayland surface
- Doesn't work on Gnome-Wayland
This commit is contained in:
parent
5aa6396c87
commit
50948197ba
@ -7,11 +7,12 @@ authors = ["candifloss <candifloss.cc>"]
|
|||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
slint = "1.14.1"
|
slint = {version="1.14.1", features = ["backend-winit"]}
|
||||||
smithay-client-toolkit = { version = "0.20.0", features = ["calloop"] }
|
smithay-client-toolkit = { version = "0.20.0", features = ["calloop"] }
|
||||||
|
tempfile = "3.23.0"
|
||||||
wayland-client = "0.31.11"
|
wayland-client = "0.31.11"
|
||||||
winit = "0.30.12"
|
wayland-protocols = { version = "0.32.9", features = ["client"] }
|
||||||
x11rb = "0.13.2"
|
wayland-protocols-wlr = "0.3.9"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
slint-build = "1.14.1"
|
slint-build = "1.14.1"
|
||||||
|
|||||||
312
src/main.rs
312
src/main.rs
@ -1,18 +1,310 @@
|
|||||||
use slint::{LogicalPosition, LogicalSize};
|
use std::fs::File;
|
||||||
|
use std::io::{BufWriter, Write};
|
||||||
|
use std::os::unix::io::AsFd;
|
||||||
|
|
||||||
slint::include_modules!();
|
use tempfile::tempfile;
|
||||||
|
|
||||||
|
use wayland_client::{
|
||||||
|
protocol::{wl_buffer, wl_compositor, wl_registry, wl_shm, wl_shm_pool, wl_surface},
|
||||||
|
Connection, Dispatch, EventQueue, QueueHandle,
|
||||||
|
};
|
||||||
|
|
||||||
|
use wayland_protocols_wlr::layer_shell::v1::client::{
|
||||||
|
zwlr_layer_shell_v1, zwlr_layer_surface_v1,
|
||||||
|
};
|
||||||
|
|
||||||
|
// ---------- client state ----------
|
||||||
|
|
||||||
|
struct State {
|
||||||
|
running: bool,
|
||||||
|
bar_height: 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,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl State {
|
||||||
|
fn new(bar_height: u32, default_width: u32) -> Self {
|
||||||
|
Self {
|
||||||
|
running: true,
|
||||||
|
bar_height,
|
||||||
|
compositor: None,
|
||||||
|
shm: None,
|
||||||
|
layer_shell: None,
|
||||||
|
surface: None,
|
||||||
|
layer_surface: None,
|
||||||
|
buffer: None,
|
||||||
|
current_width: default_width,
|
||||||
|
current_height: bar_height,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_create_layer_surface(&mut self, qh: &QueueHandle<Self>) {
|
||||||
|
if self.layer_surface.is_some() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let compositor = match &self.compositor {
|
||||||
|
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
|
||||||
|
|
||||||
|
let surface = compositor.create_surface(qh, ());
|
||||||
|
self.surface = Some(surface.clone());
|
||||||
|
|
||||||
|
let namespace = "chocobar".to_string();
|
||||||
|
|
||||||
|
let layer_surface = layer_shell.get_layer_surface(
|
||||||
|
&surface,
|
||||||
|
None, // all outputs
|
||||||
|
zwlr_layer_shell_v1::Layer::Top,
|
||||||
|
namespace,
|
||||||
|
qh,
|
||||||
|
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);
|
||||||
|
layer_surface.set_keyboard_interactivity(
|
||||||
|
zwlr_layer_surface_v1::KeyboardInteractivity::OnDemand,
|
||||||
|
);
|
||||||
|
|
||||||
|
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
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
struct LayerSurfaceData;
|
||||||
|
|
||||||
|
// ---------- wl_registry dispatch ----------
|
||||||
|
|
||||||
|
impl Dispatch<wl_registry::WlRegistry, ()> for State {
|
||||||
|
fn event(
|
||||||
|
state: &mut Self,
|
||||||
|
registry: &wl_registry::WlRegistry,
|
||||||
|
event: wl_registry::Event,
|
||||||
|
_: &(),
|
||||||
|
_: &Connection,
|
||||||
|
qh: &QueueHandle<Self>,
|
||||||
|
) {
|
||||||
|
match event {
|
||||||
|
wl_registry::Event::Global { name, interface, version: _ } => {
|
||||||
|
match interface.as_str() {
|
||||||
|
"wl_compositor" => {
|
||||||
|
let compositor = registry.bind::<wl_compositor::WlCompositor, _, _>(
|
||||||
|
name,
|
||||||
|
4,
|
||||||
|
qh,
|
||||||
|
(),
|
||||||
|
);
|
||||||
|
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, _, _>(
|
||||||
|
name,
|
||||||
|
1,
|
||||||
|
qh,
|
||||||
|
(),
|
||||||
|
);
|
||||||
|
state.layer_shell = Some(layer_shell);
|
||||||
|
state.try_create_layer_surface(qh);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wl_registry::Event::GlobalRemove { .. } => {
|
||||||
|
// ignore for this minimal example
|
||||||
|
},
|
||||||
|
_ => todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------- wlr layer shell dispatch ----------
|
||||||
|
|
||||||
|
impl Dispatch<zwlr_layer_shell_v1::ZwlrLayerShellV1, ()> for State {
|
||||||
|
fn event(
|
||||||
|
_: &mut Self,
|
||||||
|
_: &zwlr_layer_shell_v1::ZwlrLayerShellV1,
|
||||||
|
_: zwlr_layer_shell_v1::Event,
|
||||||
|
_: &(),
|
||||||
|
_: &Connection,
|
||||||
|
_: &QueueHandle<Self>,
|
||||||
|
) {
|
||||||
|
unreachable!("zwlr_layer_shell_v1 has no events");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Dispatch<zwlr_layer_surface_v1::ZwlrLayerSurfaceV1, LayerSurfaceData> for State {
|
||||||
|
fn event(
|
||||||
|
state: &mut Self,
|
||||||
|
surface_role: &zwlr_layer_surface_v1::ZwlrLayerSurfaceV1,
|
||||||
|
event: zwlr_layer_surface_v1::Event,
|
||||||
|
_: &LayerSurfaceData,
|
||||||
|
_: &Connection,
|
||||||
|
qh: &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
|
||||||
|
};
|
||||||
|
|
||||||
|
state.ensure_buffer(w, h, qh);
|
||||||
|
state.repaint();
|
||||||
|
}
|
||||||
|
zwlr_layer_surface_v1::Event::Closed => {
|
||||||
|
state.running = false;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------- ignore events from other objects we do not care about ----------
|
||||||
|
|
||||||
|
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>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let bar_height = 25;
|
let bar_height = 25;
|
||||||
let bar_width = 1366;
|
let default_width = 1366;
|
||||||
|
|
||||||
let ui = TopBar::new()?;
|
let conn = Connection::connect_to_env()?;
|
||||||
ui.set_bar_width(bar_width);
|
let display = conn.display();
|
||||||
ui.set_bar_height(bar_height);
|
|
||||||
ui.window()
|
let mut event_queue: EventQueue<State> = conn.new_event_queue();
|
||||||
.set_size(LogicalSize::new(bar_width as f32, bar_height as f32));
|
let qh = event_queue.handle();
|
||||||
ui.window().set_position(LogicalPosition::new(0.0, 0.0));
|
|
||||||
ui.run()?;
|
display.get_registry(&qh, ());
|
||||||
|
|
||||||
|
let mut state = State::new(bar_height, default_width);
|
||||||
|
|
||||||
|
while state.running {
|
||||||
|
event_queue.blocking_dispatch(&mut state)?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user