🔒 Repository is read-only – file editing is disabled.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
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<Window>,
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<Smallvil>,
pub data_device_state: DataDeviceState,
pub popups: PopupManager,
pub seat: Seat<Self>,
}
impl Smallvil {
pub fn new(event_loop: &mut EventLoop<CalloopData>, display: Display<Self>) -> Self {
let start_time = std::time::Instant::now();
let dh = display.handle();
let compositor_state = CompositorState::new::<Self>(&dh);
let xdg_shell_state = XdgShellState::new::<Self>(&dh);
let shm_state = ShmState::new::<Self>(&dh, vec![]);
let output_manager_state = OutputManagerState::new_with_xdg_output::<Self>(&dh);
let mut seat_state = SeatState::new();
let data_device_state = DataDeviceState::new::<Self>(&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<Self> = 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<Smallvil>,
event_loop: &mut EventLoop<CalloopData>,
) -> 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<f64, Logical>) -> Option<(WlSurface, Point<f64, Logical>)> {
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) {}
}