use std::{convert::TryInto, time::Duration}; use smithay_client_toolkit::activation::RequestData; use smithay_client_toolkit::reexports::calloop::{EventLoop, LoopHandle}; use smithay_client_toolkit::reexports::calloop_wayland_source::WaylandSource; use smithay_client_toolkit::{ activation::{ActivationHandler, ActivationState}, compositor::{CompositorHandler, CompositorState}, delegate_activation, delegate_compositor, delegate_keyboard, delegate_output, delegate_pointer, delegate_registry, delegate_seat, delegate_shm, delegate_xdg_shell, delegate_xdg_window, output::{OutputHandler, OutputState}, registry::{ProvidesRegistryState, RegistryState}, registry_handlers, seat::{ keyboard::{KeyEvent, KeyboardHandler, Keysym, Modifiers}, pointer::{PointerEvent, PointerEventKind, PointerHandler}, Capability, SeatHandler, SeatState, }, shell::{ xdg::{ window::{Window, WindowConfigure, WindowDecorations, WindowHandler}, XdgShell, }, WaylandSurface, }, shm::{ slot::{Buffer, SlotPool}, Shm, ShmHandler, }, }; use wayland_client::{ globals::registry_queue_init, protocol::{wl_keyboard, wl_output, wl_pointer, wl_seat, wl_shm, wl_surface}, Connection, QueueHandle, }; fn main() { env_logger::init(); // All Wayland apps start by connecting the compositor (server). let conn = Connection::connect_to_env().unwrap(); // Enumerate the list of globals to get the protocols the server implements. let (globals, event_queue) = registry_queue_init(&conn).unwrap(); let qh = event_queue.handle(); let mut event_loop: EventLoop = EventLoop::try_new().expect("Failed to initialize the event loop!"); let loop_handle = event_loop.handle(); WaylandSource::new(conn.clone(), event_queue).insert(loop_handle).unwrap(); // The compositor (not to be confused with the server which is commonly called the compositor) allows // configuring surfaces to be presented. let compositor = CompositorState::bind(&globals, &qh).expect("wl_compositor not available"); // For desktop platforms, the XDG shell is the standard protocol for creating desktop windows. let xdg_shell = XdgShell::bind(&globals, &qh).expect("xdg shell is not available"); // Since we are not using the GPU in this example, we use wl_shm to allow software rendering to a buffer // we share with the compositor process. let shm = Shm::bind(&globals, &qh).expect("wl shm is not available."); // If the compositor supports xdg-activation it probably wants us to use it to get focus let xdg_activation = ActivationState::bind(&globals, &qh).ok(); // A window is created from a surface. let surface = compositor.create_surface(&qh); // And then we can create the window. let window = xdg_shell.create_window(surface, WindowDecorations::RequestServer, &qh); // Configure the window, this may include hints to the compositor about the desired minimum size of the // window, app id for WM identification, the window title, etc. window.set_title("A wayland window"); // GitHub does not let projects use the `org.github` domain but the `io.github` domain is fine. window.set_app_id("io.github.smithay.client-toolkit.SimpleWindow"); window.set_min_size(Some((256, 256))); // In order for the window to be mapped, we need to perform an initial commit with no attached buffer. // For more info, see WaylandSurface::commit // // The compositor will respond with an initial configure that we can then use to present to the window with // the correct options. window.commit(); // To request focus, we first need to request a token if let Some(activation) = xdg_activation.as_ref() { activation.request_token( &qh, RequestData { seat_and_serial: None, surface: Some(window.wl_surface().clone()), app_id: Some(String::from("io.github.smithay.client-toolkit.SimpleWindow")), }, ) } // We don't know how large the window will be yet, so lets assume the minimum size we suggested for the // initial memory allocation. let pool = SlotPool::new(256 * 256 * 4, &shm).expect("Failed to create pool"); let mut simple_window = SimpleWindow { // Seats and outputs may be hotplugged at runtime, therefore we need to setup a registry state to // listen for seats and outputs. registry_state: RegistryState::new(&globals), seat_state: SeatState::new(&globals, &qh), output_state: OutputState::new(&globals, &qh), shm, xdg_activation, exit: false, first_configure: true, pool, width: 256, height: 256, shift: None, buffer: None, window, keyboard: None, keyboard_focus: false, pointer: None, loop_handle: event_loop.handle(), }; // We don't draw immediately, the configure will notify us when to first draw. loop { event_loop.dispatch(Duration::from_millis(16), &mut simple_window).unwrap(); if simple_window.exit { println!("exiting example"); break; } } } struct SimpleWindow { registry_state: RegistryState, seat_state: SeatState, output_state: OutputState, shm: Shm, xdg_activation: Option, exit: bool, first_configure: bool, pool: SlotPool, width: u32, height: u32, shift: Option, buffer: Option, window: Window, keyboard: Option, keyboard_focus: bool, pointer: Option, loop_handle: LoopHandle<'static, SimpleWindow>, } impl CompositorHandler for SimpleWindow { fn scale_factor_changed( &mut self, _conn: &Connection, _qh: &QueueHandle, _surface: &wl_surface::WlSurface, _new_factor: i32, ) { // Not needed for this example. } fn transform_changed( &mut self, _conn: &Connection, _qh: &QueueHandle, _surface: &wl_surface::WlSurface, _new_transform: wl_output::Transform, ) { // Not needed for this example. } fn frame( &mut self, conn: &Connection, qh: &QueueHandle, _surface: &wl_surface::WlSurface, _time: u32, ) { self.draw(conn, qh); } fn surface_enter( &mut self, _conn: &Connection, _qh: &QueueHandle, _surface: &wl_surface::WlSurface, _output: &wl_output::WlOutput, ) { // Not needed for this example. } fn surface_leave( &mut self, _conn: &Connection, _qh: &QueueHandle, _surface: &wl_surface::WlSurface, _output: &wl_output::WlOutput, ) { // Not needed for this example. } } impl OutputHandler for SimpleWindow { fn output_state(&mut self) -> &mut OutputState { &mut self.output_state } fn new_output( &mut self, _conn: &Connection, _qh: &QueueHandle, _output: wl_output::WlOutput, ) { } fn update_output( &mut self, _conn: &Connection, _qh: &QueueHandle, _output: wl_output::WlOutput, ) { } fn output_destroyed( &mut self, _conn: &Connection, _qh: &QueueHandle, _output: wl_output::WlOutput, ) { } } impl WindowHandler for SimpleWindow { fn request_close(&mut self, _: &Connection, _: &QueueHandle, _: &Window) { self.exit = true; } fn configure( &mut self, conn: &Connection, qh: &QueueHandle, _window: &Window, configure: WindowConfigure, _serial: u32, ) { println!("Window configured to: {:?}", configure); self.buffer = None; self.width = configure.new_size.0.map(|v| v.get()).unwrap_or(256); self.height = configure.new_size.1.map(|v| v.get()).unwrap_or(256); // Initiate the first draw. if self.first_configure { self.first_configure = false; self.draw(conn, qh); } } } impl ActivationHandler for SimpleWindow { type RequestData = RequestData; fn new_token(&mut self, token: String, _data: &Self::RequestData) { self.xdg_activation .as_ref() .unwrap() .activate::(self.window.wl_surface(), token); } } impl SeatHandler for SimpleWindow { fn seat_state(&mut self) -> &mut SeatState { &mut self.seat_state } fn new_seat(&mut self, _: &Connection, _: &QueueHandle, _: wl_seat::WlSeat) {} fn new_capability( &mut self, _conn: &Connection, qh: &QueueHandle, seat: wl_seat::WlSeat, capability: Capability, ) { if capability == Capability::Keyboard && self.keyboard.is_none() { println!("Set keyboard capability"); let keyboard = self .seat_state .get_keyboard_with_repeat( qh, &seat, None, self.loop_handle.clone(), Box::new(|_state, _wl_kbd, event| { println!("Repeat: {:?} ", event); }), ) .expect("Failed to create keyboard"); self.keyboard = Some(keyboard); } if capability == Capability::Pointer && self.pointer.is_none() { println!("Set pointer capability"); let pointer = self.seat_state.get_pointer(qh, &seat).expect("Failed to create pointer"); self.pointer = Some(pointer); } } fn remove_capability( &mut self, _conn: &Connection, _: &QueueHandle, _: wl_seat::WlSeat, capability: Capability, ) { if capability == Capability::Keyboard && self.keyboard.is_some() { println!("Unset keyboard capability"); self.keyboard.take().unwrap().release(); } if capability == Capability::Pointer && self.pointer.is_some() { println!("Unset pointer capability"); self.pointer.take().unwrap().release(); } } fn remove_seat(&mut self, _: &Connection, _: &QueueHandle, _: wl_seat::WlSeat) {} } impl KeyboardHandler for SimpleWindow { fn enter( &mut self, _: &Connection, _: &QueueHandle, _: &wl_keyboard::WlKeyboard, surface: &wl_surface::WlSurface, _: u32, _: &[u32], keysyms: &[Keysym], ) { if self.window.wl_surface() == surface { println!("Keyboard focus on window with pressed syms: {keysyms:?}"); self.keyboard_focus = true; } } fn leave( &mut self, _: &Connection, _: &QueueHandle, _: &wl_keyboard::WlKeyboard, surface: &wl_surface::WlSurface, _: u32, ) { if self.window.wl_surface() == surface { println!("Release keyboard focus on window"); self.keyboard_focus = false; } } fn press_key( &mut self, _conn: &Connection, _qh: &QueueHandle, _: &wl_keyboard::WlKeyboard, _: u32, event: KeyEvent, ) { println!("Key press: {event:?}"); } fn release_key( &mut self, _: &Connection, _: &QueueHandle, _: &wl_keyboard::WlKeyboard, _: u32, event: KeyEvent, ) { println!("Key release: {event:?}"); } fn update_modifiers( &mut self, _: &Connection, _: &QueueHandle, _: &wl_keyboard::WlKeyboard, _serial: u32, modifiers: Modifiers, _layout: u32, ) { println!("Update modifiers: {modifiers:?}"); } } impl PointerHandler for SimpleWindow { fn pointer_frame( &mut self, _conn: &Connection, _qh: &QueueHandle, _pointer: &wl_pointer::WlPointer, events: &[PointerEvent], ) { use PointerEventKind::*; for event in events { // Ignore events for other surfaces if &event.surface != self.window.wl_surface() { continue; } match event.kind { Enter { .. } => { println!("Pointer entered @{:?}", event.position); } Leave { .. } => { println!("Pointer left"); } Motion { .. } => {} Press { button, .. } => { println!("Press {:x} @ {:?}", button, event.position); self.shift = self.shift.xor(Some(0)); } Release { button, .. } => { println!("Release {:x} @ {:?}", button, event.position); } Axis { horizontal, vertical, .. } => { println!("Scroll H:{horizontal:?}, V:{vertical:?}"); } } } } } impl ShmHandler for SimpleWindow { fn shm_state(&mut self) -> &mut Shm { &mut self.shm } } impl SimpleWindow { pub fn draw(&mut self, _conn: &Connection, qh: &QueueHandle) { let width = self.width; let height = self.height; let stride = self.width as i32 * 4; let buffer = self.buffer.get_or_insert_with(|| { self.pool .create_buffer(width as i32, height as i32, stride, wl_shm::Format::Argb8888) .expect("create buffer") .0 }); let canvas = match self.pool.canvas(buffer) { Some(canvas) => canvas, None => { // This should be rare, but if the compositor has not released the previous // buffer, we need double-buffering. let (second_buffer, canvas) = self .pool .create_buffer( self.width as i32, self.height as i32, stride, wl_shm::Format::Argb8888, ) .expect("create buffer"); *buffer = second_buffer; canvas } }; // Draw to the window: { let shift = self.shift.unwrap_or(0); canvas.chunks_exact_mut(4).enumerate().for_each(|(index, chunk)| { let x = ((index + shift as usize) % width as usize) as u32; let y = (index / width as usize) as u32; let a = 0xFF; let r = u32::min(((width - x) * 0xFF) / width, ((height - y) * 0xFF) / height); let g = u32::min((x * 0xFF) / width, ((height - y) * 0xFF) / height); let b = u32::min(((width - x) * 0xFF) / width, (y * 0xFF) / height); let color = (a << 24) + (r << 16) + (g << 8) + b; let array: &mut [u8; 4] = chunk.try_into().unwrap(); *array = color.to_le_bytes(); }); if let Some(shift) = &mut self.shift { *shift = (*shift + 1) % width; } } // Damage the entire window self.window.wl_surface().damage_buffer(0, 0, self.width as i32, self.height as i32); // Request our next frame self.window.wl_surface().frame(qh, self.window.wl_surface().clone()); // Attach and commit to present. buffer.attach_to(self.window.wl_surface()).expect("buffer attach"); self.window.commit(); } } delegate_compositor!(SimpleWindow); delegate_output!(SimpleWindow); delegate_shm!(SimpleWindow); delegate_seat!(SimpleWindow); delegate_keyboard!(SimpleWindow); delegate_pointer!(SimpleWindow); delegate_xdg_shell!(SimpleWindow); delegate_xdg_window!(SimpleWindow); delegate_activation!(SimpleWindow); delegate_registry!(SimpleWindow); impl ProvidesRegistryState for SimpleWindow { fn registry(&mut self) -> &mut RegistryState { &mut self.registry_state } registry_handlers![OutputState, SeatState,]; }