From 94a60f8a98f7ccabf43ad7fad0ef888a953d0bb2 Mon Sep 17 00:00:00 2001 From: Chris Morgan Date: Sun, 16 Mar 2025 19:14:29 +0530 Subject: [PATCH 01/27] Support most of tablet-v2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Missing: pad support. I’ve stubbed enough that it shouldn’t crash if one is added (I feel like it should be possible to stub less, but I didn’t know enough to do that!), but since I don’t have such a thing to test or benefit from, I’ve skipped it for now. I have the feeling they’re not so common anyway? My tablet has ten buttons, but they come through as keyboard, not pad. And OpenTabletDriver doesn’t look to be able to produce pads. There are still some improvements to make: • I haven’t played with zwp_tablet_tool_v2.set_cursor yet; it may warrant something extra. • I want to add a drawing mode in the example, a trivial sketching app. • I like the metadata and frame accumulators I made in the example. I want to shift them into sctk itself as handlers or whatever, because I think that’s going to be a pretty common desire. • There are some places where I’m passing, say, a wl_seat, because it was done in the code I was basing it off, and I’m not sure if it’s useful or not. Need to whittle the *Data things down, where possible. • I’ve got a few TODOs from things that I just don’t know about. Each and every one should be checked. --- examples/tablet.rs | 741 +++++++++++++++++++++++++++++++++++++ src/seat/mod.rs | 1 + src/seat/tablet/manager.rs | 62 ++++ src/seat/tablet/mod.rs | 62 ++++ src/seat/tablet/pad.rs | 167 +++++++++ src/seat/tablet/seat.rs | 91 +++++ src/seat/tablet/tablet.rs | 110 ++++++ src/seat/tablet/tool.rs | 376 +++++++++++++++++++ 8 files changed, 1610 insertions(+) create mode 100644 examples/tablet.rs create mode 100644 src/seat/tablet/manager.rs create mode 100644 src/seat/tablet/mod.rs create mode 100644 src/seat/tablet/pad.rs create mode 100644 src/seat/tablet/seat.rs create mode 100644 src/seat/tablet/tablet.rs create mode 100644 src/seat/tablet/tool.rs diff --git a/examples/tablet.rs b/examples/tablet.rs new file mode 100644 index 000000000..a8a16b805 --- /dev/null +++ b/examples/tablet.rs @@ -0,0 +1,741 @@ +//! An example demonstrating tablets + +use std::collections::{HashSet, HashMap}; + +use smithay_client_toolkit::{ + compositor::{CompositorHandler, CompositorState}, + delegate_compositor, delegate_output, delegate_pointer, + delegate_registry, delegate_tablet, delegate_seat, delegate_shm, delegate_xdg_shell, + delegate_xdg_window, + output::{OutputHandler, OutputState}, + registry::{ProvidesRegistryState, RegistryState}, + registry_handlers, + seat::{ + pointer::{PointerEvent, PointerEventKind, PointerHandler}, + tablet::{ + ToolType, + ToolCapability, + TabletState, + TabletSeatHandler, + TabletEvent, TabletEventList, TabletHandler, + TabletToolInitEvent, TabletToolInitEventList, + TabletToolEventFrame, + TabletToolEvent, TabletToolHandler, + }, + Capability, SeatHandler, SeatState, + }, + shell::{ + xdg::{ + window::{Window, WindowConfigure, WindowDecorations, WindowHandler}, + XdgShell, + }, + WaylandSurface, + }, + shm::{slot::SlotPool, Shm, ShmHandler}, +}; +use wayland_client::{ + globals::registry_queue_init, + protocol::{wl_output, wl_pointer, wl_region, wl_seat, wl_shm, wl_surface}, + Connection, Dispatch, QueueHandle, + Proxy, +}; +use wayland_protocols::wp::tablet::zv2::client::{ + zwp_tablet_seat_v2::ZwpTabletSeatV2, + zwp_tablet_tool_v2::ZwpTabletToolV2, + zwp_tablet_v2::ZwpTabletV2, + // zwp_tablet_pad_v2::ZwpTabletPadV2, + // zwp_tablet_pad_group_v2::ZwpTabletPadGroupV2, + // zwp_tablet_pad_ring_v2::ZwpTabletPadRingV2, + // zwp_tablet_pad_strip_v2::ZwpTabletPadStripV2, +}; + +const RED: raqote::SolidSource = raqote::SolidSource { r: 221, g: 0, b: 0, a: 255 }; +const GREEN: raqote::SolidSource = raqote::SolidSource { r: 0, g: 170, b: 0, a: 255 }; +const SOLID_RED: raqote::Source = raqote::Source::Solid(RED); +const SOLID_GREEN: raqote::Source = raqote::Source::Solid(GREEN); + +const WHITE: raqote::SolidSource = raqote::SolidSource { r: 255, g: 255, b: 255, a: 255 }; +const BLACK: raqote::SolidSource = raqote::SolidSource { r: 0, g: 0, b: 0, a: 255 }; +const SOLID_BLACK: raqote::Source = raqote::Source::Solid(BLACK); + +fn main() { + env_logger::init(); + + let conn = Connection::connect_to_env().unwrap(); + + let (globals, mut event_queue) = registry_queue_init(&conn).unwrap(); + let qh = event_queue.handle(); + + let font = font_kit::source::SystemSource::new() + .select_best_match( + &[font_kit::family_name::FamilyName::SansSerif], + &font_kit::properties::Properties::new(), + ) + .unwrap() + .load() + .unwrap(); + + let mut simple_window = SimpleWindow { + registry_state: RegistryState::new(&globals), + seat_state: SeatState::new(&globals, &qh), + output_state: OutputState::new(&globals, &qh), + compositor_state: CompositorState::bind(&globals, &qh) + .expect("wl_compositor not available"), + shm_state: Shm::bind(&globals, &qh).expect("wl_shm not available"), + xdg_shell_state: XdgShell::bind(&globals, &qh).expect("xdg shell not available"), + tablet_state: TabletState::bind(&globals, &qh), + + exit: false, + width: 256, + height: 256, + window: None, + tablet_seat: None, + tablets: HashMap::new(), + tools: HashMap::new(), + font, + }; + + let surface = simple_window.compositor_state.create_surface(&qh); + + let window = + simple_window.xdg_shell_state.create_window(surface, WindowDecorations::ServerDefault, &qh); + + window.set_title("A wayland window"); + window.set_app_id("io.github.smithay.client-toolkit.Tablet"); + window.set_min_size(Some((256, 256))); + + window.commit(); + + simple_window.window = Some(window); + + while !simple_window.exit { + event_queue.blocking_dispatch(&mut simple_window).unwrap(); + } +} + +#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] +struct TabletMetadata { + /// The descriptive name of the tablet device. + name: Option, + /// The USB vendor and product IDs for the tablet device. + id: Option<(u32, u32)>, + /// System-specific device paths for the tablet. + /// + /// Path format is unspecified. + /// Clients must figure out what to do with them, if they care. + paths: Vec, +} + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +struct Button { + serial: u32, + button: u32, +} + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] +struct TabletToolCapabilities { + tilt: bool, + pressure: bool, + distance: bool, + rotation: bool, + slider: bool, + wheel: bool, +} + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] +struct TabletToolInfo { + tool_type: ToolType, + hardware_serial: Option<(u32, u32)>, + hardware_id_wacom: Option<(u32, u32)>, + capabilities: TabletToolCapabilities, +} + +/// The current state of the tool. +/// +/// This covers everything, and may, for some applications, +/// be the most practical way of perceiving it; +/// but button is more likely to be desired as events, +/// and wheel is fundamentally a delta thing, +/// so for at least them you probably want to consume the events. +/// +/// Also you won’t get the last frame’s time, if you view it this way, +/// as a proximity_out event deletes the state. +#[derive(Debug)] +struct TabletToolState { + // ProximityIn + serial: u32, + tablet: ZwpTabletV2, + surface: wl_surface::WlSurface, + // Down (cleared on Up), stores serial + down: Option, + // Motion + x: f64, + y: f64, + // Pressure + pressure: u16, + // Distance + distance: u16, + // Tilt + tilt_x: f64, + tilt_y: f64, + // Rotation + rotation_degrees: f64, + // Slider + slider_position: i32, + // Wheel + wheel_degrees: f64, + wheel_clicks: i32, + // Button + buttons: HashSet