Add tablet support

Closes #47
This commit is contained in:
Shawn Wallace 2024-10-17 01:33:56 -04:00
parent 73ca9c91f1
commit c77b66cc93
6 changed files with 925 additions and 66 deletions

View file

@ -1,5 +1,6 @@
use crate::server::{ObjectEvent, ObjectKey};
use std::os::unix::net::UnixStream;
use std::sync::{mpsc, Mutex, Once, 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,
@ -7,7 +8,7 @@ use wayland_client::protocol::{
wl_surface::WlSurface, wl_touch::WlTouch,
};
use wayland_client::{
delegate_noop,
delegate_noop, event_created_child,
globals::{registry_queue_init, Global, GlobalList, GlobalListContents},
Connection, Dispatch, EventQueue, Proxy, QueueHandle,
};
@ -27,6 +28,19 @@ use wayland_protocols::{
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::{
@ -44,7 +58,8 @@ use wl_drm::client::wl_drm::WlDrm;
#[derive(Default)]
pub struct Globals {
pub(crate) events: Vec<(ObjectKey, ObjectEvent)>,
events: Vec<(ObjectKey, ObjectEvent)>,
queued_events: Vec<mpsc::Receiver<(ObjectKey, ObjectEvent)>>,
pub new_globals: Vec<Global>,
pub selection: Option<wayland_client::protocol::wl_data_device::WlDataDevice>,
pub selection_requests: Vec<(
@ -57,7 +72,7 @@ pub struct Globals {
pub type ClientQueueHandle = QueueHandle<Globals>;
pub struct ClientState {
pub connection: Connection,
_connection: Connection,
pub queue: EventQueue<Globals>,
pub qh: ClientQueueHandle,
pub globals: Globals,
@ -77,13 +92,31 @@ impl ClientState {
let qh = queue.handle();
Self {
connection,
_connection: connection,
queue,
qh,
globals,
global_list,
}
}
pub fn read_events(&mut self) -> Vec<(ObjectKey, ObjectEvent)> {
let mut events = std::mem::take(&mut self.globals.events);
self.globals.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<T> = <T as Proxy>::Event;
@ -100,6 +133,7 @@ delegate_noop!(Globals: WpViewporter);
delegate_noop!(Globals: WpViewport);
delegate_noop!(Globals: ZxdgOutputManagerV1);
delegate_noop!(Globals: ZwpPointerConstraintsV1);
delegate_noop!(Globals: ZwpTabletManagerV2);
impl Dispatch<WlRegistry, GlobalListContents> for Globals {
fn event(
@ -188,3 +222,130 @@ push_events!(XdgOutput);
push_events!(WlTouch);
push_events!(ZwpConfinedPointerV1);
push_events!(ZwpLockedPointerV1);
pub(crate) struct LateInitObjectKey<P: Proxy> {
key: OnceLock<ObjectKey>,
queued_events: Mutex<Vec<P::Event>>,
sender: Mutex<Option<mpsc::Sender<(ObjectKey, ObjectEvent)>>>,
}
impl<P: Proxy> LateInitObjectKey<P>
where
P::Event: Into<ObjectEvent>,
{
pub fn init(&self, key: ObjectKey) {
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();
}
}
}
fn new() -> Self {
Self {
key: OnceLock::new(),
queued_events: Mutex::default(),
sender: Mutex::default(),
}
}
fn push_or_queue_event(&self, state: &mut Globals, 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<P: Proxy> std::ops::Deref for LateInitObjectKey<P> {
type Target = ObjectKey;
#[track_caller]
fn deref(&self) -> &Self::Target {
self.key.get().expect("object key has not been initialized")
}
}
impl Dispatch<ZwpTabletSeatV2, ObjectKey> for Globals {
fn event(
state: &mut Self,
_: &ZwpTabletSeatV2,
event: <ZwpTabletSeatV2 as Proxy>::Event,
key: &ObjectKey,
_: &Connection,
_: &QueueHandle<Self>,
) {
state.events.push((*key, event.into()));
}
event_created_child!(Globals, 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 Globals {
fn event(
state: &mut Self,
_: &$type,
event: <$type as Proxy>::Event,
key: &LateInitObjectKey<$type>,
_: &Connection,
_: &QueueHandle<Self>,
) {
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<ZwpTabletPadV2, LateInitObjectKey<ZwpTabletPadV2>> for Globals {
fn event(
state: &mut Self,
_: &ZwpTabletPadV2,
event: <ZwpTabletPadV2 as Proxy>::Event,
key: &LateInitObjectKey<ZwpTabletPadV2>,
_: &Connection,
_: &QueueHandle<Self>,
) {
key.push_or_queue_event(state, event);
}
event_created_child!(Globals, ZwpTabletPadV2, [
EVT_GROUP_OPCODE => (ZwpTabletPadGroupV2, LateInitObjectKey::new())
]);
}
impl Dispatch<ZwpTabletPadGroupV2, LateInitObjectKey<ZwpTabletPadGroupV2>> for Globals {
fn event(
state: &mut Self,
_: &ZwpTabletPadGroupV2,
event: <ZwpTabletPadGroupV2 as Proxy>::Event,
key: &LateInitObjectKey<ZwpTabletPadGroupV2>,
_: &Connection,
_: &QueueHandle<Self>,
) {
key.push_or_queue_event(state, event);
}
event_created_child!(Globals, ZwpTabletPadGroupV2, [
EVT_RING_OPCODE => (ZwpTabletPadRingV2, LateInitObjectKey::new()),
EVT_STRIP_OPCODE => (ZwpTabletPadStripV2, LateInitObjectKey::new())
]);
}