xwayland-satellite/src/server/event.rs
2024-10-29 23:24:44 -04:00

1034 lines
35 KiB
Rust

use super::*;
use crate::clientside::LateInitObjectKey;
use log::{debug, trace, warn};
use macros::simple_event_shunt;
use std::collections::HashSet;
use std::os::fd::AsFd;
use wayland_client::{protocol as client, Proxy};
use wayland_protocols::{
wp::{
pointer_constraints::zv1::{
client::{
zwp_confined_pointer_v1::{self, ZwpConfinedPointerV1 as ConfinedPointerClient},
zwp_locked_pointer_v1::{self, ZwpLockedPointerV1 as LockedPointerClient},
},
server::{
zwp_confined_pointer_v1::ZwpConfinedPointerV1 as ConfinedPointerServer,
zwp_locked_pointer_v1::ZwpLockedPointerV1 as LockedPointerServer,
},
},
relative_pointer::zv1::{
client::zwp_relative_pointer_v1::{
self, ZwpRelativePointerV1 as RelativePointerClient,
},
server::zwp_relative_pointer_v1::ZwpRelativePointerV1 as RelativePointerServer,
},
tablet::zv2::{
client::{
zwp_tablet_pad_group_v2::{self, ZwpTabletPadGroupV2 as TabletPadGroupClient},
zwp_tablet_pad_ring_v2::{self, ZwpTabletPadRingV2 as TabletPadRingClient},
zwp_tablet_pad_strip_v2::{self, ZwpTabletPadStripV2 as TabletPadStripClient},
zwp_tablet_pad_v2::{self, ZwpTabletPadV2 as TabletPadClient},
zwp_tablet_seat_v2::{self, ZwpTabletSeatV2 as TabletSeatClient},
zwp_tablet_tool_v2::{self, ZwpTabletToolV2 as TabletToolClient},
zwp_tablet_v2::{self, ZwpTabletV2 as TabletClient},
},
server::{
zwp_tablet_pad_group_v2::ZwpTabletPadGroupV2 as TabletPadGroupServer,
zwp_tablet_pad_ring_v2::ZwpTabletPadRingV2 as TabletPadRingServer,
zwp_tablet_pad_strip_v2::ZwpTabletPadStripV2 as TabletPadStripServer,
zwp_tablet_pad_v2::ZwpTabletPadV2 as TabletPadServer,
zwp_tablet_seat_v2::ZwpTabletSeatV2 as TabletSeatServer,
zwp_tablet_tool_v2::ZwpTabletToolV2 as TabletToolServer,
zwp_tablet_v2::ZwpTabletV2 as TabletServer,
},
},
},
xdg::{
shell::client::{xdg_popup, xdg_surface, xdg_toplevel},
xdg_output::zv1::{
client::zxdg_output_v1::{self, ZxdgOutputV1 as ClientXdgOutput},
server::zxdg_output_v1::ZxdgOutputV1 as ServerXdgOutput,
},
},
};
use wayland_server::protocol::{
wl_buffer::WlBuffer, wl_keyboard::WlKeyboard, wl_output::WlOutput, wl_pointer::WlPointer,
wl_seat::WlSeat, wl_touch::WlTouch,
};
#[derive(Debug)]
pub(crate) enum SurfaceEvents {
WlSurface(client::wl_surface::Event),
XdgSurface(xdg_surface::Event),
Toplevel(xdg_toplevel::Event),
Popup(xdg_popup::Event),
}
macro_rules! impl_from {
($type:ty, $variant:ident) => {
impl From<$type> for ObjectEvent {
fn from(value: $type) -> Self {
Self::Surface(SurfaceEvents::$variant(value))
}
}
};
}
impl_from!(client::wl_surface::Event, WlSurface);
impl_from!(xdg_surface::Event, XdgSurface);
impl_from!(xdg_toplevel::Event, Toplevel);
impl_from!(xdg_popup::Event, Popup);
impl HandleEvent for SurfaceData {
type Event = SurfaceEvents;
fn handle_event<C: XConnection>(&mut self, event: Self::Event, state: &mut ServerState<C>) {
match event {
SurfaceEvents::WlSurface(event) => self.surface_event(event, state),
SurfaceEvents::XdgSurface(event) => self.xdg_event(event, state),
SurfaceEvents::Toplevel(event) => self.toplevel_event(event, state),
SurfaceEvents::Popup(event) => self.popup_event(event, state),
}
}
}
impl SurfaceData {
fn surface_event<C: XConnection>(
&self,
event: client::wl_surface::Event,
state: &mut ServerState<C>,
) {
use client::wl_surface::Event;
match event {
Event::Enter { output } => {
let key: ObjectKey = output.data().copied().unwrap();
let Some(object) = state.objects.get_mut(key) else {
return;
};
let output: &mut Output = object.as_mut();
if let Some(win_data) = self
.window
.as_ref()
.map(|win| state.windows.get_mut(&win).unwrap())
{
let (x, y) = match output.position {
OutputPosition::Xdg { x, y } => (x, y),
OutputPosition::Wl { x, y } => (x, y),
};
win_data.update_output_offset(
key,
WindowOutputOffset { x, y },
state.connection.as_mut().unwrap(),
);
output.windows.insert(win_data.window);
}
self.server.enter(&output.server);
debug!("{} entered {}", self.server.id(), output.server.id());
}
Event::Leave { output } => {
let key: ObjectKey = output.data().copied().unwrap();
let Some(object) = state.objects.get_mut(key) else {
return;
};
let output: &mut Output = object.as_mut();
self.server.leave(&output.server);
}
Event::PreferredBufferScale { factor } => self.server.preferred_buffer_scale(factor),
other => warn!("unhandled surface request: {other:?}"),
}
}
fn xdg_event<C: XConnection>(&mut self, event: xdg_surface::Event, state: &mut ServerState<C>) {
let connection = state.connection.as_mut().unwrap();
let xdg_surface::Event::Configure { serial } = event else {
unreachable!();
};
let xdg = self.xdg_mut().unwrap();
xdg.surface.ack_configure(serial);
xdg.configured = true;
if let Some(pending) = xdg.pending.take() {
let window = state.associated_windows[self.key];
let window = state.windows.get_mut(&window).unwrap();
let x = pending.x + window.output_offset.x;
let y = pending.y + window.output_offset.y;
let width = if pending.width > 0 {
pending.width as u16
} else {
window.attrs.dims.width
};
let height = if pending.height > 0 {
pending.height as u16
} else {
window.attrs.dims.height
};
debug!("configuring {:?}: {x}x{y}, {width}x{height}", window.window);
connection.set_window_dims(
window.window,
PendingSurfaceState {
x,
y,
width: width as _,
height: height as _,
},
);
window.attrs.dims = WindowDims {
x: x as i16,
y: y as i16,
width,
height,
};
}
if let Some(SurfaceAttach { buffer, x, y }) = self.attach.take() {
self.client.attach(buffer.as_ref(), x, y);
}
if let Some(cb) = self.frame_callback.take() {
self.client.frame(&state.qh, cb);
}
self.client.commit();
}
fn toplevel_event<C: XConnection>(
&mut self,
event: xdg_toplevel::Event,
state: &mut ServerState<C>,
) {
match event {
xdg_toplevel::Event::Configure {
width,
height,
states,
} => {
debug!("configuring toplevel {width}x{height}, {states:?}");
if let Some(SurfaceRole::Toplevel(Some(toplevel))) = &mut self.role {
let prev_fs = toplevel.fullscreen;
toplevel.fullscreen =
states.contains(&(u32::from(xdg_toplevel::State::Fullscreen) as u8));
if toplevel.fullscreen != prev_fs {
let window = state.associated_windows[self.key];
let data = C::ExtraData::create(state);
state.connection.as_mut().unwrap().set_fullscreen(
window,
toplevel.fullscreen,
data,
);
}
};
self.xdg_mut().unwrap().pending = Some(PendingSurfaceState {
width,
height,
..Default::default()
});
}
xdg_toplevel::Event::Close => {
let window = state.associated_windows[self.key];
state.close_x_window(window);
}
ref other => warn!("unhandled xdgtoplevel event: {other:?}"),
}
}
fn popup_event<C: XConnection>(&mut self, event: xdg_popup::Event, _: &mut ServerState<C>) {
match event {
xdg_popup::Event::Configure {
x,
y,
width,
height,
} => {
trace!("popup configure: {x}x{y}, {width}x{height}");
self.xdg_mut().unwrap().pending = Some(PendingSurfaceState {
x,
y,
width,
height,
});
}
xdg_popup::Event::Repositioned { .. } => {}
other => todo!("{other:?}"),
}
}
}
pub struct GenericObject<Server: Resource, Client: Proxy> {
pub server: Server,
pub client: Client,
}
impl<S: Resource + 'static, C: Proxy + 'static> GenericObject<S, C> {
fn from_client<XC: XConnection>(client: C, state: &mut ServerState<XC>) -> &Self
where
Self: Into<WrappedObject>,
for<'a> &'a Self: TryFrom<&'a Object, Error = String>,
ServerState<XC>: wayland_server::Dispatch<S, ObjectKey>,
C::Event: Send + Into<ObjectEvent>,
{
let key = state.objects.insert_with_key(|key| {
let server = state
.client
.as_ref()
.unwrap()
.create_resource::<_, _, ServerState<XC>>(&state.dh, 1, key)
.unwrap();
let obj_key: &LateInitObjectKey<C> = client.data().unwrap();
obj_key.init(key);
Self { client, server }.into()
});
state.objects[key].as_ref()
}
}
pub trait GenericObjectExt {
type Server: Resource;
type Client: Proxy;
}
impl<S: Resource, C: Proxy> GenericObjectExt for GenericObject<S, C> {
type Server = S;
type Client = C;
}
pub type Buffer = GenericObject<WlBuffer, client::wl_buffer::WlBuffer>;
impl HandleEvent for Buffer {
type Event = client::wl_buffer::Event;
fn handle_event<C: XConnection>(&mut self, _: Self::Event, _: &mut ServerState<C>) {
// The only event from a buffer would be the release.
self.server.release();
}
}
pub type Seat = GenericObject<WlSeat, client::wl_seat::WlSeat>;
impl HandleEvent for Seat {
type Event = client::wl_seat::Event;
fn handle_event<C: XConnection>(&mut self, event: Self::Event, _: &mut ServerState<C>) {
simple_event_shunt! {
self.server, event: client::wl_seat::Event => [
Capabilities { |capabilities| convert_wenum(capabilities) },
Name { name }
]
}
}
}
pub struct Pointer {
server: WlPointer,
pub client: client::wl_pointer::WlPointer,
pending_enter: PendingEnter,
}
impl Pointer {
pub fn new(server: WlPointer, client: client::wl_pointer::WlPointer) -> Self {
Self {
server,
client,
pending_enter: PendingEnter(None),
}
}
}
struct PendingEnter(Option<client::wl_pointer::Event>);
impl HandleEvent for Pointer {
type Event = client::wl_pointer::Event;
fn handle_event<C: XConnection>(&mut self, event: Self::Event, state: &mut ServerState<C>) {
// Workaround GTK (stupidly) autoclosing popups if it receives an wl_pointer.enter
// event shortly after creation.
// When Niri creates a popup, it immediately sends wl_pointer.enter on the new surface,
// generating an EnterNotify event, and Xwayland will send a release button event.
// In its menu implementation, GTK treats EnterNotify "this menu is now active" and will
// destroy the menu if this occurs within a 500 ms interval (which it always does with
// Niri). Other compositors do not run into this problem because they appear to not send
// wl_pointer.enter until the user actually moves the mouse in the popup.
let mut process_event = Vec::new();
match event {
client::wl_pointer::Event::Enter {
serial,
ref surface,
surface_x,
surface_y,
} => 'enter: {
let surface_key: ObjectKey = surface.data().copied().unwrap();
let Some(surface_data): Option<&SurfaceData> =
state.objects.get(surface_key).map(|o| o.as_ref())
else {
warn!("could not enter surface: stale surface");
break 'enter;
};
let mut do_enter = || {
debug!("entering surface ({serial})");
self.server
.enter(serial, &surface_data.server, surface_x, surface_y);
let window = surface_data.window.unwrap();
state.connection.as_mut().unwrap().raise_to_top(window);
state.last_hovered = Some(window);
};
if matches!(surface_data.role, Some(SurfaceRole::Popup(_))) {
match self.pending_enter.0.take() {
Some(e) => {
let client::wl_pointer::Event::Enter {
serial: pending_serial,
..
} = e
else {
unreachable!();
};
if serial == pending_serial {
do_enter();
} else {
self.pending_enter.0 = Some(event);
}
}
None => {
self.pending_enter.0 = Some(event);
}
}
} else {
self.pending_enter.0.take();
do_enter();
}
}
client::wl_pointer::Event::Leave { serial, surface } => {
if !surface.is_alive() {
return;
}
debug!("leaving surface ({serial})");
self.pending_enter.0.take();
if let Some(surface) = state.get_server_surface_from_client(surface) {
self.server.leave(serial, surface);
} else {
warn!("could not leave surface: stale surface");
}
}
client::wl_pointer::Event::Motion {
time,
surface_x,
surface_y,
} => {
if let Some(p) = &self.pending_enter.0 {
let client::wl_pointer::Event::Enter {
serial,
surface,
surface_x,
surface_y,
} = p
else {
unreachable!();
};
process_event.push(client::wl_pointer::Event::Enter {
serial: *serial,
surface: surface.clone(),
surface_x: *surface_x,
surface_y: *surface_y,
});
process_event.push(event);
trace!("resending enter ({serial}) before motion");
} else {
self.server.motion(time, surface_x, surface_y);
}
}
_ => simple_event_shunt! {
self.server, event: client::wl_pointer::Event => [
Enter {
serial,
|surface| {
let Some(surface_data) = state.get_server_surface_from_client(surface) else {
return;
};
surface_data
},
surface_x,
surface_y
},
Leave {
serial,
|surface| {
let Some(surface_data) = state.get_server_surface_from_client(surface) else {
return;
};
surface_data
}
},
Motion {
time,
surface_x,
surface_y
},
Frame,
Button {
serial,
time,
button,
|state| convert_wenum(state)
},
Axis {
time,
|axis| convert_wenum(axis),
value
},
AxisSource {
|axis_source| convert_wenum(axis_source)
},
AxisStop {
time,
|axis| convert_wenum(axis)
},
AxisDiscrete {
|axis| convert_wenum(axis),
discrete
},
AxisValue120 {
|axis| convert_wenum(axis),
value120
},
AxisRelativeDirection {
|axis| convert_wenum(axis),
|direction| convert_wenum(direction)
}
]
},
}
for event in process_event {
self.handle_event(event, state);
}
}
}
pub type Keyboard = GenericObject<WlKeyboard, client::wl_keyboard::WlKeyboard>;
impl HandleEvent for Keyboard {
type Event = client::wl_keyboard::Event;
fn handle_event<C: XConnection>(&mut self, event: Self::Event, state: &mut ServerState<C>) {
match event {
client::wl_keyboard::Event::Enter {
serial,
surface,
keys,
} => {
let key: ObjectKey = surface.data().copied().unwrap();
if let Some(data) = state
.objects
.get(key)
.map(|o| <_ as AsRef<SurfaceData>>::as_ref(o))
{
state.last_kb_serial = Some(serial);
state.to_focus = Some(data.window.unwrap());
self.server.enter(serial, &data.server, keys);
}
}
client::wl_keyboard::Event::Leave { serial, surface } => {
if !surface.is_alive() {
return;
}
let key: ObjectKey = surface.data().copied().unwrap();
if let Some(data) = state
.objects
.get(key)
.map(|o| <_ as AsRef<SurfaceData>>::as_ref(o))
{
if state.to_focus == Some(data.window.unwrap()) {
state.to_focus.take();
} else {
state.unfocus = true;
}
self.server.leave(serial, &data.server);
}
}
_ => simple_event_shunt! {
self.server, event: client::wl_keyboard::Event => [
Keymap {
|format| convert_wenum(format),
|fd| fd.as_fd(),
size
},
Key {
serial,
time,
key,
|state| convert_wenum(state)
},
Modifiers {
serial,
mods_depressed,
mods_latched,
mods_locked,
group
},
RepeatInfo {
rate,
delay
}
]
},
}
}
}
pub type Touch = GenericObject<WlTouch, client::wl_touch::WlTouch>;
impl HandleEvent for Touch {
type Event = client::wl_touch::Event;
fn handle_event<C: XConnection>(&mut self, event: Self::Event, state: &mut ServerState<C>) {
simple_event_shunt! {
self.server, event: client::wl_touch::Event => [
Down {
serial,
time,
|surface| {
let Some(surface_data) = state.get_server_surface_from_client(surface) else {
return;
};
surface_data
},
id,
x,
y
},
Up {
serial,
time,
id
},
Motion {
time,
id,
x,
y
},
Frame,
Cancel,
Shape {
id,
major,
minor
},
Orientation {
id,
orientation
}
]
}
}
}
pub struct XdgOutput {
pub client: ClientXdgOutput,
pub server: ServerXdgOutput,
}
#[derive(Copy, Clone)]
pub enum OutputPosition {
Wl { x: i32, y: i32 },
Xdg { x: i32, y: i32 },
}
pub struct Output {
pub client: client::wl_output::WlOutput,
pub server: WlOutput,
pub xdg: Option<XdgOutput>,
windows: HashSet<x::Window>,
position: OutputPosition,
}
impl Output {
pub fn new(client: client::wl_output::WlOutput, server: WlOutput) -> Self {
Self {
client,
server,
xdg: None,
windows: HashSet::new(),
position: OutputPosition::Wl { x: 0, y: 0 },
}
}
}
#[derive(Debug)]
pub enum OutputEvent {
Wl(client::wl_output::Event),
Xdg(zxdg_output_v1::Event),
}
impl From<client::wl_output::Event> for ObjectEvent {
fn from(value: client::wl_output::Event) -> Self {
Self::Output(OutputEvent::Wl(value))
}
}
impl From<zxdg_output_v1::Event> for ObjectEvent {
fn from(value: zxdg_output_v1::Event) -> Self {
Self::Output(OutputEvent::Xdg(value))
}
}
impl HandleEvent for Output {
type Event = OutputEvent;
fn handle_event<C: XConnection>(&mut self, event: Self::Event, state: &mut ServerState<C>) {
match event {
OutputEvent::Xdg(event) => self.xdg_event(event, state),
OutputEvent::Wl(event) => self.wl_event(event, state),
}
}
}
impl Output {
fn update_offset<C: XConnection>(
&mut self,
offset: OutputPosition,
state: &mut ServerState<C>,
) {
if matches!(offset, OutputPosition::Wl { .. })
&& matches!(self.position, OutputPosition::Xdg { .. })
{
return;
}
self.position = offset;
let (x, y, id) = match offset {
OutputPosition::Xdg { x, y } => (x, y, self.xdg.as_ref().unwrap().server.id()),
OutputPosition::Wl { x, y } => (x, y, self.server.id()),
};
debug!("moving {id} to {x}x{y}");
self.windows.retain(|window| {
let Some(data): Option<&mut WindowData> = state.windows.get_mut(window) else {
return false;
};
data.update_output_offset(
self.server.data().copied().unwrap(),
WindowOutputOffset { x, y },
state.connection.as_mut().unwrap(),
);
return true;
});
}
fn wl_event<C: XConnection>(
&mut self,
event: client::wl_output::Event,
state: &mut ServerState<C>,
) {
if let client::wl_output::Event::Geometry { x, y, .. } = event {
self.update_offset(OutputPosition::Wl { x, y }, state);
}
simple_event_shunt! {
self.server, event: client::wl_output::Event => [
Name { name },
Description { description },
Mode {
|flags| convert_wenum(flags),
width,
height,
refresh
},
Scale { factor },
Geometry {
x,
y,
physical_width,
physical_height,
|subpixel| convert_wenum(subpixel),
make,
model,
|transform| convert_wenum(transform)
},
Done
]
}
}
fn xdg_event<C: XConnection>(
&mut self,
event: zxdg_output_v1::Event,
state: &mut ServerState<C>,
) {
if let zxdg_output_v1::Event::LogicalPosition { x, y } = event {
self.update_offset(OutputPosition::Xdg { x, y }, state);
}
let xdg = &self.xdg.as_ref().unwrap().server;
simple_event_shunt! {
xdg, event: zxdg_output_v1::Event => [
LogicalPosition { x, y },
LogicalSize { width, height },
Done,
Name { name },
Description { description }
]
}
}
}
pub type Drm = GenericObject<WlDrmServer, WlDrmClient>;
impl HandleEvent for Drm {
type Event = wl_drm::client::wl_drm::Event;
fn handle_event<C: XConnection>(&mut self, event: Self::Event, _: &mut ServerState<C>) {
simple_event_shunt! {
self.server, event: wl_drm::client::wl_drm::Event => [
Device { name },
Format { format },
Authenticated,
Capabilities { value }
]
}
}
}
pub type DmabufFeedback = GenericObject<
s_dmabuf::zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1,
c_dmabuf::zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1,
>;
impl HandleEvent for DmabufFeedback {
type Event = c_dmabuf::zwp_linux_dmabuf_feedback_v1::Event;
fn handle_event<C: XConnection>(&mut self, event: Self::Event, _: &mut ServerState<C>) {
simple_event_shunt! {
self.server, event: c_dmabuf::zwp_linux_dmabuf_feedback_v1::Event => [
Done,
FormatTable { |fd| fd.as_fd(), size },
MainDevice { device },
TrancheDone,
TrancheTargetDevice { device },
TrancheFormats { indices },
TrancheFlags { |flags| convert_wenum(flags) }
]
}
}
}
pub type RelativePointer = GenericObject<RelativePointerServer, RelativePointerClient>;
impl HandleEvent for RelativePointer {
type Event = zwp_relative_pointer_v1::Event;
fn handle_event<C: XConnection>(&mut self, event: Self::Event, _: &mut ServerState<C>) {
simple_event_shunt! {
self.server, event: zwp_relative_pointer_v1::Event => [
RelativeMotion {
utime_hi,
utime_lo,
dx,
dy,
dx_unaccel,
dy_unaccel
}
]
}
}
}
pub type LockedPointer = GenericObject<LockedPointerServer, LockedPointerClient>;
impl HandleEvent for LockedPointer {
type Event = zwp_locked_pointer_v1::Event;
fn handle_event<C: XConnection>(&mut self, event: Self::Event, _: &mut ServerState<C>) {
simple_event_shunt! {
self.server, event: zwp_locked_pointer_v1::Event => [
Locked,
Unlocked
]
}
}
}
pub type ConfinedPointer = GenericObject<ConfinedPointerServer, ConfinedPointerClient>;
impl HandleEvent for ConfinedPointer {
type Event = zwp_confined_pointer_v1::Event;
fn handle_event<C: XConnection>(&mut self, event: Self::Event, _: &mut ServerState<C>) {
simple_event_shunt! {
self.server, event: zwp_confined_pointer_v1::Event => [
Confined,
Unconfined
]
}
}
}
pub type TabletSeat = GenericObject<TabletSeatServer, TabletSeatClient>;
impl HandleEvent for TabletSeat {
type Event = zwp_tablet_seat_v2::Event;
fn handle_event<C: XConnection>(&mut self, event: Self::Event, state: &mut ServerState<C>) {
simple_event_shunt! {
self.server, event: zwp_tablet_seat_v2::Event => [
TabletAdded {
|id| &Tablet::from_client(id, state).server
},
ToolAdded {
|id| &TabletTool::from_client(id, state).server
},
PadAdded {
|id| &TabletPad::from_client(id, state).server
}
]
}
}
}
pub type Tablet = GenericObject<TabletServer, TabletClient>;
impl HandleEvent for Tablet {
type Event = zwp_tablet_v2::Event;
fn handle_event<C: XConnection>(&mut self, event: Self::Event, _: &mut ServerState<C>) {
simple_event_shunt! {
self.server, event: zwp_tablet_v2::Event => [
Name { name },
Id { vid, pid },
Path { path },
Done,
Removed
]
}
}
}
pub type TabletPad = GenericObject<TabletPadServer, TabletPadClient>;
impl HandleEvent for TabletPad {
type Event = zwp_tablet_pad_v2::Event;
fn handle_event<C: XConnection>(&mut self, event: Self::Event, state: &mut ServerState<C>) {
simple_event_shunt! {
self.server, event: zwp_tablet_pad_v2::Event => [
Group { |pad_group| &TabletPadGroup::from_client(pad_group, state).server },
Path { path },
Buttons { buttons },
Done,
Button {
time,
button,
|state| convert_wenum(state)
},
Enter {
serial,
|tablet| {
let key: &LateInitObjectKey<TabletClient> = tablet.data().unwrap();
let Some(tablet): Option<&Tablet> = state.objects.get(**key).map(|o| o.as_ref()) else {
return;
};
&tablet.server
},
|surface| {
let Some(surface_data) = state.get_server_surface_from_client(surface) else {
return;
};
surface_data
}
},
Leave {
serial,
|surface| {
let Some(surface_data) = state.get_server_surface_from_client(surface) else {
return;
};
surface_data
}
},
Removed
]
}
}
}
pub type TabletTool = GenericObject<TabletToolServer, TabletToolClient>;
impl HandleEvent for TabletTool {
type Event = zwp_tablet_tool_v2::Event;
fn handle_event<C: XConnection>(&mut self, event: Self::Event, state: &mut ServerState<C>) {
simple_event_shunt! {
self.server, event: zwp_tablet_tool_v2::Event => [
Type { |tool_type| convert_wenum(tool_type) },
HardwareSerial { hardware_serial_hi, hardware_serial_lo },
HardwareIdWacom { hardware_id_hi, hardware_id_lo },
Capability { |capability| convert_wenum(capability) },
Done,
Removed,
ProximityIn {
serial,
|tablet| {
let key: &LateInitObjectKey<TabletClient> = tablet.data().unwrap();
let Some(tablet): Option<&Tablet> = state.objects.get(**key).map(|o| o.as_ref()) else {
return;
};
&tablet.server
},
|surface| {
let Some(surface_data) = state.get_server_surface_from_client(surface) else {
return;
};
surface_data
}
},
ProximityOut,
Down { serial },
Up,
Motion { x, y },
Pressure { pressure },
Tilt { tilt_x, tilt_y },
Rotation { degrees },
Slider { position },
Wheel { degrees, clicks },
Button { serial, button, |state| convert_wenum(state) },
Frame { time },
]
}
}
}
pub type TabletPadGroup = GenericObject<TabletPadGroupServer, TabletPadGroupClient>;
impl HandleEvent for TabletPadGroup {
type Event = zwp_tablet_pad_group_v2::Event;
fn handle_event<C: XConnection>(&mut self, event: Self::Event, state: &mut ServerState<C>) {
simple_event_shunt! {
self.server, event: zwp_tablet_pad_group_v2::Event => [
Buttons { buttons },
Ring { |ring| &TabletPadRing::from_client(ring, state).server },
Strip { |strip| &TabletPadStrip::from_client(strip, state).server },
Modes { modes },
Done,
ModeSwitch { time, serial, mode }
]
}
}
}
pub type TabletPadRing = GenericObject<TabletPadRingServer, TabletPadRingClient>;
impl HandleEvent for TabletPadRing {
type Event = zwp_tablet_pad_ring_v2::Event;
fn handle_event<C: XConnection>(&mut self, event: Self::Event, _: &mut ServerState<C>) {
simple_event_shunt! {
self.server, event: zwp_tablet_pad_ring_v2::Event => [
Source { |source| convert_wenum(source) },
Angle { degrees },
Stop,
Frame { time }
]
}
}
}
pub type TabletPadStrip = GenericObject<TabletPadStripServer, TabletPadStripClient>;
impl HandleEvent for TabletPadStrip {
type Event = zwp_tablet_pad_strip_v2::Event;
fn handle_event<C: XConnection>(&mut self, event: Self::Event, _: &mut ServerState<C>) {
simple_event_shunt! {
self.server, event: zwp_tablet_pad_strip_v2::Event => [
Source { |source| convert_wenum(source) },
Position { position },
Stop,
Frame { time }
]
}
}
}