forked from NotAShelf/beer
wayland: we have a (blank) window ladies and gentlemen
Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: Id2b91339a2d43cc95041dafe835c6a526a6a6964
This commit is contained in:
parent
2ccd62ba5e
commit
c68d3445e7
4 changed files with 649 additions and 1 deletions
258
src/wayland.rs
Normal file
258
src/wayland.rs
Normal file
|
|
@ -0,0 +1,258 @@
|
|||
//! Wayland front-end: connection, surface, and software-drawn window.
|
||||
//!
|
||||
//! Uses smithay-client-toolkit for protocol boilerplate and calloop for the
|
||||
//! event loop, so the PTY master fd and timers share one loop.
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::Context;
|
||||
use calloop::EventLoop;
|
||||
use calloop_wayland_source::WaylandSource;
|
||||
use smithay_client_toolkit::{
|
||||
compositor::{CompositorHandler, CompositorState},
|
||||
delegate_compositor, delegate_output, delegate_registry, delegate_seat, delegate_shm,
|
||||
delegate_xdg_shell, delegate_xdg_window,
|
||||
output::{OutputHandler, OutputState},
|
||||
registry::{ProvidesRegistryState, RegistryState},
|
||||
registry_handlers,
|
||||
seat::{Capability, SeatHandler, SeatState},
|
||||
shell::{
|
||||
WaylandSurface,
|
||||
xdg::{
|
||||
XdgShell,
|
||||
window::{Window, WindowConfigure, WindowDecorations, WindowHandler},
|
||||
},
|
||||
},
|
||||
shm::{Shm, ShmHandler, slot::SlotPool},
|
||||
};
|
||||
use wayland_client::{
|
||||
Connection, QueueHandle,
|
||||
globals::registry_queue_init,
|
||||
protocol::{wl_output, wl_seat, wl_shm, wl_surface},
|
||||
};
|
||||
|
||||
/// Default window size in pixels before the compositor suggests one.
|
||||
const DEFAULT_W: u32 = 800;
|
||||
const DEFAULT_H: u32 = 600;
|
||||
/// Background fill, 0xAARRGGBB. Foot-ish dark grey.
|
||||
const BG: u32 = 0xFF18_1818;
|
||||
|
||||
/// Run a single window until it is closed.
|
||||
pub fn run() -> anyhow::Result<()> {
|
||||
let conn = Connection::connect_to_env().context("connect to Wayland compositor")?;
|
||||
let (globals, event_queue) =
|
||||
registry_queue_init(&conn).context("initialize Wayland registry")?;
|
||||
let qh = event_queue.handle();
|
||||
|
||||
let mut event_loop: EventLoop<App> =
|
||||
EventLoop::try_new().context("create calloop event loop")?;
|
||||
WaylandSource::new(conn, event_queue)
|
||||
.insert(event_loop.handle())
|
||||
.map_err(|e| anyhow::anyhow!("insert Wayland source into event loop: {e}"))?;
|
||||
|
||||
let compositor = CompositorState::bind(&globals, &qh).context("compositor not available")?;
|
||||
let xdg_shell = XdgShell::bind(&globals, &qh).context("xdg_wm_base not available")?;
|
||||
let shm = Shm::bind(&globals, &qh).context("wl_shm not available")?;
|
||||
|
||||
let surface = compositor.create_surface(&qh);
|
||||
let window = xdg_shell.create_window(surface, WindowDecorations::RequestServer, &qh);
|
||||
window.set_title("beer");
|
||||
window.set_app_id("dev.notashelf.beer");
|
||||
window.set_min_size(Some((1, 1)));
|
||||
// First commit with no buffer kicks off the initial configure.
|
||||
window.commit();
|
||||
|
||||
let pool = SlotPool::new(DEFAULT_W as usize * DEFAULT_H as usize * 4, &shm)
|
||||
.context("create shm slot pool")?;
|
||||
|
||||
let mut app = App {
|
||||
registry_state: RegistryState::new(&globals),
|
||||
output_state: OutputState::new(&globals, &qh),
|
||||
seat_state: SeatState::new(&globals, &qh),
|
||||
shm,
|
||||
pool,
|
||||
window,
|
||||
width: DEFAULT_W,
|
||||
height: DEFAULT_H,
|
||||
configured: false,
|
||||
exit: false,
|
||||
};
|
||||
|
||||
while !app.exit {
|
||||
event_loop
|
||||
.dispatch(Duration::from_millis(16), &mut app)
|
||||
.context("dispatch event loop")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Window + Wayland client state shared across all protocol handlers.
|
||||
#[derive(Debug)]
|
||||
struct App {
|
||||
registry_state: RegistryState,
|
||||
output_state: OutputState,
|
||||
seat_state: SeatState,
|
||||
shm: Shm,
|
||||
pool: SlotPool,
|
||||
window: Window,
|
||||
width: u32,
|
||||
height: u32,
|
||||
configured: bool,
|
||||
exit: bool,
|
||||
}
|
||||
|
||||
impl App {
|
||||
/// Fill the surface with the background colour and present it.
|
||||
fn draw(&mut self) {
|
||||
let (w, h) = (self.width, self.height);
|
||||
let stride = w as i32 * 4;
|
||||
|
||||
let (buffer, canvas) =
|
||||
match self
|
||||
.pool
|
||||
.create_buffer(w as i32, h as i32, stride, wl_shm::Format::Argb8888)
|
||||
{
|
||||
Ok(buf) => buf,
|
||||
Err(err) => {
|
||||
tracing::error!("allocate shm buffer: {err}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let bytes = BG.to_le_bytes();
|
||||
for px in canvas.chunks_exact_mut(4) {
|
||||
px.copy_from_slice(&bytes);
|
||||
}
|
||||
|
||||
let surface = self.window.wl_surface();
|
||||
if let Err(err) = buffer.attach_to(surface) {
|
||||
tracing::error!("attach buffer: {err}");
|
||||
return;
|
||||
}
|
||||
surface.damage_buffer(0, 0, w as i32, h as i32);
|
||||
self.window.commit();
|
||||
}
|
||||
}
|
||||
|
||||
impl CompositorHandler for App {
|
||||
fn scale_factor_changed(
|
||||
&mut self,
|
||||
_: &Connection,
|
||||
_: &QueueHandle<Self>,
|
||||
_: &wl_surface::WlSurface,
|
||||
_: i32,
|
||||
) {
|
||||
}
|
||||
|
||||
fn transform_changed(
|
||||
&mut self,
|
||||
_: &Connection,
|
||||
_: &QueueHandle<Self>,
|
||||
_: &wl_surface::WlSurface,
|
||||
_: wl_output::Transform,
|
||||
) {
|
||||
}
|
||||
|
||||
fn frame(&mut self, _: &Connection, _: &QueueHandle<Self>, _: &wl_surface::WlSurface, _: u32) {}
|
||||
|
||||
fn surface_enter(
|
||||
&mut self,
|
||||
_: &Connection,
|
||||
_: &QueueHandle<Self>,
|
||||
_: &wl_surface::WlSurface,
|
||||
_: &wl_output::WlOutput,
|
||||
) {
|
||||
}
|
||||
|
||||
fn surface_leave(
|
||||
&mut self,
|
||||
_: &Connection,
|
||||
_: &QueueHandle<Self>,
|
||||
_: &wl_surface::WlSurface,
|
||||
_: &wl_output::WlOutput,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
impl WindowHandler for App {
|
||||
fn request_close(&mut self, _: &Connection, _: &QueueHandle<Self>, _: &Window) {
|
||||
self.exit = true;
|
||||
}
|
||||
|
||||
fn configure(
|
||||
&mut self,
|
||||
_: &Connection,
|
||||
_: &QueueHandle<Self>,
|
||||
_: &Window,
|
||||
configure: WindowConfigure,
|
||||
_serial: u32,
|
||||
) {
|
||||
if let (Some(w), Some(h)) = configure.new_size {
|
||||
self.width = w.get();
|
||||
self.height = h.get();
|
||||
}
|
||||
self.configured = true;
|
||||
self.draw();
|
||||
}
|
||||
}
|
||||
|
||||
impl ShmHandler for App {
|
||||
fn shm_state(&mut self) -> &mut Shm {
|
||||
&mut self.shm
|
||||
}
|
||||
}
|
||||
|
||||
impl SeatHandler for App {
|
||||
fn seat_state(&mut self) -> &mut SeatState {
|
||||
&mut self.seat_state
|
||||
}
|
||||
|
||||
fn new_seat(&mut self, _: &Connection, _: &QueueHandle<Self>, _: wl_seat::WlSeat) {}
|
||||
|
||||
fn new_capability(
|
||||
&mut self,
|
||||
_: &Connection,
|
||||
_: &QueueHandle<Self>,
|
||||
_: wl_seat::WlSeat,
|
||||
_: Capability,
|
||||
) {
|
||||
}
|
||||
|
||||
fn remove_capability(
|
||||
&mut self,
|
||||
_: &Connection,
|
||||
_: &QueueHandle<Self>,
|
||||
_: wl_seat::WlSeat,
|
||||
_: Capability,
|
||||
) {
|
||||
}
|
||||
|
||||
fn remove_seat(&mut self, _: &Connection, _: &QueueHandle<Self>, _: wl_seat::WlSeat) {}
|
||||
}
|
||||
|
||||
impl OutputHandler for App {
|
||||
fn output_state(&mut self) -> &mut OutputState {
|
||||
&mut self.output_state
|
||||
}
|
||||
|
||||
fn new_output(&mut self, _: &Connection, _: &QueueHandle<Self>, _: wl_output::WlOutput) {}
|
||||
|
||||
fn update_output(&mut self, _: &Connection, _: &QueueHandle<Self>, _: wl_output::WlOutput) {}
|
||||
|
||||
fn output_destroyed(&mut self, _: &Connection, _: &QueueHandle<Self>, _: wl_output::WlOutput) {}
|
||||
}
|
||||
|
||||
impl ProvidesRegistryState for App {
|
||||
fn registry(&mut self) -> &mut RegistryState {
|
||||
&mut self.registry_state
|
||||
}
|
||||
registry_handlers![OutputState, SeatState];
|
||||
}
|
||||
|
||||
delegate_compositor!(App);
|
||||
delegate_output!(App);
|
||||
delegate_shm!(App);
|
||||
delegate_seat!(App);
|
||||
delegate_xdg_shell!(App);
|
||||
delegate_xdg_window!(App);
|
||||
delegate_registry!(App);
|
||||
Loading…
Add table
Add a link
Reference in a new issue