use super::ObjectEvent; use hecs::{Entity, World}; use smithay_client_toolkit::{ activation::{ActivationHandler, RequestData, RequestDataExt}, data_device_manager::{ data_device::{DataDeviceData, DataDeviceHandler}, data_offer::{DataOfferHandler, SelectionOffer}, data_source::DataSourceHandler, }, delegate_activation, delegate_data_device, }; use std::sync::{mpsc, Mutex, OnceLock}; use wayland_client::protocol::{ wl_buffer::WlBuffer, wl_callback::WlCallback, wl_compositor::WlCompositor, wl_keyboard::WlKeyboard, wl_output::WlOutput, wl_pointer::WlPointer, wl_region::WlRegion, wl_registry::WlRegistry, wl_seat::WlSeat, wl_shm::WlShm, wl_shm_pool::WlShmPool, wl_surface::WlSurface, wl_touch::WlTouch, }; use wayland_client::{ delegate_noop, event_created_child, globals::{Global, GlobalList, GlobalListContents}, Connection, Dispatch, Proxy, QueueHandle, }; use wayland_protocols::{ wp::relative_pointer::zv1::client::{ zwp_relative_pointer_manager_v1::ZwpRelativePointerManagerV1, zwp_relative_pointer_v1::ZwpRelativePointerV1, }, wp::{ fractional_scale::v1::client::{ wp_fractional_scale_manager_v1::WpFractionalScaleManagerV1, wp_fractional_scale_v1::WpFractionalScaleV1, }, linux_dmabuf::zv1::client::{ self as dmabuf, zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1 as DmabufFeedback, zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1, }, pointer_constraints::zv1::client::{ zwp_confined_pointer_v1::ZwpConfinedPointerV1, zwp_locked_pointer_v1::ZwpLockedPointerV1, zwp_pointer_constraints_v1::ZwpPointerConstraintsV1, }, tablet::zv2::client::{ zwp_tablet_manager_v2::ZwpTabletManagerV2, zwp_tablet_pad_group_v2::{ZwpTabletPadGroupV2, EVT_RING_OPCODE, EVT_STRIP_OPCODE}, zwp_tablet_pad_ring_v2::ZwpTabletPadRingV2, zwp_tablet_pad_strip_v2::ZwpTabletPadStripV2, zwp_tablet_pad_v2::{ZwpTabletPadV2, EVT_GROUP_OPCODE}, zwp_tablet_seat_v2::{ ZwpTabletSeatV2, EVT_PAD_ADDED_OPCODE, EVT_TABLET_ADDED_OPCODE, EVT_TOOL_ADDED_OPCODE, }, zwp_tablet_tool_v2::ZwpTabletToolV2, zwp_tablet_v2::ZwpTabletV2, }, viewporter::client::{wp_viewport::WpViewport, wp_viewporter::WpViewporter}, }, xdg::decoration::zv1::client::zxdg_decoration_manager_v1::ZxdgDecorationManagerV1, xdg::decoration::zv1::client::zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1, xdg::{ activation::v1::client::xdg_activation_v1::XdgActivationV1, shell::client::{ xdg_popup::XdgPopup, xdg_positioner::XdgPositioner, xdg_surface::XdgSurface, xdg_toplevel::XdgToplevel, xdg_wm_base::XdgWmBase, }, xdg_output::zv1::client::{ zxdg_output_manager_v1::ZxdgOutputManagerV1, zxdg_output_v1::ZxdgOutputV1 as XdgOutput, }, }, }; use wayland_server::protocol as server; use wl_drm::client::wl_drm::WlDrm; use xcb::x; pub(super) struct MyWorld { pub world: World, pub global_list: GlobalList, pub new_globals: Vec, events: Vec<(Entity, ObjectEvent)>, queued_events: Vec>, pub selection_offer: Option, pub selection_requests: Vec<( String, smithay_client_toolkit::data_device_manager::WritePipe, )>, pub selection_cancelled: bool, pub pending_activations: Vec<(xcb::x::Window, String)>, } impl MyWorld { pub fn new(global_list: GlobalList) -> Self { Self { world: World::new(), global_list, new_globals: Vec::new(), events: Vec::new(), queued_events: Vec::new(), selection_offer: None, selection_requests: Vec::new(), selection_cancelled: false, pending_activations: Vec::new(), } } } impl std::ops::Deref for MyWorld { type Target = World; fn deref(&self) -> &Self::Target { &self.world } } impl std::ops::DerefMut for MyWorld { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.world } } impl MyWorld { pub(crate) fn read_events(&mut self) -> Vec<(Entity, ObjectEvent)> { let mut events = std::mem::take(&mut self.events); self.queued_events.retain(|rx| { match rx.try_recv() { Ok(event) => { events.push(event); } Err(std::sync::mpsc::TryRecvError::Empty) => return true, Err(_) => unreachable!(), } events.extend(rx.try_iter()); false }); events } } pub type Event = ::Event; delegate_noop!(MyWorld: WlCompositor); delegate_noop!(MyWorld: WlRegion); delegate_noop!(MyWorld: ignore WlShm); delegate_noop!(MyWorld: ignore ZwpLinuxDmabufV1); delegate_noop!(MyWorld: ZwpRelativePointerManagerV1); delegate_noop!(MyWorld: ignore dmabuf::zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1); delegate_noop!(MyWorld: XdgPositioner); delegate_noop!(MyWorld: WlShmPool); delegate_noop!(MyWorld: WpViewporter); delegate_noop!(MyWorld: WpViewport); delegate_noop!(MyWorld: ZxdgOutputManagerV1); delegate_noop!(MyWorld: ZwpPointerConstraintsV1); delegate_noop!(MyWorld: ZwpTabletManagerV2); delegate_noop!(MyWorld: XdgActivationV1); delegate_noop!(MyWorld: ZxdgDecorationManagerV1); delegate_noop!(MyWorld: WpFractionalScaleManagerV1); delegate_noop!(MyWorld: ignore ZxdgToplevelDecorationV1); impl Dispatch for MyWorld { fn event( state: &mut Self, _: &WlRegistry, event: ::Event, _: &GlobalListContents, _: &wayland_client::Connection, _: &wayland_client::QueueHandle, ) { if let Event::::Global { name, interface, version, } = event { state.new_globals.push(Global { name, interface, version, }); }; } } impl Dispatch for MyWorld { fn event( _: &mut Self, base: &XdgWmBase, event: ::Event, _: &(), _: &wayland_client::Connection, _: &wayland_client::QueueHandle, ) { if let Event::::Ping { serial } = event { base.pong(serial); } } } impl Dispatch for MyWorld { fn event( _: &mut Self, _: &WlCallback, event: ::Event, s_callback: &server::wl_callback::WlCallback, _: &Connection, _: &QueueHandle, ) { if let Event::::Done { callback_data } = event { s_callback.done(callback_data); } } } macro_rules! push_events { ($type:ident) => { impl Dispatch<$type, Entity> for MyWorld { fn event( state: &mut Self, _: &$type, event: <$type as Proxy>::Event, key: &Entity, _: &Connection, _: &QueueHandle, ) { state.events.push((*key, event.into())); } } }; } push_events!(WlSurface); push_events!(WlBuffer); push_events!(XdgSurface); push_events!(XdgToplevel); push_events!(XdgPopup); push_events!(WlSeat); push_events!(WlPointer); push_events!(WlOutput); push_events!(WlKeyboard); push_events!(ZwpRelativePointerV1); push_events!(WlDrm); push_events!(DmabufFeedback); push_events!(XdgOutput); push_events!(WlTouch); push_events!(ZwpConfinedPointerV1); push_events!(ZwpLockedPointerV1); push_events!(WpFractionalScaleV1); pub(crate) struct LateInitObjectKey { key: OnceLock, queued_events: Mutex>, sender: Mutex>>, } impl LateInitObjectKey

where P::Event: Into, { pub fn init(&self, key: Entity) { self.key.set(key).expect("Object key should not be set"); if let Some(sender) = self.sender.lock().unwrap().take() { for event in self.queued_events.lock().unwrap().drain(..) { sender.send((key, event.into())).unwrap(); } } } pub fn get(&self) -> Entity { self.key.get().copied().expect("Object key is not set") } fn new() -> Self { Self { key: OnceLock::new(), queued_events: Mutex::default(), sender: Mutex::default(), } } fn push_or_queue_event(&self, state: &mut MyWorld, event: P::Event) { if let Some(key) = self.key.get().copied() { state.events.push((key, event.into())); } else { let mut sender = self.sender.lock().unwrap(); if sender.is_none() { let (send, recv) = mpsc::channel(); *sender = Some(send); state.queued_events.push(recv); } self.queued_events.lock().unwrap().push(event); } } } impl std::ops::Deref for LateInitObjectKey

{ type Target = Entity; #[track_caller] fn deref(&self) -> &Self::Target { self.key.get().expect("object key has not been initialized") } } impl Dispatch for MyWorld { fn event( state: &mut Self, _: &ZwpTabletSeatV2, event: ::Event, key: &Entity, _: &Connection, _: &QueueHandle, ) { state.events.push((*key, event.into())); } event_created_child!(MyWorld, ZwpTabletSeatV2, [ EVT_TABLET_ADDED_OPCODE => (ZwpTabletV2, LateInitObjectKey::new()), EVT_PAD_ADDED_OPCODE => (ZwpTabletPadV2, LateInitObjectKey::new()), EVT_TOOL_ADDED_OPCODE => (ZwpTabletToolV2, LateInitObjectKey::new()) ]); } macro_rules! push_or_queue_events { ($type:ty) => { impl Dispatch<$type, LateInitObjectKey<$type>> for MyWorld { fn event( state: &mut Self, _: &$type, event: <$type as Proxy>::Event, key: &LateInitObjectKey<$type>, _: &Connection, _: &QueueHandle, ) { key.push_or_queue_event(state, event); } } }; } push_or_queue_events!(ZwpTabletV2); push_or_queue_events!(ZwpTabletToolV2); push_or_queue_events!(ZwpTabletPadRingV2); push_or_queue_events!(ZwpTabletPadStripV2); impl Dispatch> for MyWorld { fn event( state: &mut Self, _: &ZwpTabletPadV2, event: ::Event, key: &LateInitObjectKey, _: &Connection, _: &QueueHandle, ) { key.push_or_queue_event(state, event); } event_created_child!(MyWorld, ZwpTabletPadV2, [ EVT_GROUP_OPCODE => (ZwpTabletPadGroupV2, LateInitObjectKey::new()) ]); } impl Dispatch> for MyWorld { fn event( state: &mut Self, _: &ZwpTabletPadGroupV2, event: ::Event, key: &LateInitObjectKey, _: &Connection, _: &QueueHandle, ) { key.push_or_queue_event(state, event); } event_created_child!(MyWorld, ZwpTabletPadGroupV2, [ EVT_RING_OPCODE => (ZwpTabletPadRingV2, LateInitObjectKey::new()), EVT_STRIP_OPCODE => (ZwpTabletPadStripV2, LateInitObjectKey::new()) ]); } delegate_data_device!(MyWorld); impl DataDeviceHandler for MyWorld { fn selection( &mut self, _: &wayland_client::Connection, _: &wayland_client::QueueHandle, data_device: &wayland_client::protocol::wl_data_device::WlDataDevice, ) { let data: &DataDeviceData = data_device.data().unwrap(); self.selection_offer = data.selection_offer(); } fn drop_performed( &mut self, _: &wayland_client::Connection, _: &wayland_client::QueueHandle, _: &wayland_client::protocol::wl_data_device::WlDataDevice, ) { } fn motion( &mut self, _: &wayland_client::Connection, _: &wayland_client::QueueHandle, _: &wayland_client::protocol::wl_data_device::WlDataDevice, _: f64, _: f64, ) { } fn leave( &mut self, _: &wayland_client::Connection, _: &wayland_client::QueueHandle, _: &wayland_client::protocol::wl_data_device::WlDataDevice, ) { } fn enter( &mut self, _: &wayland_client::Connection, _: &wayland_client::QueueHandle, _: &wayland_client::protocol::wl_data_device::WlDataDevice, _: f64, _: f64, _: &wayland_client::protocol::wl_surface::WlSurface, ) { } } impl DataSourceHandler for MyWorld { fn send_request( &mut self, _: &wayland_client::Connection, _: &wayland_client::QueueHandle, _: &wayland_client::protocol::wl_data_source::WlDataSource, mime: String, fd: smithay_client_toolkit::data_device_manager::WritePipe, ) { self.selection_requests.push((mime, fd)); } fn cancelled( &mut self, _: &wayland_client::Connection, _: &wayland_client::QueueHandle, _: &wayland_client::protocol::wl_data_source::WlDataSource, ) { self.selection_cancelled = true; } fn action( &mut self, _: &wayland_client::Connection, _: &wayland_client::QueueHandle, _: &wayland_client::protocol::wl_data_source::WlDataSource, _: wayland_client::protocol::wl_data_device_manager::DndAction, ) { } fn dnd_finished( &mut self, _: &wayland_client::Connection, _: &wayland_client::QueueHandle, _: &wayland_client::protocol::wl_data_source::WlDataSource, ) { } fn dnd_dropped( &mut self, _: &wayland_client::Connection, _: &wayland_client::QueueHandle, _: &wayland_client::protocol::wl_data_source::WlDataSource, ) { } fn accept_mime( &mut self, _: &wayland_client::Connection, _: &wayland_client::QueueHandle, _: &wayland_client::protocol::wl_data_source::WlDataSource, _: Option, ) { } } impl DataOfferHandler for MyWorld { fn selected_action( &mut self, _: &wayland_client::Connection, _: &wayland_client::QueueHandle, _: &mut smithay_client_toolkit::data_device_manager::data_offer::DragOffer, _: wayland_client::protocol::wl_data_device_manager::DndAction, ) { } fn source_actions( &mut self, _: &wayland_client::Connection, _: &wayland_client::QueueHandle, _: &mut smithay_client_toolkit::data_device_manager::data_offer::DragOffer, _: wayland_client::protocol::wl_data_device_manager::DndAction, ) { } } delegate_activation!(MyWorld, ActivationData); pub struct ActivationData { window: x::Window, data: RequestData, } impl ActivationData { pub fn new(window: x::Window, data: RequestData) -> Self { Self { window, data } } } impl RequestDataExt for ActivationData { fn app_id(&self) -> Option<&str> { self.data.app_id() } fn seat_and_serial(&self) -> Option<(&wayland_client::protocol::wl_seat::WlSeat, u32)> { self.data.seat_and_serial() } fn surface(&self) -> Option<&wayland_client::protocol::wl_surface::WlSurface> { self.data.surface() } } impl ActivationHandler for MyWorld { type RequestData = ActivationData; fn new_token(&mut self, token: String, data: &Self::RequestData) { self.pending_activations.push((data.window, token)); } }