use std::cmp::min; use std::collections::HashMap; use std::fmt; use std::fmt::Write as _; use std::os::unix::net::UnixStream; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use std::time::Duration; use calloop::EventLoop; use calloop_wayland_source::WaylandSource; use single_pixel_buffer::v1::client::wp_single_pixel_buffer_manager_v1::WpSinglePixelBufferManagerV1; use smithay::reexports::wayland_protocols::wp::single_pixel_buffer; use smithay::reexports::wayland_protocols::wp::viewporter::client::wp_viewport::WpViewport; use smithay::reexports::wayland_protocols::wp::viewporter::client::wp_viewporter::WpViewporter; use smithay::reexports::wayland_protocols::xdg::shell::client::xdg_surface::{self, XdgSurface}; use smithay::reexports::wayland_protocols::xdg::shell::client::xdg_toplevel::{self, XdgToplevel}; use smithay::reexports::wayland_protocols::xdg::shell::client::xdg_wm_base::{self, XdgWmBase}; use smithay::reexports::wayland_protocols_wlr::layer_shell::v1::client::zwlr_layer_shell_v1::{ self, ZwlrLayerShellV1, }; use smithay::reexports::wayland_protocols_wlr::layer_shell::v1::client::zwlr_layer_surface_v1::{ self, ZwlrLayerSurfaceV1, }; use wayland_backend::client::Backend; use wayland_client::globals::Global; use wayland_client::protocol::wl_buffer::{self, WlBuffer}; use wayland_client::protocol::wl_callback::{self, WlCallback}; use wayland_client::protocol::wl_compositor::WlCompositor; use wayland_client::protocol::wl_display::WlDisplay; use wayland_client::protocol::wl_output::{self, WlOutput}; use wayland_client::protocol::wl_registry::{self, WlRegistry}; use wayland_client::protocol::wl_surface::{self, WlSurface}; use wayland_client::{Connection, Dispatch, Proxy as _, QueueHandle}; use crate::utils::id::IdCounter; pub struct Client { pub id: ClientId, pub event_loop: EventLoop<'static, State>, pub connection: Connection, pub qh: QueueHandle, pub display: WlDisplay, pub state: State, } pub struct State { pub qh: QueueHandle, pub globals: Vec, pub outputs: HashMap, pub compositor: Option, pub xdg_wm_base: Option, pub layer_shell: Option, pub spbm: Option, pub viewporter: Option, pub windows: Vec, pub layers: Vec, } pub struct Window { pub qh: QueueHandle, pub spbm: WpSinglePixelBufferManagerV1, pub surface: WlSurface, pub xdg_surface: XdgSurface, pub xdg_toplevel: XdgToplevel, pub viewport: WpViewport, pub pending_configure: Configure, pub configures_received: Vec<(u32, Configure)>, pub close_requested: bool, pub configures_looked_at: usize, } pub struct LayerSurface { pub qh: QueueHandle, pub spbm: WpSinglePixelBufferManagerV1, pub surface: WlSurface, pub layer_surface: ZwlrLayerSurfaceV1, pub viewport: WpViewport, pub configures_received: Vec<(u32, LayerConfigure)>, pub close_requested: bool, pub configures_looked_at: usize, } #[derive(Debug, Clone, Default)] pub struct Configure { pub size: (i32, i32), pub bounds: Option<(i32, i32)>, pub states: Vec, } #[derive(Debug, Clone, Copy)] pub struct LayerConfigure { pub size: (u32, u32), } #[derive(Clone, Copy, Default)] pub struct LayerMargin { pub top: i32, pub right: i32, pub bottom: i32, pub left: i32, } #[derive(Clone, Copy, Default)] pub struct LayerConfigureProps { pub size: Option<(u32, u32)>, pub anchor: Option, pub exclusive_zone: Option, pub margin: Option, pub kb_interactivity: Option, pub layer: Option, pub exclusive_edge: Option, } #[derive(Default)] pub struct SyncData { pub done: AtomicBool, } static CLIENT_ID_COUNTER: IdCounter = IdCounter::new(); #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct ClientId(u64); impl ClientId { fn next() -> ClientId { ClientId(CLIENT_ID_COUNTER.next()) } } impl fmt::Display for Configure { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "size: {} × {}, ", self.size.0, self.size.1)?; if let Some(bounds) = self.bounds { write!(f, "bounds: {} × {}, ", bounds.0, bounds.1)?; } else { write!(f, "bounds: none, ")?; } write!(f, "states: {:?}", self.states)?; Ok(()) } } impl fmt::Display for LayerConfigure { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "size: {} × {}", self.size.0, self.size.1)?; Ok(()) } } impl Client { pub fn new(stream: UnixStream) -> Self { let id = ClientId::next(); let event_loop = EventLoop::try_new().unwrap(); let backend = Backend::connect(stream).unwrap(); let connection = Connection::from_backend(backend); let queue = connection.new_event_queue(); let qh = queue.handle(); WaylandSource::new(connection.clone(), queue) .insert(event_loop.handle()) .unwrap(); let display = connection.display(); let _registry = display.get_registry(&qh, ()); connection.flush().unwrap(); let state = State { qh: qh.clone(), globals: Vec::new(), outputs: HashMap::new(), compositor: None, xdg_wm_base: None, layer_shell: None, spbm: None, viewporter: None, windows: Vec::new(), layers: Vec::new(), }; Self { id, event_loop, connection, qh, display, state, } } pub fn dispatch(&mut self) { self.event_loop .dispatch(Duration::ZERO, &mut self.state) .unwrap(); if let Some(error) = self.connection.protocol_error() { panic!("{error}"); } } pub fn send_sync(&self) -> Arc { let data = Arc::new(SyncData::default()); self.display.sync(&self.qh, data.clone()); self.connection.flush().unwrap(); data } pub fn create_window(&mut self) -> &mut Window { self.state.create_window() } pub fn window(&mut self, surface: &WlSurface) -> &mut Window { self.state.window(surface) } pub fn create_layer( &mut self, output: Option<&WlOutput>, layer: zwlr_layer_shell_v1::Layer, namespace: &str, ) -> &mut LayerSurface { self.state.create_layer(output, layer, namespace.to_owned()) } pub fn layer(&mut self, surface: &WlSurface) -> &mut LayerSurface { self.state.layer(surface) } pub fn output(&mut self, name: &str) -> WlOutput { self.state .outputs .iter() .find(|(_, v)| *v == name) .unwrap() .0 .clone() } } impl State { pub fn create_window(&mut self) -> &mut Window { let compositor = self.compositor.as_ref().unwrap(); let xdg_wm_base = self.xdg_wm_base.as_ref().unwrap(); let viewporter = self.viewporter.as_ref().unwrap(); let surface = compositor.create_surface(&self.qh, ()); let xdg_surface = xdg_wm_base.get_xdg_surface(&surface, &self.qh, ()); let xdg_toplevel = xdg_surface.get_toplevel(&self.qh, ()); let viewport = viewporter.get_viewport(&surface, &self.qh, ()); let window = Window { qh: self.qh.clone(), spbm: self.spbm.clone().unwrap(), surface, xdg_surface, xdg_toplevel, viewport, pending_configure: Configure::default(), configures_received: Vec::new(), close_requested: false, configures_looked_at: 0, }; self.windows.push(window); self.windows.last_mut().unwrap() } pub fn window(&mut self, surface: &WlSurface) -> &mut Window { self.windows .iter_mut() .find(|w| w.surface == *surface) .unwrap() } pub fn create_layer( &mut self, output: Option<&WlOutput>, layer: zwlr_layer_shell_v1::Layer, namespace: String, ) -> &mut LayerSurface { let compositor = self.compositor.as_ref().unwrap(); let layer_shell = self.layer_shell.as_ref().unwrap(); let viewporter = self.viewporter.as_ref().unwrap(); let surface = compositor.create_surface(&self.qh, ()); let layer_surface = layer_shell.get_layer_surface(&surface, output, layer, namespace, &self.qh, ()); let viewport = viewporter.get_viewport(&surface, &self.qh, ()); let layer_surface = LayerSurface { qh: self.qh.clone(), spbm: self.spbm.clone().unwrap(), surface, layer_surface, viewport, configures_received: Vec::new(), close_requested: false, configures_looked_at: 0, }; self.layers.push(layer_surface); self.layers.last_mut().unwrap() } pub fn layer(&mut self, surface: &WlSurface) -> &mut LayerSurface { self.layers .iter_mut() .find(|w| w.surface == *surface) .unwrap() } } impl Window { pub fn commit(&self) { self.surface.commit(); } pub fn ack_last(&self) { let serial = self.configures_received.last().unwrap().0; self.xdg_surface.ack_configure(serial); } pub fn ack_last_and_commit(&self) { self.ack_last(); self.commit(); } pub fn attach_new_buffer(&self) { let buffer = self.spbm.create_u32_rgba_buffer(0, 0, 0, 0, &self.qh, ()); self.surface.attach(Some(&buffer), 0, 0); } pub fn attach_null(&self) { self.surface.attach(None, 0, 0); } pub fn set_size(&self, w: u16, h: u16) { self.viewport.set_destination(i32::from(w), i32::from(h)); } pub fn set_fullscreen(&self, output: Option<&WlOutput>) { self.xdg_toplevel.set_fullscreen(output); } pub fn unset_fullscreen(&self) { self.xdg_toplevel.unset_fullscreen(); } pub fn set_maximized(&self) { self.xdg_toplevel.set_maximized(); } pub fn unset_maximized(&self) { self.xdg_toplevel.unset_maximized(); } pub fn set_parent(&self, parent: Option<&XdgToplevel>) { self.xdg_toplevel.set_parent(parent); } pub fn set_title(&self, title: &str) { self.xdg_toplevel.set_title(title.to_owned()); } pub fn recent_configures(&mut self) -> impl Iterator { let start = self.configures_looked_at; self.configures_looked_at = self.configures_received.len(); self.configures_received[start..].iter().map(|(_, c)| c) } pub fn format_recent_configures(&mut self) -> String { let mut buf = String::new(); for configure in self.recent_configures() { if !buf.is_empty() { buf.push('\n'); } write!(buf, "{configure}").unwrap(); } buf } } impl LayerSurface { pub fn commit(&self) { self.surface.commit(); } pub fn ack_last(&self) { let serial = self.configures_received.last().unwrap().0; self.layer_surface.ack_configure(serial); } pub fn ack_last_and_commit(&self) { self.ack_last(); self.commit(); } pub fn set_configure_props(&self, props: LayerConfigureProps) { let LayerConfigureProps { size, anchor, exclusive_zone, margin, kb_interactivity, layer, exclusive_edge, } = props; if let Some(x) = size { self.layer_surface.set_size(x.0, x.1); } if let Some(x) = anchor { self.layer_surface.set_anchor(x); } if let Some(x) = exclusive_zone { self.layer_surface.set_exclusive_zone(x); } if let Some(x) = margin { self.layer_surface .set_margin(x.top, x.right, x.bottom, x.left); } if let Some(x) = kb_interactivity { self.layer_surface.set_keyboard_interactivity(x); } if let Some(x) = layer { self.layer_surface.set_layer(x); } if let Some(x) = exclusive_edge { self.layer_surface.set_exclusive_edge(x); } } pub fn attach_new_buffer(&self) { let buffer = self.spbm.create_u32_rgba_buffer(0, 0, 0, 0, &self.qh, ()); self.surface.attach(Some(&buffer), 0, 0); } pub fn attach_null(&self) { self.surface.attach(None, 0, 0); } pub fn set_size(&self, w: u16, h: u16) { self.viewport.set_destination(i32::from(w), i32::from(h)); } pub fn recent_configures(&mut self) -> impl Iterator { let start = self.configures_looked_at; self.configures_looked_at = self.configures_received.len(); self.configures_received[start..].iter().map(|(_, c)| c) } pub fn format_recent_configures(&mut self) -> String { let mut buf = String::new(); for configure in self.recent_configures() { if !buf.is_empty() { buf.push('\n'); } write!(buf, "{configure}").unwrap(); } buf } } impl Dispatch> for State { fn event( _state: &mut Self, _proxy: &WlCallback, event: ::Event, data: &Arc, _conn: &Connection, _qhandle: &QueueHandle, ) { match event { wl_callback::Event::Done { .. } => data.done.store(true, Ordering::Relaxed), _ => unreachable!(), } } } impl Dispatch for State { fn event( state: &mut Self, registry: &WlRegistry, event: ::Event, _data: &(), _conn: &Connection, qh: &QueueHandle, ) { match event { wl_registry::Event::Global { name, interface, version, } => { if interface == WlCompositor::interface().name { let version = min(version, WlCompositor::interface().version); state.compositor = Some(registry.bind(name, version, qh, ())); } else if interface == XdgWmBase::interface().name { let version = min(version, XdgWmBase::interface().version); state.xdg_wm_base = Some(registry.bind(name, version, qh, ())); } else if interface == ZwlrLayerShellV1::interface().name { let version = min(version, ZwlrLayerShellV1::interface().version); state.layer_shell = Some(registry.bind(name, version, qh, ())); } else if interface == WpSinglePixelBufferManagerV1::interface().name { let version = min(version, WpSinglePixelBufferManagerV1::interface().version); state.spbm = Some(registry.bind(name, version, qh, ())); } else if interface == WpViewporter::interface().name { let version = min(version, WpViewporter::interface().version); state.viewporter = Some(registry.bind(name, version, qh, ())); } else if interface == WlOutput::interface().name { let version = min(version, WlOutput::interface().version); let output = registry.bind(name, version, qh, ()); state.outputs.insert(output, String::new()); } let global = Global { name, interface, version, }; state.globals.push(global); } wl_registry::Event::GlobalRemove { .. } => (), _ => unreachable!(), } } } impl Dispatch for State { fn event( state: &mut Self, output: &WlOutput, event: ::Event, _data: &(), _conn: &Connection, _qhandle: &QueueHandle, ) { match event { wl_output::Event::Geometry { .. } => (), wl_output::Event::Mode { .. } => (), wl_output::Event::Done => (), wl_output::Event::Scale { .. } => (), wl_output::Event::Name { name } => { *state.outputs.get_mut(output).unwrap() = name; } wl_output::Event::Description { .. } => (), _ => unreachable!(), } } } impl Dispatch for State { fn event( _state: &mut Self, _proxy: &WlCompositor, _event: ::Event, _data: &(), _conn: &Connection, _qhandle: &QueueHandle, ) { unreachable!() } } impl Dispatch for State { fn event( _state: &mut Self, xdg_wm_base: &XdgWmBase, event: ::Event, _data: &(), _conn: &Connection, _qhandle: &QueueHandle, ) { match event { xdg_wm_base::Event::Ping { serial } => { xdg_wm_base.pong(serial); } _ => unreachable!(), } } } impl Dispatch for State { fn event( _state: &mut Self, _proxy: &ZwlrLayerShellV1, _event: ::Event, _data: &(), _conn: &Connection, _qhandle: &QueueHandle, ) { unreachable!() } } impl Dispatch for State { fn event( _state: &mut Self, _proxy: &WlSurface, event: ::Event, _data: &(), _conn: &Connection, _qhandle: &QueueHandle, ) { match event { wl_surface::Event::Enter { .. } => (), wl_surface::Event::Leave { .. } => (), wl_surface::Event::PreferredBufferScale { .. } => (), wl_surface::Event::PreferredBufferTransform { .. } => (), _ => unreachable!(), } } } impl Dispatch for State { fn event( state: &mut Self, xdg_surface: &XdgSurface, event: ::Event, _data: &(), _conn: &Connection, _qhandle: &QueueHandle, ) { match event { xdg_surface::Event::Configure { serial } => { let window = state .windows .iter_mut() .find(|w| w.xdg_surface == *xdg_surface) .unwrap(); let configure = window.pending_configure.clone(); window.configures_received.push((serial, configure)); } _ => unreachable!(), } } } impl Dispatch for State { fn event( state: &mut Self, xdg_toplevel: &XdgToplevel, event: ::Event, _data: &(), _conn: &Connection, _qhandle: &QueueHandle, ) { let window = state .windows .iter_mut() .find(|w| w.xdg_toplevel == *xdg_toplevel) .unwrap(); match event { xdg_toplevel::Event::Configure { width, height, states, } => { let configure = &mut window.pending_configure; configure.size = (width, height); configure.states = states .chunks_exact(4) .flat_map(TryInto::<[u8; 4]>::try_into) .map(u32::from_ne_bytes) .flat_map(xdg_toplevel::State::try_from) .collect(); } xdg_toplevel::Event::Close => { window.close_requested = true; } xdg_toplevel::Event::ConfigureBounds { width, height } => { window.pending_configure.bounds = Some((width, height)); } xdg_toplevel::Event::WmCapabilities { .. } => (), _ => unreachable!(), } } } impl Dispatch for State { fn event( state: &mut Self, layer_surface: &ZwlrLayerSurfaceV1, event: ::Event, _data: &(), _conn: &Connection, _qhandle: &QueueHandle, ) { let layer_surface = state .layers .iter_mut() .find(|w| w.layer_surface == *layer_surface) .unwrap(); match event { zwlr_layer_surface_v1::Event::Configure { serial, width, height, } => { let configure = LayerConfigure { size: (width, height), }; layer_surface.configures_received.push((serial, configure)); } zwlr_layer_surface_v1::Event::Closed => layer_surface.close_requested = true, _ => unreachable!(), } } } impl Dispatch for State { fn event( _state: &mut Self, _proxy: &WlBuffer, event: ::Event, _data: &(), _conn: &Connection, _qhandle: &QueueHandle, ) { match event { wl_buffer::Event::Release => (), _ => unreachable!(), } } } impl Dispatch for State { fn event( _state: &mut Self, _proxy: &WpSinglePixelBufferManagerV1, _event: ::Event, _data: &(), _conn: &Connection, _qhandle: &QueueHandle, ) { unreachable!() } } impl Dispatch for State { fn event( _state: &mut Self, _proxy: &WpViewporter, _event: ::Event, _data: &(), _conn: &Connection, _qhandle: &QueueHandle, ) { unreachable!() } } impl Dispatch for State { fn event( _state: &mut Self, _proxy: &WpViewport, _event: ::Event, _data: &(), _conn: &Connection, _qhandle: &QueueHandle, ) { unreachable!() } }