use log::trace;
use smithay_client_toolkit::compositor::CompositorHandler;
use smithay_client_toolkit::compositor::CompositorState;
use smithay_client_toolkit::delegate_compositor;
use smithay_client_toolkit::delegate_keyboard;
use smithay_client_toolkit::delegate_layer;
use smithay_client_toolkit::delegate_output;
use smithay_client_toolkit::delegate_pointer;
use smithay_client_toolkit::delegate_registry;
use smithay_client_toolkit::delegate_seat;
use smithay_client_toolkit::delegate_shm;
use smithay_client_toolkit::delegate_simple;
use smithay_client_toolkit::delegate_subcompositor;
use smithay_client_toolkit::delegate_xdg_popup;
use smithay_client_toolkit::delegate_xdg_shell;
use smithay_client_toolkit::delegate_xdg_window;
use smithay_client_toolkit::output::OutputHandler;
use smithay_client_toolkit::output::OutputState;
use smithay_client_toolkit::registry::ProvidesRegistryState;
use smithay_client_toolkit::registry::RegistryState;
use smithay_client_toolkit::registry::SimpleGlobal;
use smithay_client_toolkit::registry_handlers;
use smithay_client_toolkit::seat::Capability;
use smithay_client_toolkit::seat::SeatHandler;
use smithay_client_toolkit::seat::SeatState;
use smithay_client_toolkit::seat::keyboard::KeyEvent;
use smithay_client_toolkit::seat::keyboard::KeyboardHandler;
use smithay_client_toolkit::seat::keyboard::Keysym;
use smithay_client_toolkit::seat::pointer::PointerEvent;
use smithay_client_toolkit::seat::pointer::PointerEventKind;
use smithay_client_toolkit::seat::pointer::PointerHandler;
use smithay_client_toolkit::seat::pointer::cursor_shape::CursorShapeManager;
use smithay_client_toolkit::shell::WaylandSurface;
use smithay_client_toolkit::shell::wlr_layer::LayerShell;
use smithay_client_toolkit::shell::wlr_layer::LayerShellHandler;
use smithay_client_toolkit::shell::wlr_layer::LayerSurface;
use smithay_client_toolkit::shell::wlr_layer::LayerSurfaceConfigure;
use smithay_client_toolkit::shell::xdg::XdgShell;
use smithay_client_toolkit::shell::xdg::popup::Popup;
use smithay_client_toolkit::shell::xdg::popup::PopupConfigure;
use smithay_client_toolkit::shell::xdg::popup::PopupHandler;
use smithay_client_toolkit::shell::xdg::window::Window;
use smithay_client_toolkit::shell::xdg::window::WindowConfigure;
use smithay_client_toolkit::shell::xdg::window::WindowHandler;
use smithay_client_toolkit::shm::Shm;
use smithay_client_toolkit::shm::ShmHandler;
use smithay_client_toolkit::subcompositor::SubcompositorState;
use smithay_clipboard::Clipboard;
use std::collections::HashMap;
use std::sync::Arc;
use std::sync::Mutex;
use std::thread::JoinHandle;
use wayland_backend::client::ObjectId;
use wayland_backend::client::WaylandError;
use wayland_client::Connection;
use wayland_client::Dispatch;
use wayland_client::EventQueue;
use wayland_client::Proxy;
use wayland_client::QueueHandle;
use wayland_client::globals::registry_queue_init;
use wayland_client::protocol::wl_keyboard::WlKeyboard;
use wayland_client::protocol::wl_output;
use wayland_client::protocol::wl_output::WlOutput;
use wayland_client::protocol::wl_pointer::WlPointer;
use wayland_client::protocol::wl_region::WlRegion;
use wayland_client::protocol::wl_seat;
use wayland_client::protocol::wl_surface::WlSurface;
use wayland_protocols::wp::cursor_shape::v1::client::wp_cursor_shape_device_v1::Shape;
use wayland_protocols::wp::cursor_shape::v1::client::wp_cursor_shape_device_v1::WpCursorShapeDeviceV1;
use wayland_protocols::wp::viewporter::client::wp_viewport::WpViewport;
use wayland_protocols::wp::viewporter::client::wp_viewport::{self};
use wayland_protocols::wp::viewporter::client::wp_viewporter::WpViewporter;
#[derive(Debug, Clone)]
pub enum WaylandEvent {
Frame(WlSurface, u32),
ScaleFactorChanged(WlSurface, i32),
TransformChanged(WlSurface),
SurfaceEnteredOutput(WlSurface, WlOutput),
SurfaceLeftOutput(WlSurface, WlOutput),
LayerShellClosed(LayerSurface),
LayerShellConfigure(LayerSurface, LayerSurfaceConfigure),
PopupConfigure(Popup, PopupConfigure),
PopupDone(Popup),
WindowRequestClose(Window),
WindowConfigure(Window, WindowConfigure),
OutputCreated(WlOutput),
OutputUpdated(WlOutput),
OutputDestroyed(WlOutput),
KeyboardEnter(WlSurface, Vec<u32>, Vec<Keysym>),
KeyboardLeave(WlSurface),
KeyPress(KeyEvent),
KeyRelease(KeyEvent),
KeyRepeat(KeyEvent),
PointerEvent((WlSurface, (f64, f64), PointerEventKind)),
ModifiersChanged(smithay_client_toolkit::seat::keyboard::Modifiers),
}
impl WaylandEvent {
pub fn get_wl_surface(&self) -> Option<&WlSurface> {
match self {
WaylandEvent::Frame(s, _) => Some(s),
WaylandEvent::ScaleFactorChanged(s, _) => Some(s),
WaylandEvent::TransformChanged(s) => Some(s),
WaylandEvent::SurfaceEnteredOutput(s, _) => Some(s),
WaylandEvent::SurfaceLeftOutput(s, _) => Some(s),
WaylandEvent::WindowConfigure(w, _) => Some(&w.wl_surface()),
WaylandEvent::LayerShellConfigure(layer, _) => Some(&layer.wl_surface()),
WaylandEvent::LayerShellClosed(layer) => Some(&layer.wl_surface()),
WaylandEvent::PopupConfigure(popup, _) => Some(&popup.wl_surface()),
WaylandEvent::PopupDone(popup) => Some(&popup.wl_surface()),
WaylandEvent::KeyboardEnter(s, _, _) => Some(s),
WaylandEvent::KeyboardLeave(s) => Some(s),
WaylandEvent::PointerEvent((s, _, _)) => Some(s),
_ => None,
}
}
}
#[derive(Clone)]
pub struct WaylandEventEmitter {
dispatch_fn: Arc<dyn Fn(DispatchToken) + Send + Sync + 'static>,
events: Arc<Mutex<Vec<WaylandEvent>>>,
}
impl WaylandEventEmitter {
pub fn emit_events(&self, events: Vec<WaylandEvent>) {
self.events.lock().unwrap().extend(events);
(self.dispatch_fn)(DispatchToken::external());
}
}
pub struct Application {
wayland_events: Arc<Mutex<Vec<WaylandEvent>>>,
pub conn: Connection,
event_queue: Option<EventQueue<Self>>,
pub qh: QueueHandle<Self>,
pub registry_state: RegistryState,
pub seat_state: SeatState,
pub output_state: OutputState,
pub shm_state: Shm,
pub compositor_state: CompositorState,
pub subcompositor_state: SubcompositorState,
pub xdg_shell: XdgShell,
pub layer_shell: LayerShell,
pub clipboard: Clipboard,
pub viewporter: SimpleGlobal<WpViewporter, 1>,
cursor_shape_manager: CursorShapeManager,
last_pointer_enter_serial: Option<u32>,
last_pointer: Option<WlPointer>,
pointer_shape_devices: HashMap<ObjectId, WpCursorShapeDeviceV1>,
keyboard_focused_surface: Option<ObjectId>,
dispatcher: Option<InternalDispatcherThread>,
dispatch_fn: Arc<dyn Fn(DispatchToken) + Send + Sync + 'static>,
}
impl Application {
pub fn new<T: Fn(DispatchToken) + Send + Sync + 'static>(dispatch_fn: T) -> Self {
let conn = Connection::connect_to_env().expect("Failed to connect to Wayland");
let (globals, event_queue) =
registry_queue_init::<Self>(&conn).expect("Failed to init registry");
let qh: QueueHandle<Self> = event_queue.handle();
let compositor_state =
CompositorState::bind(&globals, &qh).expect("wl_compositor not available");
let subcompositor_state =
SubcompositorState::bind(compositor_state.wl_compositor().clone(), &globals, &qh)
.expect("wl_subcompositor not available");
let xdg_shell = XdgShell::bind(&globals, &qh).expect("xdg shell not available");
let shm_state = Shm::bind(&globals, &qh).expect("wl_shm not available");
let layer_shell = LayerShell::bind(&globals, &qh).expect("layer shell not available");
let cursor_shape_manager =
CursorShapeManager::bind(&globals, &qh).expect("cursor shape manager not available");
let viewporter = SimpleGlobal::<WpViewporter, 1>::bind(&globals, &qh)
.expect("wp_viewporter not available");
let clipboard = unsafe { Clipboard::new(conn.display().id().as_ptr() as *mut _) };
Self {
wayland_events: Arc::new(Mutex::new(Vec::new())),
event_queue: Some(event_queue),
conn,
qh: qh.clone(),
subcompositor_state,
registry_state: RegistryState::new(&globals),
seat_state: SeatState::new(&globals, &qh),
output_state: OutputState::new(&globals, &qh),
shm_state,
compositor_state,
xdg_shell,
layer_shell,
clipboard,
viewporter,
cursor_shape_manager,
last_pointer_enter_serial: None,
last_pointer: None,
pointer_shape_devices: HashMap::new(),
keyboard_focused_surface: None,
dispatcher: None,
dispatch_fn: Arc::new(dispatch_fn),
}
}
pub fn take_wayland_events(&mut self) -> Vec<WaylandEvent> {
let mut events = self.wayland_events.lock().unwrap();
events.drain(..).collect()
}
pub fn get_event_emitter(&self) -> WaylandEventEmitter {
WaylandEventEmitter {
dispatch_fn: self.dispatch_fn.clone(),
events: self.wayland_events.clone(),
}
}
fn push_wayland_event(&self, event: WaylandEvent) {
self.wayland_events.lock().unwrap().push(event);
}
pub fn set_cursor(&mut self, shape: Shape) {
if let Some(serial) = self.last_pointer_enter_serial
&& let Some(pointer) = &self.last_pointer
{
let pointer_id = pointer.id();
let device = self
.pointer_shape_devices
.entry(pointer_id)
.or_insert_with(|| {
trace!(
"[COMMON] Creating new cursor shape device for pointer id {}",
pointer.id()
);
self.cursor_shape_manager
.get_shape_device(pointer, &self.qh)
});
device.set_shape(serial, shape);
}
}
pub fn run_dispatcher(&mut self) {
let event_queue = self.event_queue.take().expect("Event queue already used");
let dispatch_fn = self.dispatch_fn.clone();
let mut dispatcher =
InternalDispatcherThread::new(self.conn.clone(), event_queue, dispatch_fn);
dispatcher.start_thread();
self.dispatcher = Some(dispatcher);
}
pub fn dispatch_pending(&mut self, token: DispatchToken) -> Vec<WaylandEvent> {
if !token.from_wayland() {
self.wayland_events.lock().unwrap().drain(..).collect()
} else {
if let Some(mut dispatcher) = self.dispatcher.take() {
let res = dispatcher.dispatch_pending(self);
self.dispatcher = Some(dispatcher);
res
} else {
panic!("Dispatcher not running, call run_dispatcher first");
}
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Default)]
pub struct DispatchToken(bool);
impl DispatchToken {
pub(crate) fn wayland() -> Self {
DispatchToken(true)
}
pub(crate) fn external() -> Self {
DispatchToken(false)
}
fn from_wayland(&self) -> bool {
self.0
}
}
enum InternalDispatcherError {
RecvError(std::sync::mpsc::RecvError),
WaylandError(WaylandError),
}
impl From<std::sync::mpsc::RecvError> for InternalDispatcherError {
fn from(e: std::sync::mpsc::RecvError) -> Self {
InternalDispatcherError::RecvError(e)
}
}
impl From<WaylandError> for InternalDispatcherError {
fn from(e: WaylandError) -> Self {
InternalDispatcherError::WaylandError(e)
}
}
struct InternalDispatcherThread {
conn: Option<Connection>,
event_queue: EventQueue<Application>,
count_reader: Option<std::sync::mpsc::Receiver<Option<usize>>>,
count_sender: std::sync::mpsc::Sender<Option<usize>>,
dispatch_fn: Option<Arc<dyn Fn(DispatchToken) + Sync + Send + 'static>>,
#[allow(dead_code)]
reader_thread: Option<JoinHandle<()>>,
}
impl InternalDispatcherThread {
fn new(
conn: Connection,
event_queue: EventQueue<Application>,
dispatch_fn: Arc<dyn Fn(DispatchToken) + Sync + Send + 'static>,
) -> Self {
let (count_sender, count_reader) = std::sync::mpsc::channel::<Option<usize>>();
InternalDispatcherThread {
conn: Some(conn),
event_queue,
count_sender,
count_reader: Some(count_reader),
dispatch_fn: Some(dispatch_fn),
reader_thread: None,
}
}
pub(crate) fn start_thread(&mut self) {
let count_reader = self.count_reader.take().expect("Count reader missing");
let dispatch_fn = self.dispatch_fn.take().expect("Dispatch function missing");
let conn = self.conn.take().expect("Connection missing");
self.reader_thread = Some(std::thread::spawn(move || {
(dispatch_fn)(DispatchToken::wayland());
loop {
match InternalDispatcherThread::read_blocking(&conn, &dispatch_fn, &count_reader) {
Ok(cont) => {
if !cont {
break;
}
}
Err(InternalDispatcherError::RecvError(_)) => {
break;
}
Err(InternalDispatcherError::WaylandError(e)) => {
eprintln!("Error in Wayland reader thread: {:?}", e);
break;
}
}
}
}));
}
#[inline]
fn read_blocking(
conn: &Connection,
dispatch_fn: &Arc<dyn Fn(DispatchToken) + Sync + Send + 'static>,
count_reader: &std::sync::mpsc::Receiver<Option<usize>>,
) -> Result<bool, InternalDispatcherError> {
match count_reader.recv()? {
Some(count) => {
if count > 0 {
(dispatch_fn)(DispatchToken::wayland());
return Ok(true);
}
}
None => return Ok(false),
}
conn.flush()?;
if let Some(guard) = conn.prepare_read() {
guard.read_without_dispatch()?;
} else {
#[cfg(feature = "_example")]
println!("♦️♦️♦️♦️♦️ Failed to read");
}
(dispatch_fn)(DispatchToken::wayland());
Ok(true) }
pub(crate) fn dispatch_pending(&mut self, app: &mut Application) -> Vec<WaylandEvent> {
let count = self
.event_queue
.dispatch_pending(app)
.expect("Wayland dispatch failed");
let _ = self.count_sender.send(Some(count));
app.take_wayland_events()
}
}
impl CompositorHandler for Application {
fn scale_factor_changed(
&mut self,
_conn: &Connection,
_qh: &QueueHandle<Self>,
surface: &WlSurface,
new_factor: i32,
) {
self.push_wayland_event(WaylandEvent::ScaleFactorChanged(
surface.clone(),
new_factor,
));
}
fn transform_changed(
&mut self,
_conn: &Connection,
_qh: &QueueHandle<Self>,
surface: &WlSurface,
_new_transform: wl_output::Transform,
) {
self.push_wayland_event(WaylandEvent::TransformChanged(surface.clone()));
}
fn frame(
&mut self,
_conn: &Connection,
_qh: &QueueHandle<Self>,
surface: &WlSurface,
time: u32,
) {
self.push_wayland_event(WaylandEvent::Frame(surface.clone(), time));
}
fn surface_enter(
&mut self,
_conn: &Connection,
_qh: &QueueHandle<Self>,
surface: &WlSurface,
output: &WlOutput,
) {
self.push_wayland_event(WaylandEvent::SurfaceEnteredOutput(
surface.clone(),
output.clone(),
));
}
fn surface_leave(
&mut self,
_conn: &Connection,
_qh: &QueueHandle<Self>,
surface: &WlSurface,
output: &WlOutput,
) {
self.push_wayland_event(WaylandEvent::SurfaceLeftOutput(
surface.clone(),
output.clone(),
));
}
}
impl OutputHandler for Application {
fn output_state(&mut self) -> &mut OutputState {
&mut self.output_state
}
fn new_output(&mut self, _conn: &Connection, _qh: &QueueHandle<Self>, output: WlOutput) {
self.push_wayland_event(WaylandEvent::OutputCreated(output));
}
fn update_output(&mut self, _conn: &Connection, _qh: &QueueHandle<Self>, output: WlOutput) {
self.push_wayland_event(WaylandEvent::OutputUpdated(output));
}
fn output_destroyed(&mut self, _conn: &Connection, _qh: &QueueHandle<Self>, output: WlOutput) {
self.push_wayland_event(WaylandEvent::OutputDestroyed(output));
}
}
impl LayerShellHandler for Application {
fn closed(&mut self, _conn: &Connection, _qh: &QueueHandle<Self>, target_layer: &LayerSurface) {
self.push_wayland_event(WaylandEvent::LayerShellClosed(target_layer.clone()));
}
fn configure(
&mut self,
_conn: &Connection,
_qh: &QueueHandle<Self>,
target_layer: &LayerSurface,
configure: LayerSurfaceConfigure,
_serial: u32,
) {
self.push_wayland_event(WaylandEvent::LayerShellConfigure(
target_layer.clone(),
configure.clone(),
));
}
}
impl PopupHandler for Application {
fn configure(
&mut self,
_conn: &Connection,
_qh: &QueueHandle<Self>,
target_popup: &Popup,
config: PopupConfigure,
) {
self.push_wayland_event(WaylandEvent::PopupConfigure(
target_popup.clone(),
config.clone(),
));
trace!("[COMMON] XDG popup configure");
}
fn done(&mut self, _conn: &Connection, _qh: &QueueHandle<Self>, target_popup: &Popup) {
self.push_wayland_event(WaylandEvent::PopupDone(target_popup.clone()));
trace!("[COMMON] XDG popup done");
}
}
impl WindowHandler for Application {
fn request_close(&mut self, _: &Connection, _: &QueueHandle<Self>, target_window: &Window) {
trace!("[COMMON] XDG window close requested");
self.push_wayland_event(WaylandEvent::WindowRequestClose(target_window.clone()));
}
fn configure(
&mut self,
_conn: &Connection,
_qh: &QueueHandle<Self>,
target_window: &Window,
configure: WindowConfigure,
_serial: u32,
) {
self.push_wayland_event(WaylandEvent::WindowConfigure(
target_window.clone(),
configure.clone(),
));
trace!("[COMMON] XDG window configure");
}
}
impl PointerHandler for Application {
fn pointer_frame(
&mut self,
_conn: &Connection,
_qh: &QueueHandle<Self>,
pointer: &WlPointer,
events: &[PointerEvent],
) {
for event in events {
match event.kind {
PointerEventKind::Enter { serial } => {
self.last_pointer_enter_serial = Some(serial);
self.last_pointer = Some(pointer.clone());
}
_ => {}
}
self.push_wayland_event(WaylandEvent::PointerEvent((
event.surface.clone(),
event.position,
event.kind.clone(),
)));
}
}
}
impl KeyboardHandler for Application {
fn enter(
&mut self,
_conn: &Connection,
_qh: &QueueHandle<Self>,
_keyboard: &WlKeyboard,
surface: &WlSurface,
_serial: u32,
_raw: &[u32],
_keysyms: &[Keysym],
) {
trace!("[MAIN] Keyboard focus gained on surface {:?}", surface.id());
self.push_wayland_event(WaylandEvent::KeyboardEnter(
surface.clone(),
_raw.to_vec(),
_keysyms.to_vec(),
));
self.keyboard_focused_surface = Some(surface.id());
}
fn leave(
&mut self,
_conn: &Connection,
_qh: &QueueHandle<Self>,
_keyboard: &WlKeyboard,
surface: &WlSurface,
_serial: u32,
) {
trace!("[MAIN] Keyboard focus lost");
self.push_wayland_event(WaylandEvent::KeyboardLeave(surface.clone()));
}
fn press_key(
&mut self,
_conn: &Connection,
_qh: &QueueHandle<Self>,
_keyboard: &WlKeyboard,
_serial: u32,
event: KeyEvent,
) {
trace!("[MAIN] Key pressed: keycode={}", event.raw_code);
self.push_wayland_event(WaylandEvent::KeyPress(event.clone()));
}
fn release_key(
&mut self,
_conn: &Connection,
_qh: &QueueHandle<Self>,
_keyboard: &WlKeyboard,
_serial: u32,
event: KeyEvent,
) {
trace!("[MAIN] Key released: keycode={}", event.raw_code);
self.push_wayland_event(WaylandEvent::KeyRelease(event.clone()));
}
fn update_modifiers(
&mut self,
_conn: &Connection,
_qh: &QueueHandle<Self>,
_keyboard: &WlKeyboard,
_serial: u32,
modifiers: smithay_client_toolkit::seat::keyboard::Modifiers,
_raw_modifiers: smithay_client_toolkit::seat::keyboard::RawModifiers,
_layout: u32,
) {
self.push_wayland_event(WaylandEvent::ModifiersChanged(modifiers.clone()));
}
fn repeat_key(
&mut self,
_conn: &Connection,
_qh: &QueueHandle<Self>,
_keyboard: &WlKeyboard,
_serial: u32,
event: KeyEvent,
) {
trace!("[MAIN] Key repeated: keycode={}", event.raw_code);
self.push_wayland_event(WaylandEvent::KeyRepeat(event.clone()));
}
}
impl SeatHandler for Application {
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,
_conn: &Connection,
qh: &QueueHandle<Self>,
seat: wl_seat::WlSeat,
capability: Capability,
) {
trace!("[MAIN] New seat capability: {:?}", capability);
if capability == Capability::Keyboard {
trace!("[MAIN] Creating wl_keyboard");
match self.seat_state.get_keyboard(qh, &seat, None) {
Ok(_wl_keyboard) => {
trace!("[MAIN] wl_keyboard created successfully");
}
Err(e) => {
trace!("[MAIN] Failed to create wl_keyboard: {:?}", e);
}
}
}
if capability == Capability::Pointer {
let _ = self.seat_state.get_pointer(&qh, &seat);
trace!("[MAIN] Creating themed pointer");
}
}
fn remove_capability(
&mut self,
_conn: &Connection,
_: &QueueHandle<Self>,
_: wl_seat::WlSeat,
_capability: Capability,
) {
}
fn remove_seat(&mut self, _: &Connection, _: &QueueHandle<Self>, _: wl_seat::WlSeat) {}
}
impl ShmHandler for Application {
fn shm_state(&mut self) -> &mut Shm {
&mut self.shm_state
}
}
impl ProvidesRegistryState for Application {
registry_handlers![OutputState];
fn registry(&mut self) -> &mut RegistryState {
&mut self.registry_state
}
}
impl AsMut<SimpleGlobal<WpViewporter, 1>> for Application {
fn as_mut(&mut self) -> &mut SimpleGlobal<WpViewporter, 1> {
&mut self.viewporter
}
}
impl Dispatch<WpViewport, ()> for Application {
fn event(
_: &mut Application,
_: &WpViewport,
_: wp_viewport::Event,
_: &(),
_: &Connection,
_: &QueueHandle<Self>,
) {
}
}
impl Dispatch<WlRegion, ()> for Application {
fn event(
_state: &mut Self,
_proxy: &WlRegion,
_event: <WlRegion as wayland_client::Proxy>::Event,
_data: &(),
_conn: &Connection,
_qhandle: &QueueHandle<Self>,
) {
}
}
delegate_compositor!(Application);
delegate_subcompositor!(Application);
delegate_output!(Application);
delegate_shm!(Application);
delegate_seat!(Application);
delegate_keyboard!(Application);
delegate_pointer!(Application);
delegate_layer!(Application);
delegate_xdg_shell!(Application);
delegate_xdg_window!(Application);
delegate_xdg_popup!(Application);
delegate_registry!(Application);
delegate_simple!(Application, WpViewporter, 1);