use super::*; use log::{debug, error, trace, warn}; use macros::simple_event_shunt; use std::sync::{Arc, OnceLock}; use wayland_client::globals::Global; use wayland_protocols::{ wp::{ linux_dmabuf::zv1::{client as c_dmabuf, server as s_dmabuf}, pointer_constraints::zv1::{ client::zwp_pointer_constraints_v1::ZwpPointerConstraintsV1 as PointerConstraintsClient, server::{ zwp_confined_pointer_v1::{ self as cp, ZwpConfinedPointerV1 as ConfinedPointerServer, }, zwp_locked_pointer_v1::{self as lp, ZwpLockedPointerV1 as LockedPointerServer}, zwp_pointer_constraints_v1::{ self as pc, ZwpPointerConstraintsV1 as PointerConstraintsServer, }, }, }, relative_pointer::zv1::{ client::zwp_relative_pointer_manager_v1::ZwpRelativePointerManagerV1 as RelativePointerManClient, server::zwp_relative_pointer_manager_v1::ZwpRelativePointerManagerV1 as RelativePointerManServer, }, tablet::zv2::{client as c_tablet, server as s_tablet}, viewporter::{client as c_vp, server as s_vp}, }, xdg::xdg_output::zv1::{ client::zxdg_output_manager_v1::ZxdgOutputManagerV1 as OutputManClient, server::{ zxdg_output_manager_v1::{ self as s_output_man, ZxdgOutputManagerV1 as OutputManServer, }, zxdg_output_v1::{self as s_xdgo, ZxdgOutputV1 as XdgOutputServer}, }, }, xwayland::shell::v1::server::{ xwayland_shell_v1::{self, XwaylandShellV1}, xwayland_surface_v1::{self, XwaylandSurfaceV1}, }, }; use wayland_server::{ protocol::{ wl_buffer::WlBuffer, wl_callback::WlCallback, wl_compositor::WlCompositor, wl_keyboard::WlKeyboard, wl_output::WlOutput, wl_pointer::WlPointer, wl_region::{self, WlRegion}, wl_seat::WlSeat, wl_shm::WlShm, wl_shm_pool::WlShmPool, wl_surface::WlSurface, wl_touch::WlTouch, }, Dispatch, DisplayHandle, GlobalDispatch, Resource, }; macro_rules! only_destroy_request_impl { ($object_type:ty) => { impl Dispatch<<$object_type as GenericObjectExt>::Server, ObjectKey> for ServerState { fn request( state: &mut Self, _: &Client, _: &<$object_type as GenericObjectExt>::Server, request: <<$object_type as GenericObjectExt>::Server as Resource>::Request, key: &ObjectKey, _: &DisplayHandle, _: &mut wayland_server::DataInit<'_, Self>, ) { if !matches!( request, <<$object_type as GenericObjectExt>::Server as Resource>::Request::Destroy ) { warn!( "unrecognized {} request: {:?}", stringify!($object_type), request ); return; } let obj: &$object_type = state.objects[*key].as_ref(); obj.client.destroy(); state.objects.remove(*key); } } }; } // noop impl Dispatch for ServerState { fn request( _: &mut Self, _: &wayland_server::Client, _: &WlCallback, _: ::Request, _: &(), _: &DisplayHandle, _: &mut wayland_server::DataInit<'_, Self>, ) { unreachable!(); } } impl Dispatch for ServerState { fn request( state: &mut Self, _: &wayland_server::Client, _: &WlSurface, request: ::Request, key: &ObjectKey, _: &DisplayHandle, data_init: &mut wayland_server::DataInit<'_, Self>, ) { let surface: &SurfaceData = state.objects[*key].as_ref(); let configured = surface.role.is_none() || surface.xdg().is_none() || surface.xdg().unwrap().configured; match request { Request::::Attach { buffer, x, y } => { if buffer.is_none() { trace!("xwayland attached null buffer to {:?}", surface.client); } let buffer = buffer.as_ref().map(|b| { let key: &ObjectKey = b.data().unwrap(); let data: &Buffer = state.objects[*key].as_ref(); &data.client }); if configured { surface.client.attach(buffer, x, y); } else { let buffer = buffer.cloned(); let surface: &mut SurfaceData = state.objects[*key].as_mut(); surface.attach = Some(SurfaceAttach { buffer, x, y }); } } Request::::DamageBuffer { x, y, width, height, } => { if configured { surface.client.damage_buffer(x, y, width, height); } } Request::::Frame { callback } => { let cb = data_init.init(callback, ()); if configured { surface.client.frame(&state.qh, cb); } else { let surface: &mut SurfaceData = state.objects[*key].as_mut(); surface.frame_callback = Some(cb); } } Request::::Commit => { if configured { surface.client.commit(); } } Request::::Destroy => { let mut object = state.objects.remove(*key).unwrap(); let surface: &mut SurfaceData = object.as_mut(); if let Some(window_data) = surface.window.and_then(|w| state.windows.get_mut(&w)) { window_data.surface_key.take(); } surface.destroy_role(); surface.client.destroy(); debug!( "deleting key: {key:?} (surface {:?})", surface.server.id().protocol_id() ); } Request::::SetBufferScale { scale } => { surface.client.set_buffer_scale(scale); } Request::::SetInputRegion { region } => { let region = region.as_ref().map(|r| r.data().unwrap()); surface.client.set_input_region(region); } other => warn!("unhandled surface request: {other:?}"), } } } impl Dispatch for ServerState { fn request( _: &mut Self, _: &wayland_server::Client, _: &WlRegion, request: ::Request, client: &client::wl_region::WlRegion, _: &DisplayHandle, _: &mut wayland_server::DataInit<'_, Self>, ) { macros::simple_event_shunt! { client, request: wl_region::Request => [ Add { x, y, width, height }, Subtract { x, y, width, height }, Destroy ] } } } impl Dispatch> for ServerState { fn request( state: &mut Self, _: &wayland_server::Client, _: &WlCompositor, request: ::Request, client: &ClientGlobalWrapper, _: &DisplayHandle, data_init: &mut wayland_server::DataInit<'_, Self>, ) { match request { Request::::CreateSurface { id } => { let mut surface_id = None; state.objects.insert_with_key(|key| { let client = client.create_surface(&state.qh, key); let server = data_init.init(id, key); surface_id = Some(server.id().protocol_id()); debug!("new surface with key {key:?} ({surface_id:?})"); SurfaceData { client, server, key, serial: Default::default(), attach: None, frame_callback: None, role: None, xwl: None, window: None, output_key: None, } .into() }); } Request::::CreateRegion { id } => { let c_region = client.create_region(&state.qh, ()); data_init.init(id, c_region); } other => { warn!("unhandled wlcompositor request: {other:?}"); } } } } impl Dispatch for ServerState { fn request( state: &mut Self, _: &wayland_server::Client, _: &WlBuffer, request: ::Request, key: &ObjectKey, _: &DisplayHandle, _: &mut wayland_server::DataInit<'_, Self>, ) { assert!(matches!(request, Request::::Destroy)); let buf: &Buffer = state.objects[*key].as_ref(); buf.client.destroy(); state.objects.remove(*key); } } impl Dispatch for ServerState { fn request( state: &mut Self, _: &wayland_server::Client, _: &WlShmPool, request: ::Request, c_pool: &client::wl_shm_pool::WlShmPool, _: &DisplayHandle, data_init: &mut wayland_server::DataInit<'_, Self>, ) { match request { Request::::CreateBuffer { id, offset, width, height, stride, format, } => { state.objects.insert_with_key(|key| { let client = c_pool.create_buffer( offset, width, height, stride, convert_wenum(format), &state.qh, key, ); let server = data_init.init(id, key); Buffer { server, client }.into() }); } Request::::Resize { size } => { c_pool.resize(size); } Request::::Destroy => { c_pool.destroy(); state.clientside.queue.flush().unwrap(); } other => warn!("unhandled shmpool request: {other:?}"), } } } impl Dispatch> for ServerState { fn request( state: &mut Self, _: &wayland_server::Client, _: &WlShm, request: ::Request, client: &ClientGlobalWrapper, _: &DisplayHandle, data_init: &mut wayland_server::DataInit<'_, Self>, ) { match request { Request::::CreatePool { id, fd, size } => { let c_pool = client.create_pool(fd.as_fd(), size, &state.qh, ()); data_init.init(id, c_pool); } other => { warn!("unhandled shm pool request: {other:?}"); } } } } impl Dispatch for ServerState { fn request( state: &mut Self, _: &wayland_server::Client, _: &WlPointer, request: ::Request, key: &ObjectKey, _: &DisplayHandle, _: &mut wayland_server::DataInit<'_, Self>, ) { let Pointer { client: c_pointer, .. }: &Pointer = state.objects[*key].as_ref(); match request { Request::::SetCursor { serial, hotspot_x, hotspot_y, surface, } => { let c_surface = surface.and_then(|s| state.get_client_surface_from_server(s)); c_pointer.set_cursor(serial, c_surface, hotspot_x, hotspot_y); } Request::::Release => { c_pointer.release(); state.objects.remove(*key); } _ => warn!("unhandled cursor request: {request:?}"), } } } impl Dispatch for ServerState { fn request( state: &mut Self, _: &wayland_server::Client, _: &WlKeyboard, request: ::Request, key: &ObjectKey, _: &DisplayHandle, _: &mut wayland_server::DataInit<'_, Self>, ) { match request { Request::::Release => { let Keyboard { client, .. }: &_ = state.objects[*key].as_ref(); client.release(); state.objects.remove(*key); } _ => unreachable!(), } } } impl Dispatch for ServerState { fn request( state: &mut Self, _: &wayland_server::Client, _: &WlTouch, request: ::Request, key: &ObjectKey, _: &DisplayHandle, _: &mut wayland_server::DataInit<'_, Self>, ) { match request { Request::::Release => { let Touch { client, .. }: &_ = state.objects[*key].as_ref(); client.release(); state.objects.remove(*key); } _ => unreachable!(), } } } impl Dispatch for ServerState { fn request( state: &mut Self, _: &wayland_server::Client, _: &WlSeat, request: ::Request, key: &ObjectKey, _: &DisplayHandle, data_init: &mut wayland_server::DataInit<'_, Self>, ) { match request { Request::::GetPointer { id } => { state .objects .insert_from_other_objects([*key], |[seat_obj], key| { let Seat { client, .. }: &Seat = seat_obj.try_into().unwrap(); let client = client.get_pointer(&state.qh, key); let server = data_init.init(id, key); trace!("new pointer: {server:?}"); Pointer::new(server, client).into() }); } Request::::GetKeyboard { id } => { state .objects .insert_from_other_objects([*key], |[seat_obj], key| { let Seat { client, .. }: &Seat = seat_obj.try_into().unwrap(); let client = client.get_keyboard(&state.qh, key); let server = data_init.init(id, key); Keyboard { client, server }.into() }); } Request::::GetTouch { id } => { state .objects .insert_from_other_objects([*key], |[seat_obj], key| { let Seat { client, .. }: &Seat = seat_obj.try_into().unwrap(); let client = client.get_touch(&state.qh, key); let server = data_init.init(id, key); Touch { client, server }.into() }); } other => warn!("unhandled seat request: {other:?}"), } } } only_destroy_request_impl!(RelativePointer); impl Dispatch> for ServerState { fn request( state: &mut Self, _: &wayland_server::Client, _: &RelativePointerManServer, request: ::Request, client: &ClientGlobalWrapper, _: &DisplayHandle, data_init: &mut wayland_server::DataInit<'_, Self>, ) { match request { Request::::GetRelativePointer { id, pointer } => { let p_key: ObjectKey = pointer.data().copied().unwrap(); state .objects .insert_from_other_objects([p_key], |[pointer_obj], key| { let pointer: &Pointer = pointer_obj.try_into().unwrap(); let client = client.get_relative_pointer(&pointer.client, &state.qh, key); let server = data_init.init(id, key); RelativePointer { client, server }.into() }); } _ => warn!("unhandled relative pointer request: {request:?}"), } } } impl Dispatch for ServerState { fn request( state: &mut Self, _: &wayland_server::Client, _: &WlOutput, request: ::Request, key: &ObjectKey, _: &DisplayHandle, _: &mut wayland_server::DataInit<'_, Self>, ) { match request { wayland_server::protocol::wl_output::Request::Release => { let Output { client, .. }: &_ = state.objects[*key].as_ref(); client.release(); todo!("handle wloutput destruction"); } _ => warn!("unhandled output request {request:?}"), } } } impl Dispatch for ServerState { fn request( state: &mut Self, _: &wayland_server::Client, _: &s_dmabuf::zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1, request: ::Request, key: &ObjectKey, _: &DisplayHandle, _: &mut wayland_server::DataInit<'_, Self>, ) { use s_dmabuf::zwp_linux_dmabuf_feedback_v1::Request::*; match request { Destroy => { let dmabuf: &DmabufFeedback = state.objects[*key].as_ref(); dmabuf.client.destroy(); state.objects.remove(*key); } _ => unreachable!(), } } } impl Dispatch< s_dmabuf::zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1, c_dmabuf::zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1, > for ServerState { fn request( state: &mut Self, _: &wayland_server::Client, _: &s_dmabuf::zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1, request: ::Request, c_params: &c_dmabuf::zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1, _: &DisplayHandle, data_init: &mut wayland_server::DataInit<'_, Self>, ) { use s_dmabuf::zwp_linux_buffer_params_v1::Request::*; match request { // TODO: Xwayland doesn't actually seem to use the Create request, and I don't feel like implementing it... Create { .. } => todo!(), CreateImmed { buffer_id, width, height, format, flags, } => { state.objects.insert_with_key(|key| { let client = c_params.create_immed( width, height, format, convert_wenum(flags), &state.qh, key, ); let server = data_init.init(buffer_id, key); Buffer { server, client }.into() }); } Add { fd, plane_idx, offset, stride, modifier_hi, modifier_lo, } => { c_params.add( fd.as_fd(), plane_idx, offset, stride, modifier_hi, modifier_lo, ); } Destroy => { c_params.destroy(); } _ => warn!("unhandled params request: {request:?}"), } } } impl Dispatch< s_dmabuf::zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1, ClientGlobalWrapper, > for ServerState { fn request( state: &mut Self, _: &wayland_server::Client, _: &s_dmabuf::zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1, request: ::Request, client: &ClientGlobalWrapper, _: &DisplayHandle, data_init: &mut wayland_server::DataInit<'_, Self>, ) { use s_dmabuf::zwp_linux_dmabuf_v1::Request::*; match request { Destroy => { client.destroy(); } CreateParams { params_id } => { let c_params = client.create_params(&state.qh, ()); data_init.init(params_id, c_params); } GetDefaultFeedback { id } => { state.objects.insert_with_key(|key| { let client = client.get_default_feedback(&state.qh, key); let server = data_init.init(id, key); DmabufFeedback { client, server }.into() }); } GetSurfaceFeedback { id, surface } => { let surf_key: ObjectKey = surface.data().copied().unwrap(); state .objects .insert_from_other_objects([surf_key], |[surface_obj], key| { let SurfaceData { client: c_surface, .. }: &SurfaceData = surface_obj.try_into().unwrap(); let client = client.get_surface_feedback(c_surface, &state.qh, key); let server = data_init.init(id, key); DmabufFeedback { client, server }.into() }); } _ => warn!("unhandled dmabuf request: {request:?}"), } } } impl Dispatch for ServerState { fn request( state: &mut Self, _: &wayland_server::Client, _: &WlDrmServer, request: ::Request, key: &ObjectKey, _: &DisplayHandle, data_init: &mut wayland_server::DataInit<'_, Self>, ) { use wl_drm::server::wl_drm::Request::*; type DrmFn = dyn FnOnce( &wl_drm::client::wl_drm::WlDrm, ObjectKey, &ClientQueueHandle, ) -> client::wl_buffer::WlBuffer; let mut bufs: Option<(Box, wayland_server::New)> = None; match request { CreateBuffer { id, name, width, height, stride, format, } => { bufs = Some(( Box::new(move |drm, key, qh| { drm.create_buffer(name, width, height, stride, format, qh, key) }), id, )); } CreatePlanarBuffer { id, name, width, height, format, offset0, stride0, offset1, stride1, offset2, stride2, } => { bufs = Some(( Box::new(move |drm, key, qh| { drm.create_planar_buffer( name, width, height, format, offset0, stride0, offset1, stride1, offset2, stride2, qh, key, ) }), id, )); } CreatePrimeBuffer { id, name, width, height, format, offset0, stride0, offset1, stride1, offset2, stride2, } => { bufs = Some(( Box::new(move |drm, key, qh| { drm.create_prime_buffer( name.as_fd(), width, height, format, offset0, stride0, offset1, stride1, offset2, stride2, qh, key, ) }), id, )); } Authenticate { id } => { let drm: &Drm = state.objects[*key].as_ref(); drm.client.authenticate(id); } _ => unreachable!(), } if let Some((buf_create, id)) = bufs { state .objects .insert_from_other_objects([*key], |[drm_obj], key| { let drm: &Drm = drm_obj.try_into().unwrap(); let client = buf_create(&drm.client, key, &state.qh); let server = data_init.init(id, key); Buffer { client, server }.into() }); } } } impl Dispatch for ServerState { fn request( _: &mut Self, _: &wayland_server::Client, _: &s_vp::wp_viewport::WpViewport, request: ::Request, c_viewport: &c_vp::wp_viewport::WpViewport, _: &DisplayHandle, _: &mut wayland_server::DataInit<'_, Self>, ) { simple_event_shunt! { c_viewport, request: s_vp::wp_viewport::Request => [ SetSource { x, y, width, height }, SetDestination { width, height }, Destroy ] } } } impl Dispatch< s_vp::wp_viewporter::WpViewporter, ClientGlobalWrapper, > for ServerState { fn request( state: &mut Self, _: &wayland_server::Client, _: &s_vp::wp_viewporter::WpViewporter, request: ::Request, client: &ClientGlobalWrapper, _: &DisplayHandle, data_init: &mut wayland_server::DataInit<'_, Self>, ) { use s_vp::wp_viewporter; match request { wp_viewporter::Request::GetViewport { id, surface } => 'get_viewport: { let Some(c_surface) = state.get_client_surface_from_server(surface) else { break 'get_viewport; }; let c_viewport = client.get_viewport(c_surface, &state.qh, ()); data_init.init(id, c_viewport); } wp_viewporter::Request::Destroy => { client.destroy(); } _ => unreachable!(), } } } impl Dispatch for ServerState { fn request( state: &mut Self, _: &wayland_server::Client, _: &XdgOutputServer, request: ::Request, key: &ObjectKey, _: &DisplayHandle, _: &mut wayland_server::DataInit<'_, Self>, ) { let s_xdgo::Request::Destroy = request else { unreachable!(); }; let output: &mut Output = state.objects[*key].as_mut(); let xdg_output = output.xdg.take().unwrap(); xdg_output.client.destroy(); } } impl Dispatch> for ServerState { fn request( state: &mut Self, _: &wayland_server::Client, _: &OutputManServer, request: ::Request, client: &ClientGlobalWrapper, _: &DisplayHandle, data_init: &mut wayland_server::DataInit<'_, Self>, ) { match request { s_output_man::Request::GetXdgOutput { id, output } => { let output_key: ObjectKey = output.data().copied().unwrap(); let output: &mut Output = state.objects[output_key].as_mut(); let client = client.get_xdg_output(&output.client, &state.qh, output_key); let server = data_init.init(id, output_key); output.xdg = Some(XdgOutput { client, server }); } s_output_man::Request::Destroy => {} _ => unreachable!(), } } } impl Dispatch for ServerState { fn request( state: &mut Self, _: &wayland_server::Client, _: &ConfinedPointerServer, request: ::Request, key: &ObjectKey, _: &DisplayHandle, _: &mut wayland_server::DataInit<'_, Self>, ) { let confined_ptr: &ConfinedPointer = state.objects[*key].as_ref(); simple_event_shunt! { confined_ptr.client, request: cp::Request => [ SetRegion { |region| region.as_ref().map(|r| r.data().unwrap()) }, Destroy ] } } } impl Dispatch for ServerState { fn request( state: &mut Self, _: &wayland_server::Client, _: &LockedPointerServer, request: ::Request, key: &ObjectKey, _: &DisplayHandle, _: &mut wayland_server::DataInit<'_, Self>, ) { let locked_ptr: &LockedPointer = state.objects[*key].as_ref(); simple_event_shunt! { locked_ptr.client, request: lp::Request => [ SetCursorPositionHint { surface_x, surface_y }, SetRegion { |region| region.as_ref().map(|r| r.data().unwrap()) }, Destroy ] } } } impl Dispatch> for ServerState { fn request( state: &mut Self, _: &wayland_server::Client, _: &PointerConstraintsServer, request: ::Request, client: &ClientGlobalWrapper, _: &DisplayHandle, data_init: &mut wayland_server::DataInit<'_, Self>, ) { use pc::Request; match request { Request::ConfinePointer { id, surface, pointer, region, lifetime, } => { let surf_key: ObjectKey = surface.data().copied().unwrap(); let ptr_key: ObjectKey = pointer.data().copied().unwrap(); state.objects.insert_from_other_objects( [surf_key, ptr_key], |[surf_obj, ptr_obj], key| { let SurfaceData { client: c_surface, .. }: &SurfaceData = surf_obj.try_into().unwrap(); let Pointer { client: c_ptr, .. }: &Pointer = ptr_obj.try_into().unwrap(); let client = client.confine_pointer( c_surface, c_ptr, region.as_ref().map(|r| r.data().unwrap()), convert_wenum(lifetime), &state.qh, key, ); let server = data_init.init(id, key); ConfinedPointer { client, server }.into() }, ); } Request::LockPointer { id, surface, pointer, region, lifetime, } => { let surf_key: ObjectKey = surface.data().copied().unwrap(); let ptr_key: ObjectKey = pointer.data().copied().unwrap(); state.objects.insert_from_other_objects( [surf_key, ptr_key], |[surf_obj, ptr_obj], key| { let SurfaceData { client: c_surface, .. }: &SurfaceData = surf_obj.try_into().unwrap(); let Pointer { client: c_ptr, .. }: &Pointer = ptr_obj.try_into().unwrap(); let client = client.lock_pointer( c_surface, c_ptr, region.as_ref().map(|r| r.data().unwrap()), convert_wenum(lifetime), &state.qh, key, ); let server = data_init.init(id, key); LockedPointer { client, server }.into() }, ); } Request::Destroy => { client.destroy(); } _ => unreachable!("unhandled pointer constraints request"), } } } impl Dispatch< s_tablet::zwp_tablet_manager_v2::ZwpTabletManagerV2, ClientGlobalWrapper, > for ServerState { fn request( state: &mut Self, _: &wayland_server::Client, _: &s_tablet::zwp_tablet_manager_v2::ZwpTabletManagerV2, request: ::Request, client: &ClientGlobalWrapper, _: &DisplayHandle, data_init: &mut wayland_server::DataInit<'_, Self>, ) { use s_tablet::zwp_tablet_manager_v2::Request::*; match request { GetTabletSeat { tablet_seat, seat } => { let seat_key: ObjectKey = seat.data().copied().unwrap(); state .objects .insert_from_other_objects([seat_key], |[seat_obj], key| { let Seat { client: c_seat, .. }: &Seat = seat_obj.try_into().unwrap(); let client = client.get_tablet_seat(c_seat, &state.qh, key); let server = data_init.init(tablet_seat, key); TabletSeat { client, server }.into() }); } other => { warn!("unhandled tablet request: {other:?}"); } } } } only_destroy_request_impl!(TabletSeat); only_destroy_request_impl!(Tablet); only_destroy_request_impl!(TabletPadGroup); impl Dispatch for ServerState { fn request( state: &mut Self, _: &Client, _: &s_tablet::zwp_tablet_pad_v2::ZwpTabletPadV2, request: ::Request, key: &ObjectKey, _: &DisplayHandle, _: &mut wayland_server::DataInit<'_, Self>, ) { let pad: &TabletPad = state.objects[*key].as_ref(); match request { s_tablet::zwp_tablet_pad_v2::Request::SetFeedback { button, description, serial, } => { pad.client.set_feedback(button, description, serial); } s_tablet::zwp_tablet_pad_v2::Request::Destroy => { pad.client.destroy(); state.objects.remove(*key); } other => warn!("unhandled tablet pad request: {other:?}"), } } } impl Dispatch for ServerState { fn request( state: &mut Self, _: &Client, _: &s_tablet::zwp_tablet_tool_v2::ZwpTabletToolV2, request: ::Request, key: &ObjectKey, _: &DisplayHandle, _: &mut wayland_server::DataInit<'_, Self>, ) { let tool: &TabletTool = state.objects[*key].as_ref(); match request { s_tablet::zwp_tablet_tool_v2::Request::SetCursor { serial, surface, hotspot_x, hotspot_y, } => { let surf_key: Option = surface.map(|s| s.data().copied().unwrap()); let c_surface = surf_key.map(|key| { let d: &SurfaceData = state.objects[key].as_ref(); &d.client }); tool.client .set_cursor(serial, c_surface, hotspot_x, hotspot_y); } s_tablet::zwp_tablet_tool_v2::Request::Destroy => { tool.client.destroy(); state.objects.remove(*key); } other => warn!("unhandled tablet tool request: {other:?}"), } } } impl Dispatch for ServerState { fn request( state: &mut Self, _: &Client, _: &s_tablet::zwp_tablet_pad_ring_v2::ZwpTabletPadRingV2, request: ::Request, key: &ObjectKey, _: &DisplayHandle, _: &mut wayland_server::DataInit<'_, Self>, ) { let ring: &TabletPadRing = state.objects[*key].as_ref(); match request { s_tablet::zwp_tablet_pad_ring_v2::Request::SetFeedback { description, serial, } => { ring.client.set_feedback(description, serial); } s_tablet::zwp_tablet_pad_ring_v2::Request::Destroy => { ring.client.destroy(); state.objects.remove(*key); } other => warn!("unhandled tablet pad ring requst: {other:?}"), } } } impl Dispatch for ServerState { fn request( state: &mut Self, _: &Client, _: &s_tablet::zwp_tablet_pad_strip_v2::ZwpTabletPadStripV2, request: ::Request, key: &ObjectKey, _: &DisplayHandle, _: &mut wayland_server::DataInit<'_, Self>, ) { let strip: &TabletPadStrip = state.objects[*key].as_ref(); match request { s_tablet::zwp_tablet_pad_strip_v2::Request::SetFeedback { description, serial, } => { strip.client.set_feedback(description, serial); } s_tablet::zwp_tablet_pad_strip_v2::Request::Destroy => { strip.client.destroy(); state.objects.remove(*key); } other => warn!("unhandled tablet pad strip requst: {other:?}"), } } } #[derive(Clone)] pub(crate) struct ClientGlobalWrapper(Arc>); impl std::ops::Deref for ClientGlobalWrapper { type Target = T; fn deref(&self) -> &Self::Target { self.0.get().unwrap() } } impl Default for ClientGlobalWrapper { fn default() -> Self { Self(Arc::default()) } } macro_rules! global_dispatch_no_events { ($server:ty, $client:ty) => { impl GlobalDispatch<$server, Global> for ServerState where ServerState: Dispatch<$server, ClientGlobalWrapper<$client>>, Globals: wayland_client::Dispatch<$client, ()>, { fn bind( state: &mut Self, _: &DisplayHandle, _: &wayland_server::Client, resource: wayland_server::New<$server>, data: &Global, data_init: &mut wayland_server::DataInit<'_, Self>, ) { let client = ClientGlobalWrapper::<$client>::default(); let server = data_init.init(resource, client.clone()); client .0 .set( state .clientside .global_list .registry() .bind::<$client, _, _>(data.name, server.version(), &state.qh, ()), ) .unwrap(); } } }; } macro_rules! global_dispatch_with_events { ($server:ty, $client:ty) => { impl GlobalDispatch<$server, Global> for ServerState where $server: Resource, $client: Proxy, ServerState: Dispatch<$server, ObjectKey>, Globals: wayland_client::Dispatch<$client, ObjectKey>, GenericObject<$server, $client>: Into, { fn bind( state: &mut Self, _: &DisplayHandle, _: &wayland_server::Client, resource: wayland_server::New<$server>, data: &Global, data_init: &mut wayland_server::DataInit<'_, Self>, ) { state.objects.insert_with_key(|key| { let server = data_init.init(resource, key); let client = state .clientside .global_list .registry() .bind::<$client, _, _>(data.name, server.version(), &state.qh, key); GenericObject { server, client }.into() }); } } }; } global_dispatch_no_events!(WlShm, client::wl_shm::WlShm); global_dispatch_no_events!(WlCompositor, client::wl_compositor::WlCompositor); global_dispatch_no_events!(RelativePointerManServer, RelativePointerManClient); global_dispatch_no_events!( s_dmabuf::zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1, c_dmabuf::zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1 ); global_dispatch_no_events!(OutputManServer, OutputManClient); global_dispatch_no_events!( s_vp::wp_viewporter::WpViewporter, c_vp::wp_viewporter::WpViewporter ); global_dispatch_no_events!(PointerConstraintsServer, PointerConstraintsClient); global_dispatch_no_events!( s_tablet::zwp_tablet_manager_v2::ZwpTabletManagerV2, c_tablet::zwp_tablet_manager_v2::ZwpTabletManagerV2 ); impl GlobalDispatch for ServerState where WlSeat: Resource, client::wl_seat::WlSeat: Proxy, ServerState: Dispatch, Globals: wayland_client::Dispatch, GenericObject: Into, { fn bind( state: &mut Self, _: &DisplayHandle, _: &wayland_server::Client, resource: wayland_server::New, data: &Global, data_init: &mut wayland_server::DataInit<'_, Self>, ) { state.objects.insert_with_key(|key| { let server = data_init.init(resource, key); let client = state .clientside .global_list .registry() .bind::(data.name, server.version(), &state.qh, key); if let Some(c) = &mut state.clipboard_data { c.device = Some(c.manager.get_data_device(&state.qh, &client)); } GenericObject { server, client }.into() }); } } impl GlobalDispatch for ServerState { fn bind( state: &mut Self, _: &DisplayHandle, _: &wayland_server::Client, resource: wayland_server::New, data: &Global, data_init: &mut wayland_server::DataInit<'_, Self>, ) { let key = state.objects.insert_with_key(|key| { let server = data_init.init(resource, key); let client = state .clientside .global_list .registry() .bind::( data.name, server.version(), &state.qh, key, ); Output::new(client, server).into() }); state.output_keys.insert(key, ()); } } global_dispatch_with_events!(WlDrmServer, WlDrmClient); impl GlobalDispatch for ServerState { fn bind( _: &mut Self, _: &DisplayHandle, _: &wayland_server::Client, resource: wayland_server::New, _: &(), data_init: &mut wayland_server::DataInit<'_, Self>, ) { data_init.init(resource, ()); } } impl Dispatch for ServerState { fn request( state: &mut Self, client: &wayland_server::Client, _: &XwaylandShellV1, request: ::Request, _: &(), dhandle: &DisplayHandle, data_init: &mut wayland_server::DataInit<'_, Self>, ) { use xwayland_shell_v1::Request; match request { Request::GetXwaylandSurface { id, surface } => { let key: ObjectKey = surface.data().copied().unwrap(); let data: &mut SurfaceData = state.objects[key].as_mut(); if data.xwl.is_some() { error!("Surface {surface:?} already has the xwayland surface role!"); client.kill( dhandle, wayland_client::backend::protocol::ProtocolError { code: 0, object_id: surface.id().protocol_id(), object_interface: "wl_surface".to_string(), message: "Surface already has role".to_string(), }, ); return; } let xwl = data_init.init(id, key); data.xwl = Some(xwl); } Request::Destroy => {} _ => unreachable!(), } } } impl Dispatch for ServerState { fn request( state: &mut Self, _: &wayland_server::Client, _: &XwaylandSurfaceV1, request: ::Request, key: &ObjectKey, _: &DisplayHandle, _: &mut wayland_server::DataInit<'_, Self>, ) { use xwayland_surface_v1::Request; let data: &mut SurfaceData = state.objects[*key].as_mut(); match request { Request::SetSerial { serial_lo, serial_hi, } => { let serial = [serial_lo, serial_hi]; data.serial = Some(serial); if let Some((win, window_data)) = state.windows.iter_mut().find_map(|(win, data)| { Some(*win) .zip((data.surface_serial.is_some_and(|s| s == serial)).then_some(data)) }) { debug!( "associate surface {} with {:?}", data.server.id().protocol_id(), win ); window_data.surface_key = Some(*key); state.associated_windows.insert(*key, win); if window_data.mapped { state.create_role_window(win, *key); } } } Request::Destroy => { data.xwl.take(); } _ => unreachable!(), } } }