mod events;
use calloop::RegistrationToken;
use sctk::{
output::{OutputHandler, OutputState},
registry::{ProvidesRegistryState, RegistryState},
seat::SeatState,
};
pub use waycrate_xkbkeycode::keyboard;
pub use waycrate_xkbkeycode::xkb_keyboard;
use waycrate_xkbkeycode::xkb_keyboard::ElementState;
use waycrate_xkbkeycode::xkb_keyboard::RepeatInfo;
mod seat;
mod strtoshape;
pub mod id;
use strtoshape::str_to_shape;
use events::{AxisScroll, DispatchMessageInner};
pub use events::{DispatchMessage, ReturnData, SessionLockEvent};
use wayland_backend::client::ObjectId;
use wayland_client::protocol::wl_callback::WlCallback;
use wayland_client::{
ConnectError, Connection, Dispatch, DispatchError, EventQueue, Proxy, QueueHandle,
delegate_noop,
globals::{BindError, GlobalError, GlobalList, registry_queue_init},
protocol::{
wl_buffer::WlBuffer,
wl_compositor::WlCompositor,
wl_display::WlDisplay,
wl_output::{self, WlOutput},
wl_pointer::WlPointer,
wl_seat::WlSeat,
wl_shm::WlShm,
wl_shm_pool::WlShmPool,
wl_surface::WlSurface,
},
};
use wayland_protocols::ext::session_lock::v1::client::{
ext_session_lock_manager_v1::ExtSessionLockManagerV1,
ext_session_lock_surface_v1::{self, ExtSessionLockSurfaceV1},
ext_session_lock_v1::ExtSessionLockV1,
};
use wayland_protocols::wp::viewporter::client::{
wp_viewport::WpViewport, wp_viewporter::WpViewporter,
};
use wayland_cursor::{CursorImageBuffer, CursorTheme};
use wayland_protocols::wp::cursor_shape::v1::client::{
wp_cursor_shape_device_v1::WpCursorShapeDeviceV1,
wp_cursor_shape_manager_v1::WpCursorShapeManagerV1,
};
use wayland_protocols_misc::zwp_virtual_keyboard_v1::client::{
zwp_virtual_keyboard_manager_v1::ZwpVirtualKeyboardManagerV1,
zwp_virtual_keyboard_v1::ZwpVirtualKeyboardV1,
};
use wayland_protocols::wp::fractional_scale::v1::client::{
wp_fractional_scale_manager_v1::WpFractionalScaleManagerV1,
wp_fractional_scale_v1::{self, WpFractionalScaleV1},
};
use std::collections::HashMap;
use std::time::Duration;
use std::time::Instant;
pub use calloop;
use calloop::{
Error as CallLoopError, EventLoop, LoopHandle,
channel::{self, Channel},
timer::{TimeoutAction, Timer},
};
use calloop_wayland_source::WaylandSource;
use wayland_client::backend::WaylandError;
use crate::seat::SeatStorage;
#[derive(Debug, thiserror::Error)]
pub enum SessionLockEventError {
#[error("connect error")]
ConnectError(#[from] ConnectError),
#[error("Global Error")]
GlobalError(#[from] GlobalError),
#[error("Bind Error")]
BindError(#[from] BindError),
#[error("Error during queue")]
DispatchError(#[from] DispatchError),
#[error("create file failed")]
TempFileCreateFailed(#[from] std::io::Error),
#[error("Event Loop Error")]
EventLoopError(#[from] CallLoopError),
#[error("roundtrip Error")]
RoundTripError(#[from] WaylandError),
}
pub mod reexport {
pub use wayland_protocols_wlr::layer_shell::v1::client::{
zwlr_layer_shell_v1::{Layer, ZwlrLayerShellV1},
zwlr_layer_surface_v1::{Anchor, KeyboardInteractivity},
};
pub mod wl_shm {
pub use wayland_client::protocol::wl_shm::Format;
pub use wayland_client::protocol::wl_shm::WlShm;
}
pub mod zwp_virtual_keyboard_v1 {
pub use wayland_protocols_misc::zwp_virtual_keyboard_v1::client::{
zwp_virtual_keyboard_manager_v1::{self, ZwpVirtualKeyboardManagerV1},
zwp_virtual_keyboard_v1::{self, ZwpVirtualKeyboardV1},
};
}
pub mod wp_fractional_scale_v1 {
pub use wayland_protocols::wp::fractional_scale::v1::client::{
wp_fractional_scale_manager_v1::{self, WpFractionalScaleManagerV1},
wp_fractional_scale_v1::{self, WpFractionalScaleV1},
};
}
pub mod wayland_client {
pub use wayland_client::{
Connection, QueueHandle, WEnum,
globals::GlobalList,
protocol::{
wl_keyboard::{self, KeyState},
wl_pointer::{self, ButtonState},
wl_seat::WlSeat,
},
};
}
pub mod wp_cursor_shape_device_v1 {
pub use crate::strtoshape::ShapeName;
pub use wayland_protocols::wp::cursor_shape::v1::client::wp_cursor_shape_device_v1::Shape;
}
pub mod wp_viewport {
pub use wayland_protocols::wp::viewporter::client::wp_viewport::WpViewport;
}
}
#[derive(Debug)]
pub struct WindowWrapper {
pub id: id::Id,
display: WlDisplay,
wl_surface: WlSurface,
pub viewport: Option<WpViewport>,
}
impl WindowWrapper {
pub fn id(&self) -> id::Id {
self.id
}
}
impl WindowWrapper {
#[inline]
pub fn raw_window_handle_rwh_06(&self) -> Result<rwh_06::RawWindowHandle, rwh_06::HandleError> {
Ok(rwh_06::WaylandWindowHandle::new({
let ptr = self.wl_surface.id().as_ptr();
std::ptr::NonNull::new(ptr as *mut _).expect("wl_surface will never be null")
})
.into())
}
#[inline]
pub fn raw_display_handle_rwh_06(
&self,
) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
Ok(rwh_06::WaylandDisplayHandle::new({
let ptr = self.display.id().as_ptr();
std::ptr::NonNull::new(ptr as *mut _).expect("wl_proxy should never be null")
})
.into())
}
}
impl rwh_06::HasWindowHandle for WindowWrapper {
fn window_handle(&self) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> {
let raw = self.raw_window_handle_rwh_06()?;
Ok(unsafe { rwh_06::WindowHandle::borrow_raw(raw) })
}
}
impl rwh_06::HasDisplayHandle for WindowWrapper {
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
let raw = self.raw_display_handle_rwh_06()?;
Ok(unsafe { rwh_06::DisplayHandle::borrow_raw(raw) })
}
}
#[derive(Debug)]
pub struct WindowStateUnit<T> {
id: id::Id,
display: WlDisplay,
wl_output: wl_output::WlOutput,
wl_surface: WlSurface,
size: (u32, u32),
buffer: Option<WlBuffer>,
session_shell: ExtSessionLockSurfaceV1,
fractional_scale: Option<WpFractionalScaleV1>,
viewport: Option<WpViewport>,
binding: Option<T>,
qh: QueueHandle<WindowState<T>>,
scale: u32,
present_available_state: PresentAvailableState,
refresh: RefreshRequest,
}
impl<T> WindowStateUnit<T> {
pub fn id(&self) -> id::Id {
self.id
}
pub fn try_set_viewport_destination(&self, width: i32, height: i32) -> Option<()> {
let viewport = self.viewport.as_ref()?;
viewport.set_destination(width, height);
Some(())
}
pub fn try_set_viewport_source(&self, x: f64, y: f64, width: f64, height: f64) -> Option<()> {
let viewport = self.viewport.as_ref()?;
viewport.set_source(x, y, width, height);
Some(())
}
pub fn gen_wrapper(&self) -> WindowWrapper {
WindowWrapper {
id: self.id,
display: self.display.clone(),
wl_surface: self.wl_surface.clone(),
viewport: self.viewport.clone(),
}
}
}
impl<T> WindowStateUnit<T> {
#[inline]
pub fn raw_window_handle_rwh_06(&self) -> Result<rwh_06::RawWindowHandle, rwh_06::HandleError> {
Ok(rwh_06::WaylandWindowHandle::new({
let ptr = self.wl_surface.id().as_ptr();
std::ptr::NonNull::new(ptr as *mut _).expect("wl_surface will never be null")
})
.into())
}
#[inline]
pub fn raw_display_handle_rwh_06(
&self,
) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
Ok(rwh_06::WaylandDisplayHandle::new({
let ptr = self.display.id().as_ptr();
std::ptr::NonNull::new(ptr as *mut _).expect("wl_proxy should never be null")
})
.into())
}
}
#[derive(Debug, Clone)]
pub struct DisplayWrapper {
display: WlDisplay,
}
impl DisplayWrapper {
#[inline]
pub fn raw_display_handle_rwh_06(
&self,
) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
Ok(rwh_06::WaylandDisplayHandle::new({
let ptr = self.display.id().as_ptr();
std::ptr::NonNull::new(ptr as *mut _).expect("wl_proxy should never be null")
})
.into())
}
}
impl rwh_06::HasDisplayHandle for DisplayWrapper {
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
let raw = self.raw_display_handle_rwh_06()?;
Ok(unsafe { rwh_06::DisplayHandle::borrow_raw(raw) })
}
}
impl<T> WindowStateUnit<T> {
pub fn get_wlsurface(&self) -> &WlSurface {
&self.wl_surface
}
pub fn set_binding(&mut self, binding: T) {
self.binding = Some(binding);
}
pub fn get_binding_mut(&mut self) -> Option<&mut T> {
self.binding.as_mut()
}
pub fn get_binding(&self) -> Option<&T> {
self.binding.as_ref()
}
pub fn get_size(&self) -> (u32, u32) {
self.size
}
pub fn scale_u32(&self) -> u32 {
self.scale
}
pub fn scale_float(&self) -> f64 {
self.scale as f64 / 120.
}
pub fn request_refresh(&mut self, request: RefreshRequest) {
match self.refresh {
RefreshRequest::NextFrame => {}
RefreshRequest::At(instant) => match request {
RefreshRequest::NextFrame => self.refresh = request,
RefreshRequest::At(other_instant) => {
if other_instant < instant {
self.refresh = request;
}
}
RefreshRequest::Wait => {}
},
RefreshRequest::Wait => self.refresh = request,
}
}
fn should_refresh(&self) -> bool {
match self.refresh {
RefreshRequest::NextFrame => true,
RefreshRequest::At(instant) => instant <= Instant::now(),
RefreshRequest::Wait => false,
}
}
fn refresh_timeout(&self) -> Option<Duration> {
match self.refresh {
RefreshRequest::NextFrame => {
if self.present_available_state == PresentAvailableState::Available {
Some(Duration::ZERO)
} else {
None
}
}
RefreshRequest::At(instant) => {
let timeout = instant.saturating_duration_since(Instant::now());
if timeout.is_zero()
&& self.present_available_state != PresentAvailableState::Available
{
None
} else {
Some(timeout)
}
}
RefreshRequest::Wait => None,
}
}
pub fn take_present_slot(&mut self) -> bool {
if !self.should_refresh() {
return false;
}
if self.present_available_state != PresentAvailableState::Available {
return false;
}
self.refresh = RefreshRequest::Wait;
self.present_available_state = PresentAvailableState::Taken;
true
}
pub fn reset_present_slot(&mut self) -> bool {
if self.present_available_state == PresentAvailableState::Taken {
self.present_available_state = PresentAvailableState::Available;
true
} else {
false
}
}
}
impl<T: 'static> WindowStateUnit<T> {
pub fn request_next_present(&mut self) {
match self.present_available_state {
PresentAvailableState::Taken => {
self.present_available_state = PresentAvailableState::Requested;
self.wl_surface
.frame(&self.qh, (self.id, PresentAvailableState::Available));
}
PresentAvailableState::Requested | PresentAvailableState::Available => {}
}
}
}
#[derive(Debug)]
struct KeyboardTokenState {
delay: Duration,
key: u32,
surface_id: Option<id::Id>,
pressed_state: ElementState,
object_id: ObjectId,
}
#[derive(Debug)]
pub struct WindowState<T> {
outputs: Vec<wl_output::WlOutput>,
current_surface: Option<WlSurface>,
active_surfaces: HashMap<Option<i32>, (WlSurface, Option<id::Id>)>,
units: Vec<WindowStateUnit<T>>,
message: Vec<(Option<id::Id>, DispatchMessageInner)>,
display: Option<WlDisplay>,
connection: Option<Connection>,
event_queue: Option<EventQueue<WindowState<T>>>,
wl_compositor: Option<WlCompositor>,
shm: Option<WlShm>,
cursor_manager: Option<WpCursorShapeManagerV1>,
viewporter: Option<WpViewporter>,
lock: Option<ExtSessionLockV1>,
fractional_scale_manager: Option<WpFractionalScaleManagerV1>,
globals: Option<GlobalList>,
registry_state: Option<RegistryState>,
output_state: Option<OutputState>,
seat_state: Option<SeatState>,
seats: HashMap<ObjectId, SeatStorage>,
seat_back: Option<WlSeat>,
to_remove_tokens: Vec<RegistrationToken>,
repeat_delay: Option<KeyboardTokenState>,
closed_ids: Vec<id::Id>,
use_display_handle: bool,
finger_locations: HashMap<i32, (f64, f64)>,
enter_serial: Option<u32>,
return_data: Vec<ReturnData>,
}
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum RefreshRequest {
NextFrame,
At(Instant),
#[default]
Wait,
}
impl<T> WindowState<T> {
pub fn gen_main_wrapper(&self) -> WindowWrapper {
self.main_window().gen_wrapper()
}
pub fn main_window(&self) -> &WindowStateUnit<T> {
&self.units[0]
}
pub fn get_window_with_id(&self, id: id::Id) -> Option<&WindowStateUnit<T>> {
self.units.iter().find(|w| w.id() == id)
}
pub fn windows(&self) -> &Vec<WindowStateUnit<T>> {
&self.units
}
fn push_window(&mut self, window_state_unit: WindowStateUnit<T>) {
let surface = window_state_unit.wl_surface.clone();
self.units.push(window_state_unit);
self.update_current_surface(Some(surface));
}
}
impl<T> WindowState<T> {
pub fn new() -> Self {
Self::default()
}
fn min_dispatch_timeout(&self) -> Option<Duration> {
let mut min: Option<Duration> = None;
for unit in &self.units {
match unit.refresh_timeout() {
Some(Duration::ZERO) => return Some(Duration::ZERO),
Some(d) => min = Some(min.map_or(d, |m: Duration| m.min(d))),
None => {}
}
}
min
}
pub fn append_return_data(&mut self, data: ReturnData) {
self.return_data.push(data);
}
pub fn handle_event<F, Message>(
&mut self,
mut event_handler: F,
event: SessionLockEvent<T, Message>,
unit_id: Option<id::Id>,
) where
Message: std::marker::Send + 'static,
F: FnMut(SessionLockEvent<T, Message>, &mut WindowState<T>, Option<id::Id>) -> ReturnData,
{
let return_data = event_handler(event, self, unit_id);
if !matches!(return_data, ReturnData::None) {
self.append_return_data(return_data);
}
}
}
impl<T> Default for WindowState<T> {
fn default() -> Self {
Self {
outputs: Vec::new(),
current_surface: None,
active_surfaces: HashMap::new(),
units: Vec::new(),
message: Vec::new(),
display: None,
connection: None,
event_queue: None,
wl_compositor: None,
shm: None,
cursor_manager: None,
viewporter: None,
fractional_scale_manager: None,
lock: None,
globals: None,
registry_state: None,
output_state: None,
seat_state: None,
seat_back: None,
seats: HashMap::new(),
to_remove_tokens: Vec::new(),
repeat_delay: None,
closed_ids: Vec::new(),
use_display_handle: false,
finger_locations: HashMap::new(),
enter_serial: None,
return_data: Vec::new(),
}
}
}
impl<T> WindowState<T> {
pub fn get_id_list(&self) -> Vec<id::Id> {
self.units.iter().map(|unit| unit.id).collect()
}
pub fn get_unit_iter(&self) -> impl Iterator<Item = &WindowStateUnit<T>> {
self.units.iter()
}
pub fn get_unit_iter_mut(&mut self) -> impl Iterator<Item = &mut WindowStateUnit<T>> {
self.units.iter_mut()
}
pub fn get_mut_unit_with_id(&mut self, id: id::Id) -> Option<&mut WindowStateUnit<T>> {
self.units.iter_mut().find(|unit| unit.id == id)
}
pub fn get_unit_with_id(&self, id: id::Id) -> Option<&WindowStateUnit<T>> {
self.units.iter().find(|unit| unit.id == id)
}
fn current_surface_id(&self) -> Option<id::Id> {
self.units
.iter()
.find(|unit| Some(&unit.wl_surface) == self.current_surface.as_ref())
.map(|unit| unit.id())
}
pub fn with_use_display_handle(mut self, use_display_handle: bool) -> Self {
self.use_display_handle = use_display_handle;
self
}
pub fn with_connection(mut self, connection_or: Option<Connection>) -> Self {
self.connection = connection_or;
self
}
fn get_id_from_surface(&self, surface: &WlSurface) -> Option<id::Id> {
self.units
.iter()
.find(|unit| &unit.wl_surface == surface)
.map(|unit| unit.id())
}
pub fn is_mouse_surface(&self, surface_id: id::Id) -> bool {
self.active_surfaces
.get(&None)
.filter(|(_, id)| *id == Some(surface_id))
.is_some()
}
fn update_current_surface(&mut self, surface: Option<WlSurface>) {
if surface == self.current_surface {
return;
}
if let Some(surface) = surface {
self.current_surface = Some(surface);
for keyboard_state in self.get_keyboard_state_iter_mut() {
keyboard_state.current_repeat = None;
}
}
}
}
impl<T: 'static> WindowState<T> {
pub fn display_wrapper(&self) -> DisplayWrapper {
DisplayWrapper {
display: self.display.clone().expect("You should it after build"),
}
}
pub fn request_next_present(&mut self, id: id::Id) {
self.get_mut_unit_with_id(id)
.map(WindowStateUnit::request_next_present);
}
pub fn reset_present_slot(&mut self, id: id::Id) {
self.get_mut_unit_with_id(id)
.map(WindowStateUnit::reset_present_slot);
}
pub fn request_refresh_all(&mut self, request: RefreshRequest) {
self.units
.iter_mut()
.for_each(|unit| unit.request_refresh(request));
}
pub fn request_refresh(&mut self, id: id::Id, request: RefreshRequest) {
if let Some(unit) = self.get_mut_unit_with_id(id) {
unit.request_refresh(request);
}
}
}
impl<T: 'static> ProvidesRegistryState for WindowState<T> {
fn registry(&mut self) -> &mut RegistryState {
self.registry_state.as_mut().unwrap()
}
sctk::registry_handlers![SeatState, OutputState];
}
impl<T: 'static> OutputHandler for WindowState<T> {
fn output_state(&mut self) -> &mut OutputState {
self.output_state.as_mut().unwrap()
}
fn new_output(
&mut self,
_conn: &Connection,
_qh: &QueueHandle<Self>,
output: wl_output::WlOutput,
) {
self.outputs.push(output.clone());
self.message
.push((None, DispatchMessageInner::NewDisplay(output)));
}
fn update_output(
&mut self,
_conn: &Connection,
_qh: &QueueHandle<Self>,
_output: wl_output::WlOutput,
) {
}
fn output_destroyed(
&mut self,
_conn: &Connection,
_qh: &QueueHandle<Self>,
output: wl_output::WlOutput,
) {
self.outputs.retain(|x| x != &output);
let removed_states = self.units.extract_if(.., |unit| {
!unit.wl_surface.is_alive()
&& !self
.outputs
.iter()
.any(|storage| storage == &unit.wl_output)
});
for deleled in removed_states.into_iter() {
self.closed_ids.push(deleled.id);
}
}
}
impl<T> Dispatch<ext_session_lock_surface_v1::ExtSessionLockSurfaceV1, ()> for WindowState<T> {
fn event(
state: &mut Self,
surface: &ext_session_lock_surface_v1::ExtSessionLockSurfaceV1,
event: <ext_session_lock_surface_v1::ExtSessionLockSurfaceV1 as Proxy>::Event,
_data: &(),
_conn: &Connection,
_qhandle: &QueueHandle<Self>,
) {
if let ext_session_lock_surface_v1::Event::Configure {
serial,
width,
height,
} = event
{
surface.ack_configure(serial);
let Some(unit_index) = state
.units
.iter()
.position(|unit| unit.session_shell == *surface)
else {
return;
};
state.units[unit_index].size = (width, height);
state.units[unit_index].request_refresh(RefreshRequest::NextFrame);
}
}
}
impl<T> Dispatch<wp_fractional_scale_v1::WpFractionalScaleV1, ()> for WindowState<T> {
fn event(
state: &mut Self,
proxy: &wp_fractional_scale_v1::WpFractionalScaleV1,
event: <wp_fractional_scale_v1::WpFractionalScaleV1 as Proxy>::Event,
_data: &(),
_conn: &Connection,
_qhandle: &QueueHandle<Self>,
) {
if let wp_fractional_scale_v1::Event::PreferredScale { scale } = event {
let Some(unit) = state.units.iter_mut().find(|info| {
info.fractional_scale
.as_ref()
.is_some_and(|fractional_scale| fractional_scale == proxy)
}) else {
return;
};
unit.scale = scale;
unit.request_refresh(RefreshRequest::NextFrame);
state.message.push((
Some(unit.id),
DispatchMessageInner::PreferredScale {
scale_float: scale as f64 / 120.,
scale_u32: scale,
},
));
}
}
}
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
enum PresentAvailableState {
Requested,
#[default]
Available,
Taken,
}
impl<T> Dispatch<WlCallback, (id::Id, PresentAvailableState)> for WindowState<T> {
fn event(
state: &mut Self,
_proxy: &WlCallback,
event: <WlCallback as Proxy>::Event,
data: &(id::Id, PresentAvailableState),
_conn: &Connection,
_qhandle: &QueueHandle<Self>,
) {
use wayland_client::protocol::wl_callback::Event as WlCallbackEvent;
if let WlCallbackEvent::Done { callback_data: _ } = event
&& let Some(unit) = state.get_mut_unit_with_id(data.0)
{
unit.present_available_state = data.1;
}
}
}
delegate_noop!(@<T>WindowState<T>: ignore WlCompositor); delegate_noop!(@<T>WindowState<T>: ignore WlSurface); delegate_noop!(@<T>WindowState<T>: ignore WlOutput); delegate_noop!(@<T>WindowState<T>: ignore WlShm); delegate_noop!(@<T>WindowState<T>: ignore WlShmPool); delegate_noop!(@<T>WindowState<T>: ignore WlBuffer);
delegate_noop!(@<T>WindowState<T>: ignore ExtSessionLockV1); delegate_noop!(@<T>WindowState<T>: ignore ExtSessionLockManagerV1);
delegate_noop!(@<T>WindowState<T>: ignore WpCursorShapeManagerV1);
delegate_noop!(@<T>WindowState<T>: ignore WpCursorShapeDeviceV1);
delegate_noop!(@<T> WindowState<T>: ignore WpViewporter);
delegate_noop!(@<T> WindowState<T>: ignore WpViewport);
delegate_noop!(@<T>WindowState<T>: ignore ZwpVirtualKeyboardV1);
delegate_noop!(@<T>WindowState<T>: ignore ZwpVirtualKeyboardManagerV1);
delegate_noop!(@<T>WindowState<T>: ignore WpFractionalScaleManagerV1);
sctk::delegate_registry!(@<T: 'static> WindowState<T>);
sctk::delegate_output!(@<T: 'static> WindowState<T>);
impl<T: 'static> WindowState<T> {
pub fn build(mut self) -> Result<Self, SessionLockEventError> {
let connection = if let Some(connection) = self.connection.take() {
connection
} else {
Connection::connect_to_env()?
};
let (globals, mut event_queue) = registry_queue_init::<Self>(&connection)?;
self.display = Some(connection.display());
let qh = event_queue.handle();
self.registry_state = Some(RegistryState::new(&globals));
self.output_state = Some(OutputState::new(&globals, &qh));
let seat_state = SeatState::new(&globals, &qh);
for seat in seat_state.seats() {
self.seats.insert(seat.id(), SeatStorage::new());
}
self.seat_state = Some(seat_state);
let wmcompositer = globals.bind::<WlCompositor, _, _>(&qh, 1..=5, ())?;
let shm = globals.bind::<WlShm, _, _>(&qh, 1..=1, ())?;
self.shm = Some(shm);
self.seat_back = Some(globals.bind::<WlSeat, _, _>(&qh, 1..=1, ())?);
let cursor_manager = globals
.bind::<WpCursorShapeManagerV1, _, _>(&qh, 1..=1, ())
.ok();
let viewporter = globals.bind::<WpViewporter, _, _>(&qh, 1..=1, ()).ok();
let fractional_scale_manager = globals
.bind::<WpFractionalScaleManagerV1, _, _>(&qh, 1..=1, ())
.ok();
let lock_manager = globals.bind::<ExtSessionLockManagerV1, _, _>(&qh, 1..=1, ())?;
event_queue.blocking_dispatch(&mut self)?; let lock = lock_manager.lock(&qh, ());
let wl_outputs = self.outputs.clone();
for wl_output in wl_outputs.iter() {
let wl_surface = wmcompositer.create_surface(&qh, ()); wl_surface.commit();
let session_lock_surface = lock.get_lock_surface(&wl_surface, wl_output, &qh, ());
let mut fractional_scale = None;
if let Some(ref fractional_scale_manager) = fractional_scale_manager {
fractional_scale =
Some(fractional_scale_manager.get_fractional_scale(&wl_surface, &qh, ()));
}
let viewport = viewporter
.as_ref()
.map(|viewport| viewport.get_viewport(&wl_surface, &qh, ()));
self.push_window(WindowStateUnit {
id: id::Id::unique(),
display: connection.display(),
wl_surface,
wl_output: wl_output.clone(),
size: (0, 0),
buffer: None,
session_shell: session_lock_surface,
viewport,
fractional_scale,
binding: None,
scale: 120,
present_available_state: PresentAvailableState::Available,
refresh: RefreshRequest::Wait,
qh: qh.clone(),
});
}
self.viewporter = viewporter;
self.connection = Some(connection);
self.event_queue = Some(event_queue);
self.wl_compositor = Some(wmcompositer);
self.cursor_manager = cursor_manager;
self.lock = Some(lock);
self.fractional_scale_manager = fractional_scale_manager;
self.globals = Some(globals);
Ok(self)
}
pub fn running_with_proxy<F, Message>(
self,
message_receiver: Channel<Message>,
event_handler: F,
) -> Result<(), SessionLockEventError>
where
Message: std::marker::Send + 'static,
F: FnMut(SessionLockEvent<T, Message>, &mut WindowState<T>, Option<id::Id>) -> ReturnData
+ 'static,
{
self.running_with_proxy_option(Some(message_receiver), event_handler)
}
pub fn running<F>(self, event_handler: F) -> Result<(), SessionLockEventError>
where
F: FnMut(SessionLockEvent<T, ()>, &mut WindowState<T>, Option<id::Id>) -> ReturnData
+ 'static,
{
self.running_with_proxy_option(None, event_handler)
}
fn running_with_proxy_option<F, Message>(
mut self,
mut message_receiver: Option<Channel<Message>>,
mut event_handler: F,
) -> Result<(), SessionLockEventError>
where
Message: std::marker::Send + 'static,
F: FnMut(SessionLockEvent<T, Message>, &mut WindowState<T>, Option<id::Id>) -> ReturnData
+ 'static,
{
let globals = self.globals.take().unwrap();
let mut event_queue_origin = self.event_queue.take().unwrap();
let qh = event_queue_origin.handle();
let wmcompositer = self.wl_compositor.take().unwrap();
let shm = self.shm.take().unwrap();
let fractional_scale_manager = self.fractional_scale_manager.take();
let cursor_manager: Option<WpCursorShapeManagerV1> = self.cursor_manager.take();
let viewporter = self.viewporter.take();
let connection = self.connection.take().unwrap();
let lock = self.lock.take().unwrap();
let mut init_event = None;
let cursor_update_context = CursorUpdateContext {
cursor_manager,
qh: qh.clone(),
connection: connection.clone(),
shm: shm.clone(),
wmcompositer: wmcompositer.clone(),
};
while !matches!(init_event, Some(ReturnData::None)) {
match init_event {
None => {
init_event = Some(event_handler(
SessionLockEvent::InitRequest,
&mut self,
None,
));
}
Some(ReturnData::RequestBind) => {
init_event = Some(event_handler(
SessionLockEvent::BindProvide(&globals, &qh),
&mut self,
None,
));
}
_ => panic!("Not provide server here"),
}
}
self.message.clear();
struct EventWrapper<Raw, F> {
raw: Raw,
fun: F,
loop_handle: LoopHandle<'static, Self>,
}
let mut event_loop: EventLoop<_> =
EventLoop::try_new().expect("Failed to initialize the event loop");
let event_queue = connection.new_event_queue::<EventWrapper<Self, F>>();
WaylandSource::new(connection.clone(), event_queue)
.insert(event_loop.handle())
.expect("Failed to init Wayland Source");
let mut state = EventWrapper {
raw: self,
fun: event_handler,
loop_handle: event_loop.handle(),
};
let signal = event_loop.get_signal();
let process_window_state = |window_state: &mut WindowState<T>, event_handler: &mut F| {
let mut messages = Vec::new();
std::mem::swap(&mut messages, &mut window_state.message);
for msg in messages.iter() {
match msg {
(_, DispatchMessageInner::NewDisplay(wl_output)) => {
let wl_surface = wmcompositer.create_surface(&qh, ());
wl_surface.commit();
let session_lock_surface =
lock.get_lock_surface(&wl_surface, wl_output, &qh, ());
let mut fractional_scale = None;
if let Some(ref fractional_scale_manager) = fractional_scale_manager {
fractional_scale = Some(fractional_scale_manager.get_fractional_scale(
&wl_surface,
&qh,
(),
));
}
let viewport = viewporter
.as_ref()
.map(|viewport| viewport.get_viewport(&wl_surface, &qh, ()));
window_state.push_window(WindowStateUnit {
id: id::Id::unique(),
display: connection.display(),
wl_output: wl_output.clone(),
wl_surface,
size: (0, 0),
buffer: None,
session_shell: session_lock_surface,
viewport,
fractional_scale,
binding: None,
scale: 120,
present_available_state: PresentAvailableState::Available,
refresh: RefreshRequest::Wait,
qh: qh.clone(),
});
}
_ => {
let (index_message, msg) = msg;
let msg: DispatchMessage = msg.clone().into();
match event_handler(
SessionLockEvent::RequestMessages(&msg),
window_state,
*index_message,
) {
ReturnData::RequestUnlockAndExist => {
lock.unlock_and_destroy();
connection
.roundtrip()
.expect("should roundtrip successfully");
signal.stop();
return true;
}
ReturnData::RequestSetCursorShape((shape_name, pointer)) => {
let Some(serial) = window_state.enter_serial else {
continue;
};
set_cursor_shape(
&cursor_update_context,
shape_name,
pointer,
serial,
);
}
_ => {}
}
}
}
}
window_state.handle_event(&mut *event_handler, SessionLockEvent::NormalDispatch, None);
loop {
let mut return_data = vec![];
std::mem::swap(&mut window_state.return_data, &mut return_data);
for data in return_data {
match data {
ReturnData::RequestUnlockAndExist => {
lock.unlock_and_destroy();
connection.roundtrip().expect("should go final roundtrip");
signal.stop();
return true;
}
ReturnData::RequestSetCursorShape((shape_name, pointer)) => {
let Some(serial) = window_state.enter_serial else {
continue;
};
set_cursor_shape(&cursor_update_context, shape_name, pointer, serial);
}
_ => {}
}
}
window_state.return_data.retain(|x| *x != ReturnData::None);
if window_state.return_data.is_empty() {
break;
}
}
let closed_ids = window_state.closed_ids.clone();
for id in closed_ids {
window_state.handle_event(
&mut *event_handler,
SessionLockEvent::RequestMessages(&DispatchMessage::Closed),
Some(id),
);
}
window_state.closed_ids.clear();
for idx in 0..window_state.units.len() {
let unit = &mut window_state.units[idx];
let (width, height) = unit.size;
if width == 0 || height == 0 {
continue;
}
if unit.take_present_slot() {
let unit_id = unit.id;
let scale_float = unit.scale_float();
let wl_surface = unit.wl_surface.clone();
if unit.buffer.is_none() && !window_state.use_display_handle {
let Ok(mut file) = tempfile::tempfile() else {
log::error!("Cannot create new file from tempfile");
return false;
};
let ReturnData::WlBuffer(buffer) = event_handler(
SessionLockEvent::RequestBuffer(&mut file, &shm, &qh, width, height),
window_state,
Some(unit_id),
) else {
panic!("You cannot return this one");
};
wl_surface.attach(Some(&buffer), 0, 0);
wl_surface.commit();
window_state.units[idx].buffer = Some(buffer);
}
window_state.handle_event(
&mut *event_handler,
SessionLockEvent::RequestMessages(&DispatchMessage::RequestRefresh {
width,
height,
scale_float,
}),
Some(unit_id),
);
window_state.units[idx].reset_present_slot();
}
}
false
};
let mut force_first_tick = state.raw.units.is_empty();
loop {
let timeout = if force_first_tick {
Some(Duration::ZERO)
} else {
state.raw.min_dispatch_timeout()
};
event_loop.dispatch(timeout, &mut state)?;
force_first_tick = false;
let r_window_state = &mut state;
let window_state = &mut r_window_state.raw;
event_queue_origin.dispatch_pending(window_state)?;
let event_handler = &mut r_window_state.fun;
if process_window_state(window_state, event_handler) {
break;
}
let looph = &r_window_state.loop_handle;
for token in window_state.to_remove_tokens.iter() {
looph.remove(*token);
}
window_state.to_remove_tokens.clear();
if let Some(KeyboardTokenState {
key,
delay,
surface_id,
pressed_state,
object_id,
}) = window_state.repeat_delay.take()
{
let timer = Timer::from_duration(delay);
if let Some(keyboard_state) =
window_state.get_keyboard_state_by_id(object_id.clone())
{
keyboard_state.repeat_token = looph
.insert_source(timer, move |_, _, r_window_state| {
let state = &mut r_window_state.raw;
let event_handler = &mut r_window_state.fun;
let keyboard_state = match state
.seats
.values_mut()
.find(|seat| {
seat.keyboard_state
.as_ref()
.is_some_and(|state| state.keyboard.id() == object_id)
})
.map(|storage| storage.keyboard_state.as_mut().unwrap())
{
Some(keyboard_state) => keyboard_state,
None => return TimeoutAction::Drop,
};
let repeat_keycode = match keyboard_state.current_repeat {
Some(repeat_keycode) => repeat_keycode,
None => return TimeoutAction::Drop,
};
if repeat_keycode != key {
return TimeoutAction::Drop;
}
if let Some(mut key_context) = keyboard_state.xkb_context.key_context()
{
let event = key_context.process_key_event(
repeat_keycode,
pressed_state,
false,
);
let event = DispatchMessageInner::KeyboardInput {
event,
is_synthetic: false,
};
state.message.push((surface_id, event));
}
let repeat_info = keyboard_state.repeat_info;
let _ = keyboard_state;
state.handle_event(
&mut *event_handler,
SessionLockEvent::NormalDispatch,
None,
);
match repeat_info {
RepeatInfo::Repeat { gap, .. } => TimeoutAction::ToDuration(gap),
RepeatInfo::Disable => TimeoutAction::Drop,
}
})
.ok();
}
}
let _ = connection.flush();
if let Some(channel) = message_receiver.take() {
event_loop
.handle()
.insert_source(channel, |event, _, r_window_state| {
let channel::Event::Msg(event) = event else {
return;
};
let window_state = &mut r_window_state.raw;
let event_handler = &mut r_window_state.fun;
window_state.handle_event(
&mut *event_handler,
SessionLockEvent::UserEvent(event),
None,
);
})
.expect("We need message state");
}
}
Ok(())
}
}
fn get_cursor_buffer(
shape: &str,
connection: &Connection,
shm: &WlShm,
) -> Option<CursorImageBuffer> {
let mut cursor_theme = CursorTheme::load(connection, shm.clone(), 23).ok()?;
let cursor = cursor_theme.get_cursor(shape);
Some(cursor?[0].clone())
}
struct CursorUpdateContext<T: 'static> {
cursor_manager: Option<WpCursorShapeManagerV1>,
qh: QueueHandle<WindowState<T>>,
connection: Connection,
shm: WlShm,
wmcompositer: WlCompositor,
}
fn set_cursor_shape<T: 'static>(
context: &CursorUpdateContext<T>,
shape_name: String,
pointer: WlPointer,
serial: u32,
) {
if let Some(cursor_manager) = &context.cursor_manager {
let Some(shape) = str_to_shape(&shape_name) else {
log::error!("Not supported shape");
return;
};
let device = cursor_manager.get_pointer(&pointer, &context.qh, ());
device.set_shape(serial, shape);
device.destroy();
} else {
let Some(cursor_buffer) = get_cursor_buffer(&shape_name, &context.connection, &context.shm)
else {
log::error!("Cannot find cursor {shape_name}");
return;
};
let cursor_surface = context.wmcompositer.create_surface(&context.qh, ());
cursor_surface.attach(Some(&cursor_buffer), 0, 0);
let (hotspot_x, hotspot_y) = cursor_buffer.hotspot();
pointer.set_cursor(
serial,
Some(&cursor_surface),
hotspot_x as i32,
hotspot_y as i32,
);
cursor_surface.commit();
}
}