use std::{ffi::OsString, sync::Arc}; use smithay::{ desktop::{PopupManager, Space, Window, WindowSurfaceType}, input::{Seat, SeatState}, reexports::{ calloop::{generic::Generic, EventLoop, Interest, LoopSignal, Mode, PostAction}, wayland_server::{ backend::{ClientData, ClientId, DisconnectReason}, protocol::wl_surface::WlSurface, Display, DisplayHandle, }, }, utils::{Logical, Point}, wayland::{ compositor::{CompositorClientState, CompositorState}, output::OutputManagerState, selection::data_device::DataDeviceState, shell::xdg::XdgShellState, shm::ShmState, socket::ListeningSocketSource, }, }; use crate::CalloopData; pub struct Smallvil { pub start_time: std::time::Instant, pub socket_name: OsString, pub display_handle: DisplayHandle, pub space: Space, pub loop_signal: LoopSignal, // Smithay State pub compositor_state: CompositorState, pub xdg_shell_state: XdgShellState, pub shm_state: ShmState, pub output_manager_state: OutputManagerState, pub seat_state: SeatState, pub data_device_state: DataDeviceState, pub popups: PopupManager, pub seat: Seat, } impl Smallvil { pub fn new(event_loop: &mut EventLoop, display: Display) -> Self { let start_time = std::time::Instant::now(); let dh = display.handle(); let compositor_state = CompositorState::new::(&dh); let xdg_shell_state = XdgShellState::new::(&dh); let shm_state = ShmState::new::(&dh, vec![]); let output_manager_state = OutputManagerState::new_with_xdg_output::(&dh); let mut seat_state = SeatState::new(); let data_device_state = DataDeviceState::new::(&dh); let popups = PopupManager::default(); // A seat is a group of keyboards, pointer and touch devices. // A seat typically has a pointer and maintains a keyboard focus and a pointer focus. let mut seat: Seat = seat_state.new_wl_seat(&dh, "winit"); // Notify clients that we have a keyboard, for the sake of the example we assume that keyboard is always present. // You may want to track keyboard hot-plug in real compositor. seat.add_keyboard(Default::default(), 200, 25).unwrap(); // Notify clients that we have a pointer (mouse) // Here we assume that there is always pointer plugged in seat.add_pointer(); // A space represents a two-dimensional plane. Windows and Outputs can be mapped onto it. // // Windows get a position and stacking order through mapping. // Outputs become views of a part of the Space and can be rendered via Space::render_output. let space = Space::default(); let socket_name = Self::init_wayland_listener(display, event_loop); // Get the loop signal, used to stop the event loop let loop_signal = event_loop.get_signal(); Self { start_time, display_handle: dh, space, loop_signal, socket_name, compositor_state, xdg_shell_state, shm_state, output_manager_state, seat_state, data_device_state, popups, seat, } } fn init_wayland_listener( display: Display, event_loop: &mut EventLoop, ) -> OsString { // Creates a new listening socket, automatically choosing the next available `wayland` socket name. let listening_socket = ListeningSocketSource::new_auto().unwrap(); // Get the name of the listening socket. // Clients will connect to this socket. let socket_name = listening_socket.socket_name().to_os_string(); let loop_handle = event_loop.handle(); loop_handle .insert_source(listening_socket, move |client_stream, _, state| { // Inside the callback, you should insert the client into the display. // // You may also associate some data with the client when inserting the client. state .display_handle .insert_client(client_stream, Arc::new(ClientState::default())) .unwrap(); }) .expect("Failed to init the wayland event source."); // You also need to add the display itself to the event loop, so that client events will be processed by wayland-server. loop_handle .insert_source( Generic::new(display, Interest::READ, Mode::Level), |_, display, state| { // Safety: we don't drop the display unsafe { display.get_mut().dispatch_clients(&mut state.state).unwrap(); } Ok(PostAction::Continue) }, ) .unwrap(); socket_name } pub fn surface_under(&self, pos: Point) -> Option<(WlSurface, Point)> { self.space.element_under(pos).and_then(|(window, location)| { window .surface_under(pos - location.to_f64(), WindowSurfaceType::ALL) .map(|(s, p)| (s, (p + location).to_f64())) }) } } #[derive(Default)] pub struct ClientState { pub compositor_state: CompositorClientState, } impl ClientData for ClientState { fn initialized(&self, _client_id: ClientId) {} fn disconnected(&self, _client_id: ClientId, _reason: DisconnectReason) {} }