diff --git a/docs/config.md b/docs/config.md index 1173a5f9..f4afe007 100644 --- a/docs/config.md +++ b/docs/config.md @@ -474,6 +474,22 @@ You can use the `configure-input` action to change these settings at runtime. See the specification for more details. +### Mapping Tablets to Outputs + +You can map tablets to outputs using the `output` property: + +```toml +[[outputs]] +name = "left" +match.serial-number = "33K03894SL0" + +[[inputs]] +match.name = "Wacom Bamboo Comic 2FG Pen" +output.name = "left" +``` + +See the specification for more details. + # Theming You can configure the colors, sizes, and fonts used by the compositor with the top-level `theme` table. diff --git a/docs/features.md b/docs/features.md index b49073b5..3d1feb0e 100644 --- a/docs/features.md +++ b/docs/features.md @@ -165,6 +165,7 @@ Jay supports the following wayland protocols: | zwp_pointer_gestures_v1 | 3 | | | zwp_primary_selection_device_manager_v1 | 1 | | | zwp_relative_pointer_manager_v1 | 1 | | +| zwp_tablet_manager_v2 | 1 | | | zwp_text_input_manager_v3 | 1 | | | zwp_virtual_keyboard_manager_v1 | 1 | Yes | | zxdg_decoration_manager_v1 | 1 | | @@ -182,5 +183,4 @@ The following features are currently not supported but might get implemented in - Fine-grained damage tracking. - Touch support. -- Tablet support. - Tearing updates of fullscreen games. diff --git a/release-notes.md b/release-notes.md index 3d42166d..077b98d1 100644 --- a/release-notes.md +++ b/release-notes.md @@ -7,6 +7,7 @@ - Focus-follows-mouse can now be disabled. - Add support for pointer-gestures-unstable-v1. - Configs can now handle switch events (laptop lid closed/opened). +- Add support for tablet-v2. # 1.1.0 (2024-04-22) diff --git a/src/backend.rs b/src/backend.rs index f1ded7d8..0a830ac8 100644 --- a/src/backend.rs +++ b/src/backend.rs @@ -4,7 +4,14 @@ use { drm_feedback::DrmFeedback, fixed::Fixed, gfx_api::{GfxFramebuffer, SyncFile}, - ifs::wl_seat::wl_pointer::{CONTINUOUS, FINGER, HORIZONTAL_SCROLL, VERTICAL_SCROLL, WHEEL}, + ifs::wl_seat::{ + tablet::{ + PadButtonState, TabletInit, TabletPadId, TabletPadInit, TabletRingEventSource, + TabletStripEventSource, TabletToolChanges, TabletToolId, TabletToolInit, + ToolButtonState, + }, + wl_pointer::{CONTINUOUS, FINGER, HORIZONTAL_SCROLL, VERTICAL_SCROLL, WHEEL}, + }, libinput::consts::DeviceCapability, video::drm::{ConnectorType, DrmConnector, DrmError, DrmVersion}, }, @@ -122,6 +129,8 @@ pub trait HardwareCursor: Debug { pub type TransformMatrix = [[f64; 2]; 2]; +linear_ids!(InputDeviceGroupIds, InputDeviceGroupId, usize); + pub trait InputDevice { fn id(&self) -> InputDeviceId; fn removed(&self) -> bool; @@ -165,6 +174,12 @@ pub trait InputDevice { None } fn set_natural_scrolling_enabled(&self, enabled: bool); + fn tablet_info(&self) -> Option> { + None + } + fn tablet_pad_info(&self) -> Option> { + None + } } #[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)] @@ -317,6 +332,53 @@ pub enum InputEvent { time_usec: u64, event: SwitchEvent, }, + + TabletToolAdded { + time_usec: u64, + init: Box, + }, + TabletToolChanged { + time_usec: u64, + id: TabletToolId, + changes: Box, + }, + TabletToolButton { + time_usec: u64, + id: TabletToolId, + button: u32, + state: ToolButtonState, + }, + TabletToolRemoved { + time_usec: u64, + id: TabletToolId, + }, + + TabletPadButton { + time_usec: u64, + id: TabletPadId, + button: u32, + state: PadButtonState, + }, + TabletPadModeSwitch { + time_usec: u64, + pad: TabletPadId, + group: u32, + mode: u32, + }, + TabletPadRing { + time_usec: u64, + pad: TabletPadId, + ring: u32, + source: Option, + angle: Option, + }, + TabletPadStrip { + time_usec: u64, + pad: TabletPadId, + strip: u32, + source: Option, + position: Option, + }, } pub enum DrmEvent { diff --git a/src/backends/metal.rs b/src/backends/metal.rs index fb76db20..832f9925 100644 --- a/src/backends/metal.rs +++ b/src/backends/metal.rs @@ -6,19 +6,23 @@ use { crate::{ async_engine::SpawnedFuture, backend::{ - Backend, InputDevice, InputDeviceAccelProfile, InputDeviceCapability, InputDeviceId, - InputEvent, KeyState, TransformMatrix, + Backend, InputDevice, InputDeviceAccelProfile, InputDeviceCapability, + InputDeviceGroupId, InputDeviceId, InputEvent, KeyState, TransformMatrix, }, backends::metal::video::{MetalDrmDeviceData, MetalRenderContext, PendingDrmDevice}, dbus::{DbusError, SignalHandler}, drm_feedback::DrmFeedback, gfx_api::GfxError, + ifs::wl_seat::tablet::{ + TabletId, TabletInit, TabletPadGroupInit, TabletPadId, TabletPadInit, + }, libinput::{ consts::{ AccelProfile, LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE, - LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT, + LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT, LIBINPUT_DEVICE_CAP_TABLET_PAD, + LIBINPUT_DEVICE_CAP_TABLET_TOOL, }, - device::RegisteredDevice, + device::{LibInputDevice, RegisteredDevice}, LibInput, LibInputAdapter, LibInputError, }, logind::{LogindError, Session}, @@ -39,6 +43,7 @@ use { gbm::GbmError, }, }, + bstr::ByteSlice, std::{ any::Any, cell::{Cell, RefCell}, @@ -286,6 +291,7 @@ pub async fn create(state: &Rc) -> Result, MetalError> { } struct MetalInputDevice { + state: Rc, slot: usize, id: InputDeviceId, devnum: c::dev_t, @@ -293,11 +299,14 @@ struct MetalInputDevice { inputdev: CloneCell>>, devnode: CString, _sysname: CString, + syspath: CString, removed: Cell, events: SyncQueue, cb: CloneCell>>, name: CloneCell>, transform_matrix: Cell>, + tablet_id: Cell>, + tablet_pad_id: Cell>, // state pressed_keys: SmallMap, @@ -597,6 +606,71 @@ impl InputDevice for MetalInputDevice { fn natural_scrolling_enabled(&self) -> Option { self.effective.natural_scrolling_enabled.get() } + + fn tablet_info(&self) -> Option> { + let dev = self.inputdev.get()?; + let dev = dev.device(); + if !dev.has_cap(LIBINPUT_DEVICE_CAP_TABLET_TOOL) { + return None; + } + let id = match self.tablet_id.get() { + Some(id) => id, + None => { + let id = self.state.tablet_ids.next(); + self.tablet_id.set(Some(id)); + id + } + }; + Some(Box::new(TabletInit { + id, + group: self.get_device_group(&dev), + name: dev.name(), + pid: dev.product(), + vid: dev.vendor(), + path: self.syspath.as_bytes().as_bstr().to_string(), + })) + } + + fn tablet_pad_info(&self) -> Option> { + let dev = self.inputdev.get()?; + let dev = dev.device(); + if !dev.has_cap(LIBINPUT_DEVICE_CAP_TABLET_PAD) { + return None; + } + let id = match self.tablet_pad_id.get() { + Some(id) => id, + None => { + let id = self.state.tablet_pad_ids.next(); + self.tablet_pad_id.set(Some(id)); + id + } + }; + let buttons = dev.pad_num_buttons(); + let strips = dev.pad_num_strips(); + let rings = dev.pad_num_rings(); + let mut groups = vec![]; + for n in 0..dev.pad_num_mode_groups() { + let Some(group) = dev.pad_mode_group(n) else { + break; + }; + groups.push(TabletPadGroupInit { + buttons: (0..buttons).filter(|b| group.has_button(*b)).collect(), + rings: (0..rings).filter(|b| group.has_ring(*b)).collect(), + strips: (0..strips).filter(|b| group.has_strip(*b)).collect(), + modes: group.num_modes(), + mode: group.mode(), + }); + } + Some(Box::new(TabletPadInit { + id, + group: self.get_device_group(&dev), + path: self.syspath.as_bytes().as_bstr().to_string(), + buttons, + strips, + rings, + groups, + })) + } } impl MetalInputDevice { @@ -606,4 +680,14 @@ impl MetalInputDevice { cb(); } } + + fn get_device_group(&self, dev: &LibInputDevice) -> InputDeviceGroupId { + let group = dev.device_group(); + let mut id = group.user_data(); + if id == 0 { + id = self.state.input_device_group_ids.next().raw(); + group.set_user_data(id); + } + InputDeviceGroupId::from_raw(id) + } } diff --git a/src/backends/metal/input.rs b/src/backends/metal/input.rs index c64f683c..c9788993 100644 --- a/src/backends/metal/input.rs +++ b/src/backends/metal/input.rs @@ -3,14 +3,25 @@ use { backend::{AxisSource, InputEvent, KeyState, ScrollAxis}, backends::metal::MetalBackend, fixed::Fixed, + ifs::wl_seat::tablet::{ + PadButtonState, TabletRingEventSource, TabletStripEventSource, TabletTool2dChange, + TabletToolCapability, TabletToolChanges, TabletToolId, TabletToolInit, + TabletToolPositionChange, TabletToolType, TabletToolWheelChange, ToolButtonState, + }, libinput::{ consts::{ - LIBINPUT_BUTTON_STATE_PRESSED, LIBINPUT_KEY_STATE_PRESSED, - LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL, - LIBINPUT_SWITCH_LID, LIBINPUT_SWITCH_STATE_OFF, LIBINPUT_SWITCH_STATE_ON, - LIBINPUT_SWITCH_TABLET_MODE, + LIBINPUT_BUTTON_STATE_PRESSED, LIBINPUT_BUTTON_STATE_RELEASED, + LIBINPUT_KEY_STATE_PRESSED, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL, + LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL, LIBINPUT_SWITCH_LID, + LIBINPUT_SWITCH_STATE_OFF, LIBINPUT_SWITCH_STATE_ON, LIBINPUT_SWITCH_TABLET_MODE, + LIBINPUT_TABLET_PAD_RING_SOURCE_FINGER, LIBINPUT_TABLET_PAD_STRIP_SOURCE_FINGER, + LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN, LIBINPUT_TABLET_TOOL_TIP_DOWN, + LIBINPUT_TABLET_TOOL_TIP_UP, LIBINPUT_TABLET_TOOL_TYPE_AIRBRUSH, + LIBINPUT_TABLET_TOOL_TYPE_BRUSH, LIBINPUT_TABLET_TOOL_TYPE_ERASER, + LIBINPUT_TABLET_TOOL_TYPE_LENS, LIBINPUT_TABLET_TOOL_TYPE_MOUSE, + LIBINPUT_TABLET_TOOL_TYPE_PEN, LIBINPUT_TABLET_TOOL_TYPE_PENCIL, }, - event::LibInputEvent, + event::{LibInputEvent, LibInputEventTabletTool}, }, utils::{bitflags::BitflagsExt, errorfmt::ErrorFmt}, }, @@ -103,6 +114,13 @@ impl MetalBackend { c::LIBINPUT_EVENT_GESTURE_HOLD_BEGIN => self.handle_gesture_hold_begin(event), c::LIBINPUT_EVENT_GESTURE_HOLD_END => self.handle_gesture_hold_end(event), c::LIBINPUT_EVENT_SWITCH_TOGGLE => self.handle_switch_toggle(event), + c::LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY => self.handle_tablet_tool_proximity(event), + c::LIBINPUT_EVENT_TABLET_TOOL_AXIS => self.handle_tablet_tool_axis(event), + c::LIBINPUT_EVENT_TABLET_TOOL_BUTTON => self.handle_tablet_tool_button(event), + c::LIBINPUT_EVENT_TABLET_TOOL_TIP => self.handle_tablet_tool_tip(event), + c::LIBINPUT_EVENT_TABLET_PAD_BUTTON => self.handle_tablet_pad_button(event), + c::LIBINPUT_EVENT_TABLET_PAD_RING => self.handle_tablet_pad_ring(event), + c::LIBINPUT_EVENT_TABLET_PAD_STRIP => self.handle_tablet_pad_strip(event), _ => {} } } @@ -320,4 +338,211 @@ impl MetalBackend { event: switch_event, }); } + + fn get_tool_id(&self, event: &LibInputEventTabletTool) -> TabletToolId { + let tool = event.tool(); + let mut user_data = tool.user_data(); + if user_data == 0 { + user_data = self.state.tablet_tool_ids.next().raw(); + tool.set_user_data(user_data); + } + TabletToolId::from_raw(user_data) + } + + fn build_tablet_tool_changed( + &self, + event: &LibInputEventTabletTool, + down: Option, + ) -> InputEvent { + let mut changes = Box::::default(); + changes.down = down; + if event.x_has_changed() || event.y_has_changed() { + changes.pos = Some(TabletTool2dChange { + x: TabletToolPositionChange { + x: event.x_transformed(1), + dx: event.dx(), + }, + y: TabletToolPositionChange { + x: event.y_transformed(1), + dx: event.dy(), + }, + }) + } + if event.pressure_has_changed() { + changes.pressure = Some(event.pressure()); + } + if event.distance_has_changed() { + changes.distance = Some(event.distance()); + } + if event.tilt_x_has_changed() || event.tilt_y_has_changed() { + changes.tilt = Some(TabletTool2dChange { + x: event.tilt_x(), + y: event.tilt_y(), + }); + } + if event.rotation_has_changed() { + changes.rotation = Some(event.rotation()); + } + if event.slider_has_changed() { + changes.slider = Some(event.slider_position()); + } + if event.wheel_has_changed() { + changes.wheel = Some(TabletToolWheelChange { + degrees: event.wheel_delta(), + clicks: event.wheel_delta_discrete(), + }); + } + InputEvent::TabletToolChanged { + time_usec: event.time_usec(), + id: self.get_tool_id(event), + changes, + } + } + + fn handle_tablet_tool_proximity(self: &Rc, event: LibInputEvent) { + let (event, dev) = unpack!(self, event, tablet_tool_event); + let id = self.get_tool_id(&event); + if event.proximity_state() == LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN { + let Some(tablet_id) = dev.tablet_id.get() else { + return; + }; + let tool = event.tool(); + dev.event(InputEvent::TabletToolAdded { + time_usec: event.time_usec(), + init: Box::new(TabletToolInit { + tablet_id, + id, + type_: match tool.type_() { + LIBINPUT_TABLET_TOOL_TYPE_PEN => TabletToolType::Pen, + LIBINPUT_TABLET_TOOL_TYPE_ERASER => TabletToolType::Eraser, + LIBINPUT_TABLET_TOOL_TYPE_BRUSH => TabletToolType::Brush, + LIBINPUT_TABLET_TOOL_TYPE_PENCIL => TabletToolType::Pencil, + LIBINPUT_TABLET_TOOL_TYPE_AIRBRUSH => TabletToolType::Airbrush, + LIBINPUT_TABLET_TOOL_TYPE_MOUSE => TabletToolType::Mouse, + LIBINPUT_TABLET_TOOL_TYPE_LENS => TabletToolType::Lens, + _ => return, + }, + hardware_serial: tool.serial(), + hardware_id_wacom: tool.tool_id(), + capabilities: { + let mut caps = vec![]; + macro_rules! add_cap { + ($f:ident, $cap:ident) => { + if tool.$f() { + caps.push(TabletToolCapability::$cap); + } + }; + } + add_cap!(has_tilt, Tilt); + add_cap!(has_pressure, Pressure); + add_cap!(has_distance, Distance); + add_cap!(has_rotation, Rotation); + add_cap!(has_slider, Slider); + add_cap!(has_wheel, Wheel); + caps + }, + }), + }); + dev.event(self.build_tablet_tool_changed(&event, None)); + } else { + dev.event(InputEvent::TabletToolRemoved { + time_usec: event.time_usec(), + id, + }); + } + } + + fn handle_tablet_tool_tip(self: &Rc, event: LibInputEvent) { + let (event, dev) = unpack!(self, event, tablet_tool_event); + let down = match event.tip_state() { + LIBINPUT_TABLET_TOOL_TIP_UP => false, + LIBINPUT_TABLET_TOOL_TIP_DOWN => true, + _ => return, + }; + dev.event(self.build_tablet_tool_changed(&event, Some(down))); + } + + fn handle_tablet_tool_axis(self: &Rc, event: LibInputEvent) { + let (event, dev) = unpack!(self, event, tablet_tool_event); + dev.event(self.build_tablet_tool_changed(&event, None)); + } + + fn handle_tablet_tool_button(self: &Rc, event: LibInputEvent) { + let (event, dev) = unpack!(self, event, tablet_tool_event); + dev.event(InputEvent::TabletToolButton { + time_usec: event.time_usec(), + id: self.get_tool_id(&event), + button: event.button(), + state: match event.button_state() { + LIBINPUT_BUTTON_STATE_RELEASED => ToolButtonState::Released, + LIBINPUT_BUTTON_STATE_PRESSED => ToolButtonState::Pressed, + _ => return, + }, + }); + } + + fn handle_tablet_pad_button(self: &Rc, event: LibInputEvent) { + let (event, dev) = unpack!(self, event, tablet_pad_event); + let id = match dev.tablet_pad_id.get() { + None => return, + Some(id) => id, + }; + let state = match event.button_state() { + LIBINPUT_BUTTON_STATE_RELEASED => PadButtonState::Released, + LIBINPUT_BUTTON_STATE_PRESSED => PadButtonState::Pressed, + _ => return, + }; + dev.event(InputEvent::TabletPadModeSwitch { + time_usec: event.time_usec(), + pad: id, + group: event.mode_group().index(), + mode: event.mode(), + }); + dev.event(InputEvent::TabletPadButton { + time_usec: event.time_usec(), + id, + button: event.button_number(), + state, + }); + } + + fn handle_tablet_pad_ring(self: &Rc, event: LibInputEvent) { + let (event, dev) = unpack!(self, event, tablet_pad_event); + dev.event(InputEvent::TabletPadRing { + time_usec: event.time_usec(), + pad: match dev.tablet_pad_id.get() { + None => return, + Some(id) => id, + }, + ring: event.ring_number(), + source: match event.ring_source() { + LIBINPUT_TABLET_PAD_RING_SOURCE_FINGER => Some(TabletRingEventSource::Finger), + _ => None, + }, + angle: match event.ring_position() { + n if n == -1.0 => None, + n => Some(n), + }, + }); + } + + fn handle_tablet_pad_strip(self: &Rc, event: LibInputEvent) { + let (event, dev) = unpack!(self, event, tablet_pad_event); + dev.event(InputEvent::TabletPadStrip { + time_usec: event.time_usec(), + pad: match dev.tablet_pad_id.get() { + None => return, + Some(id) => id, + }, + strip: event.strip_number(), + source: match event.strip_source() { + LIBINPUT_TABLET_PAD_STRIP_SOURCE_FINGER => Some(TabletStripEventSource::Finger), + _ => None, + }, + position: match event.strip_position() { + n if n == -1.0 => None, + n => Some(n), + }, + }); + } } diff --git a/src/backends/metal/monitor.rs b/src/backends/metal/monitor.rs index acfa51e8..c9baf699 100644 --- a/src/backends/metal/monitor.rs +++ b/src/backends/metal/monitor.rs @@ -293,6 +293,7 @@ impl MetalBackend { let devnum = dev.devnum(); let devnode = dev.devnode()?; let sysname = dev.sysname()?; + let syspath = dev.syspath()?; log::info!("Device added: {}", devnode.to_bytes().as_bstr()); let mut slots = self.device_holder.input_devices.borrow_mut(); let slot = 'slot: { @@ -305,6 +306,7 @@ impl MetalBackend { slots.len() - 1 }; let dev = Rc::new(MetalInputDevice { + state: self.state.clone(), slot, id: device_id, devnum, @@ -312,6 +314,7 @@ impl MetalBackend { inputdev: Default::default(), devnode: devnode.to_owned(), _sysname: sysname.to_owned(), + syspath: syspath.to_owned(), removed: Cell::new(false), events: Default::default(), cb: Default::default(), @@ -321,6 +324,8 @@ impl MetalBackend { desired: Default::default(), transform_matrix: Default::default(), effective: Default::default(), + tablet_id: Default::default(), + tablet_pad_id: Default::default(), }); slots[slot] = Some(dev.clone()); self.device_holder diff --git a/src/cli/seat_test.rs b/src/cli/seat_test.rs index 7456ab37..cdc8422e 100644 --- a/src/cli/seat_test.rs +++ b/src/cli/seat_test.rs @@ -1,6 +1,7 @@ use { crate::{ cli::{GlobalArgs, SeatTestArgs}, + fixed::Fixed, ifs::wl_seat::wl_pointer::{PendingScroll, CONTINUOUS, FINGER, WHEEL}, tools::tool_client::{with_tool_client, Handle, ToolClient}, wire::{ @@ -8,7 +9,13 @@ use { jay_seat_events::{ Axis120, AxisFrame, AxisInverted, AxisPx, AxisSource, AxisStop, Button, HoldBegin, HoldEnd, Key, Modifiers, PinchBegin, PinchEnd, PinchUpdate, PointerAbs, PointerRel, - SwipeBegin, SwipeEnd, SwipeUpdate, SwitchEvent, + SwipeBegin, SwipeEnd, SwipeUpdate, SwitchEvent, TabletPadButton, + TabletPadModeSwitch, TabletPadRingAngle, TabletPadRingFrame, TabletPadRingSource, + TabletPadRingStop, TabletPadStripFrame, TabletPadStripPosition, + TabletPadStripSource, TabletPadStripStop, TabletToolButton, TabletToolDistance, + TabletToolDown, TabletToolFrame, TabletToolMotion, TabletToolPressure, + TabletToolProximityIn, TabletToolProximityOut, TabletToolRotation, + TabletToolSlider, TabletToolTilt, TabletToolUp, TabletToolWheel, }, }, }, @@ -42,6 +49,36 @@ impl SeatTest { } } +#[derive(Default, Debug, Copy, Clone)] +pub struct PendingTabletTool { + proximity_in: bool, + proximity_out: bool, + down: bool, + up: bool, + pos: Option<(Fixed, Fixed)>, + pressure: Option, + distance: Option, + tilt: Option<(f64, f64)>, + rotation: Option, + slider: Option, + wheel: Option<(f64, i32)>, + button: Option<(u32, u32)>, +} + +#[derive(Default, Debug, Copy, Clone)] +pub struct PendingTabletPadStrip { + source: u32, + pos: Option, + stop: bool, +} + +#[derive(Default, Debug, Copy, Clone)] +pub struct PendingTabletPadRing { + source: u32, + degrees: Option, + stop: bool, +} + async fn run(seat_test: Rc) { let tc = &seat_test.tc; let comp = tc.jay_compositor().await; @@ -344,6 +381,208 @@ async fn run(seat_test: Rc) { ); } }); + let tt = Rc::new(RefCell::new(PendingTabletTool::default())); + TabletToolProximityIn::handle(tc, se, tt.clone(), move |tt, _| { + tt.borrow_mut().proximity_in = true; + }); + TabletToolProximityOut::handle(tc, se, tt.clone(), move |tt, _| { + tt.borrow_mut().proximity_out = true; + }); + TabletToolDown::handle(tc, se, tt.clone(), move |tt, _| { + tt.borrow_mut().down = true; + }); + TabletToolUp::handle(tc, se, tt.clone(), move |tt, _| { + tt.borrow_mut().up = true; + }); + TabletToolMotion::handle(tc, se, tt.clone(), move |tt, ev| { + tt.borrow_mut().pos = Some((ev.x, ev.y)); + }); + TabletToolPressure::handle(tc, se, tt.clone(), move |tt, ev| { + tt.borrow_mut().pressure = Some(ev.pressure); + }); + TabletToolDistance::handle(tc, se, tt.clone(), move |tt, ev| { + tt.borrow_mut().distance = Some(ev.distance); + }); + TabletToolTilt::handle(tc, se, tt.clone(), move |tt, ev| { + tt.borrow_mut().tilt = Some((ev.tilt_x, ev.tilt_y)); + }); + TabletToolRotation::handle(tc, se, tt.clone(), move |tt, ev| { + tt.borrow_mut().rotation = Some(ev.degrees); + }); + TabletToolSlider::handle(tc, se, tt.clone(), move |tt, ev| { + tt.borrow_mut().slider = Some(ev.position); + }); + TabletToolWheel::handle(tc, se, tt.clone(), move |tt, ev| { + tt.borrow_mut().wheel = Some((ev.degrees, ev.clicks)); + }); + TabletToolButton::handle(tc, se, tt.clone(), move |tt, ev| { + tt.borrow_mut().button = Some((ev.button, ev.state)); + }); + let st = seat_test.clone(); + TabletToolFrame::handle(tc, se, tt.clone(), move |tt, ev| { + let tt = tt.take(); + if !all && ev.seat != seat { + return; + } + if all { + print!("Seat: {}, ", st.name(ev.seat)); + } + print!( + "Time: {:.4}, Device: {}, Tool: {}", + time(ev.time_usec), + ev.input_device, + ev.tool, + ); + if tt.proximity_in { + print!(", proximity in"); + } + if tt.proximity_out { + print!(", proximity out"); + } + if tt.down { + print!(", down"); + } + if tt.up { + print!(", up"); + } + if let Some((x, y)) = tt.pos { + print!(", pos: {x}x{y}"); + } + if let Some(val) = tt.pressure { + print!(", pressure: {val}"); + } + if let Some(val) = tt.distance { + print!(", distance: {val}"); + } + if let Some((x, y)) = tt.tilt { + print!(", tilt: {x}x{y}"); + } + if let Some(val) = tt.rotation { + print!(", rotation: {val}"); + } + if let Some(val) = tt.slider { + print!(", slider: {val}"); + } + if let Some((degrees, clicks)) = tt.wheel { + print!(", wheel degrees: {degrees}, wheel clicks: {clicks}"); + } + if let Some((button, state)) = tt.button { + let dir = match state { + 0 => "up", + _ => "down", + }; + print!(", button {button} {dir}"); + } + println!(); + }); + let st = seat_test.clone(); + TabletPadModeSwitch::handle(tc, se, (), move |_, ev| { + if all || ev.seat == seat { + if all { + print!("Seat: {}, ", st.name(ev.seat)); + } + println!( + "Time: {:.4}, Device: {}, mode switch: {}", + time(ev.time_usec), + ev.input_device, + ev.mode, + ); + } + }); + let st = seat_test.clone(); + TabletPadButton::handle(tc, se, (), move |_, ev| { + if all || ev.seat == seat { + if all { + print!("Seat: {}, ", st.name(ev.seat)); + } + let dir = match ev.state { + 0 => "up", + _ => "down", + }; + println!( + "Time: {:.4}, Device: {}, Button {} {dir}", + time(ev.time_usec), + ev.input_device, + ev.button, + ); + } + }); + let tt = Rc::new(RefCell::new(PendingTabletPadStrip::default())); + TabletPadStripSource::handle(tc, se, tt.clone(), move |tt, ev| { + tt.borrow_mut().source = ev.source; + }); + TabletPadStripPosition::handle(tc, se, tt.clone(), move |tt, ev| { + tt.borrow_mut().pos = Some(ev.position); + }); + TabletPadStripStop::handle(tc, se, tt.clone(), move |tt, _| { + tt.borrow_mut().stop = true; + }); + let st = seat_test.clone(); + TabletPadStripFrame::handle(tc, se, tt.clone(), move |tt, ev| { + let tt = tt.take(); + if !all && ev.seat != seat { + return; + } + if all { + print!("Seat: {}, ", st.name(ev.seat)); + } + print!( + "Time: {:.4}, Device: {}, Strip: {}", + time(ev.time_usec), + ev.input_device, + ev.strip, + ); + let source = match tt.source { + 1 => "finger", + _ => "unknown", + }; + print!(", source: {source}"); + if let Some(pos) = tt.pos { + print!(", pos: {pos}"); + } + if tt.stop { + print!(", stop"); + } + println!(); + }); + let tt = Rc::new(RefCell::new(PendingTabletPadRing::default())); + TabletPadRingSource::handle(tc, se, tt.clone(), move |tt, ev| { + tt.borrow_mut().source = ev.source; + }); + TabletPadRingAngle::handle(tc, se, tt.clone(), move |tt, ev| { + tt.borrow_mut().degrees = Some(ev.degrees); + }); + TabletPadRingStop::handle(tc, se, tt.clone(), move |tt, _| { + tt.borrow_mut().stop = true; + }); + let st = seat_test.clone(); + TabletPadRingFrame::handle(tc, se, tt.clone(), move |tt, ev| { + let tt = tt.take(); + if !all && ev.seat != seat { + return; + } + if all { + print!("Seat: {}, ", st.name(ev.seat)); + } + print!( + "Time: {:.4}, Device: {}, Ring: {}", + time(ev.time_usec), + ev.input_device, + ev.ring, + ); + let source = match tt.source { + 1 => "finger", + _ => "unknown", + }; + print!(", source: {source}"); + if let Some(val) = tt.degrees { + print!(", degrees: {val}"); + } + if tt.stop { + print!(", stop"); + } + println!(); + }); pending::<()>().await; } diff --git a/src/client.rs b/src/client.rs index ed64086d..d964bbd9 100644 --- a/src/client.rs +++ b/src/client.rs @@ -17,6 +17,7 @@ use { copyhashmap::{CopyHashMap, Locked}, errorfmt::ErrorFmt, numcell::NumCell, + pending_serial::PendingSerial, trim::AsciiTrim, }, wire::WlRegistryId, @@ -350,6 +351,10 @@ impl Client { self.state.next_serial(Some(self)) } + pub fn pending_serial(&self) -> PendingSerial<'_> { + PendingSerial::new(self) + } + pub fn new_id>(&self) -> Result { self.objects.id(self) } diff --git a/src/client/objects.rs b/src/client/objects.rs index 7309ee1f..4616e014 100644 --- a/src/client/objects.rs +++ b/src/client/objects.rs @@ -15,7 +15,7 @@ use { wl_output::WlOutput, wl_region::WlRegion, wl_registry::WlRegistry, - wl_seat::{wl_pointer::WlPointer, WlSeat}, + wl_seat::{tablet::zwp_tablet_tool_v2::ZwpTabletToolV2, wl_pointer::WlPointer, WlSeat}, wl_surface::{ xdg_surface::{xdg_toplevel::XdgToplevel, XdgSurface}, WlSurface, @@ -35,7 +35,7 @@ use { WlDataSourceId, WlOutputId, WlPointerId, WlRegionId, WlRegistryId, WlSeatId, WlSurfaceId, WpDrmLeaseConnectorV1Id, WpLinuxDrmSyncobjTimelineV1Id, XdgPositionerId, XdgSurfaceId, XdgToplevelId, XdgWmBaseId, ZwlrDataControlSourceV1Id, - ZwpPrimarySelectionSourceV1Id, + ZwpPrimarySelectionSourceV1Id, ZwpTabletToolV2Id, }, }, std::{cell::RefCell, mem, rc::Rc}, @@ -65,6 +65,7 @@ pub struct Objects { pub zwlr_data_sources: CopyHashMap>, pub jay_toplevels: CopyHashMap>, pub drm_lease_outputs: CopyHashMap>, + pub tablet_tools: CopyHashMap>, ids: RefCell>, } @@ -96,6 +97,7 @@ impl Objects { zwlr_data_sources: Default::default(), jay_toplevels: Default::default(), drm_lease_outputs: Default::default(), + tablet_tools: Default::default(), ids: RefCell::new(vec![]), } } @@ -131,6 +133,7 @@ impl Objects { self.zwlr_data_sources.clear(); self.jay_toplevels.clear(); self.drm_lease_outputs.clear(); + self.tablet_tools.clear(); } pub fn id(&self, client_data: &Client) -> Result diff --git a/src/compositor.rs b/src/compositor.rs index ae663ce0..065629a6 100644 --- a/src/compositor.rs +++ b/src/compositor.rs @@ -238,9 +238,12 @@ fn start_compositor2( security_context_acceptors: Default::default(), cursor_user_group_ids: Default::default(), cursor_user_ids: Default::default(), - cursor_users: Default::default(), cursor_user_groups: Default::default(), cursor_user_group_hardware_cursor: Default::default(), + input_device_group_ids: Default::default(), + tablet_ids: Default::default(), + tablet_tool_ids: Default::default(), + tablet_pad_ids: Default::default(), }); state.tracker.register(ClientId::from_raw(0)); create_dummy_output(&state); diff --git a/src/globals.rs b/src/globals.rs index 2989539b..d893a06c 100644 --- a/src/globals.rs +++ b/src/globals.rs @@ -18,6 +18,7 @@ use { wl_registry::WlRegistry, wl_seat::{ ext_transient_seat_manager_v1::ExtTransientSeatManagerV1Global, + tablet::zwp_tablet_manager_v2::ZwpTabletManagerV2Global, text_input::{ zwp_input_method_manager_v2::ZwpInputMethodManagerV2Global, zwp_text_input_manager_v3::ZwpTextInputManagerV3Global, @@ -193,6 +194,7 @@ impl Globals { add_singleton!(XdgWmDialogV1Global); add_singleton!(ExtTransientSeatManagerV1Global); add_singleton!(ZwpPointerGesturesV1Global); + add_singleton!(ZwpTabletManagerV2Global); } pub fn add_backend_singletons(&self, backend: &Rc) { diff --git a/src/ifs/jay_seat_events.rs b/src/ifs/jay_seat_events.rs index e9ddb35d..9df7d121 100644 --- a/src/ifs/jay_seat_events.rs +++ b/src/ifs/jay_seat_events.rs @@ -3,7 +3,14 @@ use { backend::{InputDeviceId, KeyState}, client::Client, fixed::Fixed, - ifs::wl_seat::{wl_pointer::PendingScroll, SeatId}, + ifs::wl_seat::{ + tablet::{ + PadButtonState, TabletRingEventSource, TabletStripEventSource, TabletTool, + TabletToolChanges, TabletToolId, ToolButtonState, + }, + wl_pointer::PendingScroll, + SeatId, + }, leaks::Tracker, object::{Object, Version}, wire::{jay_seat_events::*, JaySeatEventsId}, @@ -236,6 +243,231 @@ impl JaySeatEvents { event: event as _, }); } + + pub fn send_tablet_tool_proximity_in( + &self, + seat: SeatId, + tablet: InputDeviceId, + tool: TabletToolId, + time_usec: u64, + ) { + self.client + .event(TabletToolProximityIn { self_id: self.id }); + self.client.event(TabletToolFrame { + self_id: self.id, + seat: seat.raw(), + time_usec, + input_device: tablet.raw(), + tool: tool.raw() as _, + }); + } + + pub fn send_tablet_tool_proximity_out( + &self, + seat: SeatId, + tablet: InputDeviceId, + tool: TabletToolId, + time_usec: u64, + ) { + self.client + .event(TabletToolProximityOut { self_id: self.id }); + self.client.event(TabletToolFrame { + self_id: self.id, + seat: seat.raw(), + time_usec, + input_device: tablet.raw(), + tool: tool.raw() as _, + }); + } + + pub fn send_tablet_tool_changes( + &self, + seat: SeatId, + tablet: InputDeviceId, + tool: &TabletTool, + time_usec: u64, + changes: &TabletToolChanges, + ) { + let self_id = self.id; + if let Some(down) = changes.down { + match down { + true => self.client.event(TabletToolDown { self_id }), + false => self.client.event(TabletToolUp { self_id }), + } + } + if changes.pos.is_some() { + let (x, y) = tool.cursor().position(); + self.client.event(TabletToolMotion { self_id, x, y }); + } + if let Some(val) = changes.pressure { + self.client.event(TabletToolPressure { + self_id, + pressure: val, + }); + } + if let Some(val) = changes.distance { + self.client.event(TabletToolDistance { + self_id, + distance: val, + }); + } + if let Some(val) = changes.tilt { + self.client.event(TabletToolTilt { + self_id, + tilt_x: val.x, + tilt_y: val.y, + }); + } + if let Some(val) = changes.rotation { + self.client.event(TabletToolRotation { + self_id, + degrees: val, + }); + } + if let Some(val) = changes.slider { + self.client.event(TabletToolSlider { + self_id, + position: val, + }); + } + if let Some(val) = changes.wheel { + self.client.event(TabletToolWheel { + self_id, + degrees: val.degrees, + clicks: val.clicks, + }); + } + self.client.event(TabletToolFrame { + self_id: self.id, + seat: seat.raw(), + time_usec, + input_device: tablet.raw(), + tool: tool.id.raw() as _, + }); + } + + pub fn send_tablet_tool_button( + &self, + seat: SeatId, + tablet: InputDeviceId, + tool: &TabletTool, + time_usec: u64, + button: u32, + state: ToolButtonState, + ) { + self.client.event(TabletToolButton { + self_id: self.id, + button, + state: state as _, + }); + self.client.event(TabletToolFrame { + self_id: self.id, + seat: seat.raw(), + time_usec, + input_device: tablet.raw(), + tool: tool.id.raw() as _, + }); + } + + pub fn send_tablet_pad_mode_switch( + &self, + seat: SeatId, + pad: InputDeviceId, + time_usec: u64, + group: u32, + mode: u32, + ) { + self.client.event(TabletPadModeSwitch { + self_id: self.id, + seat: seat.raw(), + time_usec, + input_device: pad.raw(), + group, + mode, + }); + } + + pub fn send_tablet_pad_button( + &self, + seat: SeatId, + pad: InputDeviceId, + time_usec: u64, + button: u32, + state: PadButtonState, + ) { + self.client.event(TabletPadButton { + self_id: self.id, + seat: seat.raw(), + time_usec, + input_device: pad.raw(), + button, + state: state as _, + }); + } + + pub fn send_tablet_pad_strip( + &self, + seat: SeatId, + pad: InputDeviceId, + time_usec: u64, + strip: u32, + source: Option, + position: Option, + ) { + if let Some(source) = source { + self.client.event(TabletPadStripSource { + self_id: self.id, + source: source as _, + }); + } + if let Some(position) = position { + self.client.event(TabletPadStripPosition { + self_id: self.id, + position, + }); + } else { + self.client.event(TabletPadStripStop { self_id: self.id }); + } + self.client.event(TabletPadStripFrame { + self_id: self.id, + seat: seat.raw(), + time_usec, + input_device: pad.raw(), + strip, + }); + } + + pub fn send_tablet_pad_ring( + &self, + seat: SeatId, + pad: InputDeviceId, + time_usec: u64, + ring: u32, + source: Option, + degrees: Option, + ) { + if let Some(source) = source { + self.client.event(TabletPadRingSource { + self_id: self.id, + source: source as _, + }); + } + if let Some(degrees) = degrees { + self.client.event(TabletPadRingAngle { + self_id: self.id, + degrees, + }); + } else { + self.client.event(TabletPadRingStop { self_id: self.id }); + } + self.client.event(TabletPadRingFrame { + self_id: self.id, + seat: seat.raw(), + time_usec, + input_device: pad.raw(), + ring, + }); + } } impl JaySeatEventsRequestHandler for JaySeatEvents { diff --git a/src/ifs/wl_seat.rs b/src/ifs/wl_seat.rs index b79b039f..b3eb8cc7 100644 --- a/src/ifs/wl_seat.rs +++ b/src/ifs/wl_seat.rs @@ -4,6 +4,7 @@ pub mod ext_transient_seat_v1; mod gesture_owner; mod kb_owner; mod pointer_owner; +pub mod tablet; pub mod text_input; pub mod wl_keyboard; pub mod wl_pointer; @@ -46,6 +47,7 @@ use { gesture_owner::GestureOwnerHolder, kb_owner::KbOwnerHolder, pointer_owner::PointerOwnerHolder, + tablet::TabletSeatData, text_input::{ zwp_input_method_keyboard_grab_v2::ZwpInputMethodKeyboardGrabV2, zwp_input_method_v2::ZwpInputMethodV2, zwp_text_input_v3::ZwpTextInputV3, @@ -64,6 +66,7 @@ use { }, leaks::Tracker, object::{Object, Version}, + rect::Rect, state::{DeviceHandlerData, State}, time::now_usec, tree::{ @@ -188,6 +191,7 @@ pub struct WlSeatGlobal { swipe_bindings: PerClientBindings, pinch_bindings: PerClientBindings, hold_bindings: PerClientBindings, + tablet: TabletSeatData, } const CHANGE_CURSOR_MOVED: u32 = 1 << 0; @@ -252,6 +256,7 @@ impl WlSeatGlobal { swipe_bindings: Default::default(), pinch_bindings: Default::default(), hold_bindings: Default::default(), + tablet: Default::default(), }); slf.pointer_cursor.set_owner(slf.clone()); let seat = slf.clone(); @@ -861,6 +866,7 @@ impl WlSeatGlobal { self.pinch_bindings.clear(); self.hold_bindings.clear(); self.cursor_user_group.detach(); + self.tablet_clear(); } pub fn id(&self) -> SeatId { @@ -1122,7 +1128,7 @@ impl DeviceHandlerData { pub fn set_seat(&self, seat: Option>) { let old = self.seat.set(seat.clone()); if let Some(old) = old { - if let Some(new) = seat { + if let Some(new) = &seat { if old.id() == new.id() { return; } @@ -1131,8 +1137,22 @@ impl DeviceHandlerData { let xkb_state = &mut *xkb_state.borrow_mut(); xkb_state.reset(); old.handle_xkb_state_change(xkb_state, xkb_state); + if let Some(info) = &self.tablet_init { + old.tablet_remove_tablet(info.id); + } + if let Some(info) = &self.tablet_pad_init { + old.tablet_remove_tablet_pad(info.id); + } } self.update_xkb_state(); + if let Some(seat) = &seat { + if let Some(info) = &self.tablet_init { + seat.tablet_add_tablet(self.device.id(), info); + } + if let Some(info) = &self.tablet_pad_init { + seat.tablet_add_tablet_pad(self.device.id(), info); + } + } } pub fn set_keymap(&self, keymap: Option>) { @@ -1177,4 +1197,13 @@ impl DeviceHandlerData { } } } + + pub fn get_rect(&self, state: &State) -> Rect { + if let Some(output) = self.output.get() { + if let Some(output) = output.get() { + return output.pos.get(); + } + } + state.root.extents.get() + } } diff --git a/src/ifs/wl_seat/event_handling.rs b/src/ifs/wl_seat/event_handling.rs index 7bb9cd18..be5cab5a 100644 --- a/src/ifs/wl_seat/event_handling.rs +++ b/src/ifs/wl_seat/event_handling.rs @@ -15,6 +15,7 @@ use { DynDataSource, }, wl_seat::{ + tablet::{TabletPad, TabletPadId, TabletTool, TabletToolId}, text_input::TextDisconnectReason, wl_keyboard::{self, WlKeyboard}, wl_pointer::{ @@ -25,7 +26,7 @@ use { }, zwp_pointer_constraints_v1::{ConstraintType, SeatConstraintStatus}, zwp_relative_pointer_v1::ZwpRelativePointerV1, - Dnd, SeatId, WlSeat, WlSeatGlobal, CHANGE_CURSOR_MOVED, + Dnd, SeatId, WlSeat, WlSeatGlobal, CHANGE_CURSOR_MOVED, CHANGE_TREE, }, wl_surface::{xdg_surface::xdg_popup::XdgPopup, WlSurface}, }, @@ -55,6 +56,8 @@ pub struct NodeSeatState { gesture_foci: SmallMap, 1>, pointer_grabs: SmallMap, 1>, dnd_targets: SmallMap, 1>, + tablet_pad_foci: SmallMap, 1>, + tablet_tool_foci: SmallMap, 1>, } impl NodeSeatState { @@ -92,6 +95,22 @@ impl NodeSeatState { self.pointer_grabs.remove(&seat.id); } + pub(super) fn add_tablet_pad_focus(&self, pad: &Rc) { + self.tablet_pad_foci.insert(pad.id, pad.clone()); + } + + pub(super) fn remove_tablet_pad_focus(&self, pad: &TabletPad) { + self.tablet_pad_foci.remove(&pad.id); + } + + pub(super) fn add_tablet_tool_focus(&self, tool: &Rc) { + self.tablet_tool_foci.insert(tool.id, tool.clone()); + } + + pub(super) fn remove_tablet_tool_focus(&self, tool: &TabletTool) { + self.tablet_tool_foci.remove(&tool.id); + } + pub(super) fn add_dnd_target(&self, seat: &Rc) { self.dnd_targets.insert(seat.id, seat.clone()); } @@ -163,6 +182,12 @@ impl NodeSeatState { seat.pointer_stack_modified.set(true); seat.state.tree_changed(); } + while let Some((_, tool)) = self.tablet_tool_foci.pop() { + tool.tool_owner.focus_root(&tool); + } + while let Some((_, pad)) = self.tablet_pad_foci.pop() { + pad.pad_owner.focus_root(&pad); + } self.release_kb_focus2(focus_last); } @@ -203,7 +228,13 @@ impl WlSeatGlobal { | InputEvent::PinchEnd { time_usec, .. } | InputEvent::HoldBegin { time_usec, .. } | InputEvent::HoldEnd { time_usec, .. } - | InputEvent::SwitchEvent { time_usec, .. } => { + | InputEvent::SwitchEvent { time_usec, .. } + | InputEvent::TabletToolChanged { time_usec, .. } + | InputEvent::TabletToolButton { time_usec, .. } + | InputEvent::TabletPadButton { time_usec, .. } + | InputEvent::TabletPadModeSwitch { time_usec, .. } + | InputEvent::TabletPadRing { time_usec, .. } + | InputEvent::TabletPadStrip { time_usec, .. } => { self.last_input_usec.set(time_usec); if self.idle_notifications.is_not_empty() { for (_, notification) in self.idle_notifications.lock().drain() { @@ -214,7 +245,39 @@ impl WlSeatGlobal { InputEvent::AxisPx { .. } | InputEvent::AxisSource { .. } | InputEvent::AxisStop { .. } - | InputEvent::Axis120 { .. } => {} + | InputEvent::Axis120 { .. } + | InputEvent::TabletToolAdded { .. } + | InputEvent::TabletToolRemoved { .. } => {} + } + match event { + InputEvent::ConnectorPosition { .. } + | InputEvent::Motion { .. } + | InputEvent::Button { .. } + | InputEvent::AxisFrame { .. } + | InputEvent::SwipeBegin { .. } + | InputEvent::SwipeUpdate { .. } + | InputEvent::SwipeEnd { .. } + | InputEvent::PinchBegin { .. } + | InputEvent::PinchUpdate { .. } + | InputEvent::PinchEnd { .. } + | InputEvent::HoldBegin { .. } + | InputEvent::HoldEnd { .. } => { + self.pointer_cursor.activate(); + } + InputEvent::Key { .. } => {} + InputEvent::AxisPx { .. } => {} + InputEvent::AxisSource { .. } => {} + InputEvent::AxisStop { .. } => {} + InputEvent::Axis120 { .. } => {} + InputEvent::SwitchEvent { .. } => {} + InputEvent::TabletToolAdded { .. } => {} + InputEvent::TabletToolChanged { .. } => {} + InputEvent::TabletToolButton { .. } => {} + InputEvent::TabletToolRemoved { .. } => {} + InputEvent::TabletPadButton { .. } => {} + InputEvent::TabletPadModeSwitch { .. } => {} + InputEvent::TabletPadRing { .. } => {} + InputEvent::TabletPadStrip { .. } => {} } match event { InputEvent::Key { @@ -305,6 +368,49 @@ impl WlSeatGlobal { InputEvent::SwitchEvent { time_usec, event } => { self.switch_event(dev.device.id(), time_usec, event) } + InputEvent::TabletToolAdded { time_usec, init } => { + self.tablet_handle_new_tool(time_usec, &init) + } + InputEvent::TabletToolChanged { + time_usec, + id, + changes: change, + } => self.tablet_event_tool_changes(id, time_usec, dev.get_rect(&self.state), &change), + InputEvent::TabletToolButton { + time_usec, + id, + button, + state, + } => self.tablet_event_tool_button(id, time_usec, button, state), + InputEvent::TabletToolRemoved { time_usec, id } => { + self.tablet_handle_remove_tool(time_usec, id) + } + InputEvent::TabletPadButton { + time_usec, + id, + button, + state, + } => self.tablet_event_pad_button(id, time_usec, button, state), + InputEvent::TabletPadModeSwitch { + time_usec, + pad, + group, + mode, + } => self.tablet_event_pad_mode_switch(pad, time_usec, group, mode), + InputEvent::TabletPadRing { + time_usec, + pad, + ring, + source, + angle, + } => self.tablet_event_pad_ring(pad, ring, source, angle, time_usec), + InputEvent::TabletPadStrip { + time_usec, + pad, + strip, + source, + position, + } => self.tablet_event_pad_strip(pad, strip, source, position, time_usec), } } @@ -784,6 +890,9 @@ impl WlSeatGlobal { pub(super) fn apply_changes(self: &Rc) { self.state.damage(); self.pointer_owner.apply_changes(self); + if self.changes.get().contains(CHANGE_TREE) { + self.tablet_apply_changes(); + } self.changes.set(0); } } diff --git a/src/ifs/wl_seat/kb_owner.rs b/src/ifs/wl_seat/kb_owner.rs index cda1eeb5..796aeb57 100644 --- a/src/ifs/wl_seat/kb_owner.rs +++ b/src/ifs/wl_seat/kb_owner.rs @@ -79,6 +79,7 @@ impl KbOwner for DefaultKbOwner { // log::info!("focus {}", node.node_id()); node.clone().node_on_focus(seat); seat.keyboard_node.set(node.clone()); + seat.tablet_on_keyboard_node_change(); } } diff --git a/src/ifs/wl_seat/tablet.rs b/src/ifs/wl_seat/tablet.rs new file mode 100644 index 00000000..bd8517d6 --- /dev/null +++ b/src/ifs/wl_seat/tablet.rs @@ -0,0 +1,393 @@ +use { + crate::{ + backend::{InputDeviceGroupId, InputDeviceId}, + cursor_user::CursorUser, + ifs::{ + wl_seat::{ + tablet::{ + pad_owner::PadOwnerHolder, tablet_bindings::TabletBindings, + tool_owner::ToolOwnerHolder, zwp_tablet_pad_group_v2::ZwpTabletPadGroupV2, + zwp_tablet_pad_ring_v2::ZwpTabletPadRingV2, + zwp_tablet_pad_strip_v2::ZwpTabletPadStripV2, + zwp_tablet_pad_v2::ZwpTabletPadV2, zwp_tablet_seat_v2::ZwpTabletSeatV2, + zwp_tablet_tool_v2::ZwpTabletToolV2, zwp_tablet_v2::ZwpTabletV2, + }, + WlSeatGlobal, + }, + wl_surface::WlSurface, + }, + object::Version, + time::now_usec, + tree::{FoundNode, Node}, + utils::{bindings::PerClientBindings, clonecell::CloneCell, copyhashmap::CopyHashMap}, + }, + std::{ + cell::{Cell, RefCell}, + rc::Rc, + }, +}; + +mod pad; +mod pad_owner; +mod tablet_bindings; +mod tool; +pub mod tool_owner; +pub mod zwp_tablet_manager_v2; +pub mod zwp_tablet_pad_group_v2; +pub mod zwp_tablet_pad_ring_v2; +pub mod zwp_tablet_pad_strip_v2; +pub mod zwp_tablet_pad_v2; +pub mod zwp_tablet_seat_v2; +pub mod zwp_tablet_tool_v2; +pub mod zwp_tablet_v2; + +#[derive(Default)] +pub struct TabletSeatData { + seats: PerClientBindings, + tablets: CopyHashMap>, + tools: CopyHashMap>, + pads: CopyHashMap>, +} + +#[derive(Debug, Clone)] +pub struct TabletInit { + pub id: TabletId, + pub group: InputDeviceGroupId, + pub name: String, + pub pid: u32, + pub vid: u32, + pub path: String, +} + +#[derive(Debug, Clone)] +pub struct TabletToolInit { + pub tablet_id: TabletId, + pub id: TabletToolId, + pub type_: TabletToolType, + pub hardware_serial: u64, + pub hardware_id_wacom: u64, + pub capabilities: Vec, +} + +#[derive(Debug, Clone)] +pub struct TabletPadInit { + pub id: TabletPadId, + pub group: InputDeviceGroupId, + pub path: String, + pub buttons: u32, + pub strips: u32, + pub rings: u32, + pub groups: Vec, +} + +#[derive(Debug, Clone)] +pub struct TabletPadGroupInit { + pub buttons: Vec, + pub rings: Vec, + pub strips: Vec, + pub modes: u32, + pub mode: u32, +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum PadButtonState { + Released, + Pressed, +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum ToolButtonState { + Released, + Pressed, +} + +linear_ids!(TabletIds, TabletId); + +pub struct Tablet { + _id: TabletId, + dev: InputDeviceId, + group: InputDeviceGroupId, + name: String, + pid: u32, + vid: u32, + path: String, + bindings: TabletBindings, + tools: CopyHashMap>, + pads: CopyHashMap>, + tree: RefCell>, + seat: Rc, +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum TabletToolType { + Pen, + Eraser, + Brush, + Pencil, + Airbrush, + #[allow(dead_code)] + Finger, + Mouse, + Lens, +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum TabletToolCapability { + Tilt, + Pressure, + Distance, + Rotation, + Slider, + Wheel, +} + +linear_ids!(TabletToolIds, TabletToolId, usize); + +#[derive(Default)] +pub struct TabletToolOpt { + tool: CloneCell>>, +} + +pub struct TabletTool { + pub id: TabletToolId, + opt: Rc, + tablet: Rc, + type_: TabletToolType, + hardware_serial: u64, + hardware_id_wacom: u64, + capabilities: Vec, + bindings: TabletBindings, + node: CloneCell>, + pub(super) tool_owner: ToolOwnerHolder, + cursor: Rc, + + down: Cell, + pressure: Cell, + distance: Cell, + tilt_x: Cell, + tilt_y: Cell, + rotation: Cell, + slider: Cell, +} + +linear_ids!(TabletPadIds, TabletPadId); + +pub struct TabletPad { + pub id: TabletPadId, + dev: InputDeviceId, + seat: Rc, + group: InputDeviceGroupId, + tablet: CloneCell>>, + path: String, + buttons: u32, + bindings: TabletBindings, + groups: Vec>, + strips: Vec>, + rings: Vec>, + node: CloneCell>, + pub(super) pad_owner: PadOwnerHolder, +} + +pub struct TabletPadGroup { + buttons: Vec, + mode: Cell, + modes: u32, + rings: Vec, + strips: Vec, + bindings: TabletBindings, +} + +pub struct TabletPadStrip { + bindings: TabletBindings, +} + +pub struct TabletPadRing { + bindings: TabletBindings, +} + +#[derive(Copy, Clone, Debug)] +pub enum TabletRingEventSource { + Finger, +} + +#[derive(Copy, Clone, Debug)] +pub enum TabletStripEventSource { + Finger, +} + +#[derive(Debug, Default)] +pub struct TabletToolChanges { + pub down: Option, + pub pos: Option>, + pub pressure: Option, + pub distance: Option, + pub tilt: Option>, + pub rotation: Option, + pub slider: Option, + pub wheel: Option, +} + +#[derive(Copy, Clone, Debug)] +pub struct TabletTool2dChange { + pub x: T, + pub y: T, +} + +#[derive(Copy, Clone, Debug)] +pub struct TabletToolPositionChange { + pub x: f64, + pub dx: f64, +} + +#[derive(Copy, Clone, Debug)] +pub struct TabletToolWheelChange { + pub degrees: f64, + pub clicks: i32, +} + +impl WlSeatGlobal { + fn tablet_add_seat(&self, seat: &Rc) { + self.tablet.seats.add(&seat.client, seat); + for tablet in self.tablet.tablets.lock().values() { + seat.announce_tablet(tablet); + } + for tool in self.tablet.tools.lock().values() { + seat.announce_tool(tool); + } + for pad in self.tablet.pads.lock().values() { + seat.announce_pad(pad); + } + } + + pub fn tablet_add_tablet(self: &Rc, dev: InputDeviceId, init: &TabletInit) { + let tablet = Rc::new(Tablet { + _id: init.id, + dev, + group: init.group, + name: init.name.clone(), + pid: init.pid, + vid: init.vid, + path: init.path.clone(), + bindings: Default::default(), + tools: Default::default(), + pads: Default::default(), + tree: Default::default(), + seat: self.clone(), + }); + self.tablet.tablets.set(init.id, tablet.clone()); + self.tablet_for_each_seat_obj(|s| s.announce_tablet(&tablet)); + for pad in self.tablet.pads.lock().values() { + if pad.tablet.is_none() && pad.group == init.group { + self.connect_tablet_and_pad(&tablet, pad); + } + } + } + + fn tablet_for_each_seat_obj(&self, mut f: impl FnMut(&Rc)) { + for seats in self.tablet.seats.borrow().values() { + for seat in seats.values() { + f(seat); + } + } + } + + pub fn tablet_clear(&self) { + self.tablet.seats.clear(); + for (_, tablet) in self.tablet.tablets.lock().drain() { + tablet.pads.clear(); + tablet.bindings.clear(); + tablet.tools.clear(); + } + for (_, tool) in self.tablet.tools.lock().drain() { + tool.cursor.detach(); + tool.opt.tool.take(); + tool.tool_owner.destroy(&tool); + tool.bindings.clear(); + } + for (_, pad) in self.tablet.pads.lock().drain() { + pad.pad_owner.destroy(&pad); + pad.tablet.take(); + pad.bindings.clear(); + for group in &pad.groups { + group.bindings.clear(); + } + for rings in &pad.rings { + rings.bindings.clear(); + } + for strips in &pad.strips { + strips.bindings.clear(); + } + } + } + + pub fn tablet_remove_tablet(self: &Rc, id: TabletId) { + let Some(tablet) = self.tablet.tablets.remove(&id) else { + return; + }; + for (_, tool) in tablet.tools.lock().drain() { + self.tablet_handle_remove_tool(now_usec(), tool.id); + } + for (_, pad) in tablet.pads.lock().drain() { + pad.pad_owner.destroy(&pad); + pad.tablet.take(); + } + for (_, binding) in tablet.bindings.lock().drain() { + binding.send_removed(); + } + } + + fn connect_tablet_and_pad(self: &Rc, tablet: &Rc, pad: &Rc) { + pad.tablet.set(Some(tablet.clone())); + tablet.pads.set(pad.id, pad.clone()); + pad.pad_owner.update_node(pad); + } + + pub fn tablet_on_keyboard_node_change(self: &Rc) { + if self.tablet.pads.is_empty() { + return; + } + for pad in self.tablet.pads.lock().values() { + if pad.tablet.is_some() { + pad.pad_owner.update_node(pad); + } + } + } + + fn tablet_for_each_seat(&self, surface: &WlSurface, f: impl FnMut(&ZwpTabletSeatV2)) { + self.tablet + .seats + .for_each(surface.client.id, Version::ALL, f) + } + + pub(super) fn tablet_apply_changes(self: &Rc) { + if self.tablet.tools.is_empty() { + return; + } + let now = now_usec(); + for tool in self.tablet.tools.lock().values() { + tool.tool_owner.apply_changes(tool, now, None); + } + } +} + +fn normalizei(n: f64) -> i32 { + (65535.0 * n) as i32 +} + +fn normalizeu(n: f64) -> u32 { + normalizei(n) as u32 +} + +impl TabletTool { + pub fn cursor(&self) -> &Rc { + &self.cursor + } + + pub fn node(&self) -> Rc { + self.node.get() + } + + pub fn seat(&self) -> &Rc { + &self.tablet.seat + } +} diff --git a/src/ifs/wl_seat/tablet/pad.rs b/src/ifs/wl_seat/tablet/pad.rs new file mode 100644 index 00000000..7eddb329 --- /dev/null +++ b/src/ifs/wl_seat/tablet/pad.rs @@ -0,0 +1,290 @@ +use { + crate::{ + backend::InputDeviceId, + fixed::Fixed, + ifs::{ + wl_seat::{ + tablet::{ + normalizeu, zwp_tablet_pad_v2::ZwpTabletPadV2, zwp_tablet_v2::ZwpTabletV2, + PadButtonState, TabletPad, TabletPadGroup, TabletPadId, TabletPadInit, + TabletPadRing, TabletPadStrip, TabletRingEventSource, TabletStripEventSource, + }, + WlSeatGlobal, + }, + wl_surface::WlSurface, + }, + time::{now_usec, usec_to_msec}, + utils::clonecell::CloneCell, + }, + std::{cell::Cell, rc::Rc}, +}; + +impl WlSeatGlobal { + pub fn tablet_add_tablet_pad(self: &Rc, dev: InputDeviceId, init: &TabletPadInit) { + let mut strips = Vec::new(); + for _ in 0..init.strips { + strips.push(Rc::new(TabletPadStrip { + bindings: Default::default(), + })); + } + let mut rings = Vec::new(); + for _ in 0..init.rings { + rings.push(Rc::new(TabletPadRing { + bindings: Default::default(), + })); + } + let mut groups = Vec::new(); + for group_init in &init.groups { + groups.push(Rc::new(TabletPadGroup { + buttons: group_init.buttons.clone(), + mode: Cell::new(group_init.mode), + modes: group_init.modes, + rings: group_init.rings.clone(), + strips: group_init.strips.clone(), + bindings: Default::default(), + })); + } + let pad = Rc::new(TabletPad { + id: init.id, + dev, + seat: self.clone(), + group: init.group, + tablet: Default::default(), + path: init.path.clone(), + buttons: init.buttons, + bindings: Default::default(), + groups, + strips, + rings, + node: CloneCell::new(self.state.root.clone()), + pad_owner: Default::default(), + }); + self.tablet.pads.set(init.id, pad.clone()); + self.tablet_for_each_seat_obj(|s| s.announce_pad(&pad)); + for tablet in self.tablet.tablets.lock().values() { + if tablet.group == init.group { + self.connect_tablet_and_pad(tablet, &pad); + } + } + } + + pub fn tablet_remove_tablet_pad(self: &Rc, id: TabletPadId) { + let Some(pad) = self.tablet.pads.remove(&id) else { + return; + }; + pad.pad_owner.destroy(&pad); + if let Some(tablet) = pad.tablet.take() { + tablet.pads.remove(&pad.id); + } + for (_, binding) in pad.bindings.lock().drain() { + binding.send_removed(); + } + } + + pub fn tablet_event_pad_mode_switch( + self: &Rc, + pad: TabletPadId, + time_usec: u64, + group_idx: u32, + mode: u32, + ) { + if let Some(pad) = self.tablet.pads.get(&pad) { + if let Some(group) = pad.groups.get(group_idx as usize) { + if group.mode.replace(mode) != mode { + self.state.for_each_seat_tester(|t| { + t.send_tablet_pad_mode_switch(self.id, pad.dev, time_usec, group_idx, mode) + }); + if pad.tablet.is_some() { + let node = pad.node.get(); + node.node_on_tablet_pad_mode_switch(&pad, group, time_usec, mode); + } + } + } + } + } + + pub fn tablet_event_pad_button( + self: &Rc, + pad: TabletPadId, + time_usec: u64, + button: u32, + state: PadButtonState, + ) { + if let Some(pad) = self.tablet.pads.get(&pad) { + self.state.for_each_seat_tester(|t| { + t.send_tablet_pad_button(self.id, pad.dev, time_usec, button, state) + }); + if pad.tablet.is_some() { + pad.pad_owner.button(&pad, time_usec, button, state); + } + } + } + + pub fn tablet_event_pad_ring( + self: &Rc, + pad: TabletPadId, + ring: u32, + source: Option, + angle: Option, + time_usec: u64, + ) { + if let Some(pad) = self.tablet.pads.get(&pad) { + self.state.for_each_seat_tester(|t| { + t.send_tablet_pad_ring(self.id, pad.dev, time_usec, ring, source, angle) + }); + if pad.tablet.is_some() { + if let Some(ring) = pad.rings.get(ring as usize) { + let node = self.keyboard_node.get(); + node.node_on_tablet_pad_ring(&pad, ring, source, angle, time_usec); + } + } + } + } + + pub fn tablet_event_pad_strip( + self: &Rc, + pad: TabletPadId, + strip: u32, + source: Option, + position: Option, + time_usec: u64, + ) { + if let Some(pad) = self.tablet.pads.get(&pad) { + self.state.for_each_seat_tester(|t| { + t.send_tablet_pad_strip(self.id, pad.dev, time_usec, strip, source, position) + }); + if pad.tablet.is_some() { + if let Some(strip) = pad.strips.get(strip as usize) { + let node = pad.node.get(); + node.node_on_tablet_pad_strip(&pad, strip, source, position, time_usec); + } + } + } + } +} + +impl TabletPad { + fn for_each_pair(&self, n: &WlSurface, mut f: impl FnMut(&ZwpTabletV2, &ZwpTabletPadV2)) { + let Some(tablet) = self.tablet.get() else { + return; + }; + self.seat.tablet_for_each_seat(n, |s| { + let Some(tablet) = tablet.bindings.get(s) else { + return; + }; + let Some(pad) = self.bindings.get(s) else { + return; + }; + f(&tablet, &pad); + }) + } + + fn for_each_entered(&self, n: &WlSurface, mut f: impl FnMut(&ZwpTabletPadV2)) { + self.seat.tablet_for_each_seat(n, |s| { + let Some(pad) = self.bindings.get(s) else { + return; + }; + if pad.entered.get() { + f(&pad); + } + }) + } + + pub fn surface_enter(self: &Rc, n: &WlSurface) { + let mut serial = n.client.pending_serial(); + let time = usec_to_msec(now_usec()); + self.for_each_pair(n, |tablet, pad| { + pad.send_enter(serial.get(), &tablet, n); + for group in &self.groups { + let mode = group.mode.get(); + if let Some(group) = group.bindings.get(&pad.seat) { + group.send_mode_switch(time, serial.get(), mode); + } + } + }); + } + + pub fn surface_leave(self: &Rc, n: &WlSurface) { + let mut serial = n.client.pending_serial(); + self.for_each_entered(n, |pad| { + pad.send_leave(serial.get(), n); + }); + } + + pub fn surface_ring( + self: &Rc, + n: &WlSurface, + ring: &Rc, + source: Option, + angle: Option, + time_usec: u64, + ) { + let time = usec_to_msec(time_usec); + self.seat.tablet_for_each_seat(n, |s| { + if let Some(ring) = ring.bindings.get(&s) { + if let Some(source) = source { + ring.send_source(source); + } + if let Some(angle) = angle { + ring.send_angle(Fixed::from_f64(angle)); + } else { + ring.send_stop(); + } + ring.send_frame(time); + } + }); + } + + pub fn surface_strip( + self: &Rc, + n: &WlSurface, + strip: &Rc, + source: Option, + position: Option, + time_usec: u64, + ) { + let time = usec_to_msec(time_usec); + self.for_each_entered(n, |pad| { + if let Some(strip) = strip.bindings.get(&pad.seat) { + if let Some(source) = source { + strip.send_source(source); + } + if let Some(position) = position { + strip.send_position(normalizeu(position)); + } else { + strip.send_stop(); + } + strip.send_frame(time); + } + }); + } + + pub fn surface_mode_switch( + self: &Rc, + n: &WlSurface, + group: &Rc, + time_usec: u64, + mode: u32, + ) { + let time = usec_to_msec(time_usec); + let mut serial = n.client.pending_serial(); + self.for_each_entered(n, |pad| { + if let Some(group) = group.bindings.get(&pad.seat) { + group.send_mode_switch(time, serial.get(), mode); + } + }); + } + + pub fn surface_button( + self: &Rc, + n: &WlSurface, + time_usec: u64, + button: u32, + state: PadButtonState, + ) { + let time = usec_to_msec(time_usec); + self.for_each_entered(n, |pad| { + pad.send_button(time, button, state); + }) + } +} diff --git a/src/ifs/wl_seat/tablet/pad_owner.rs b/src/ifs/wl_seat/tablet/pad_owner.rs new file mode 100644 index 00000000..ab473fa3 --- /dev/null +++ b/src/ifs/wl_seat/tablet/pad_owner.rs @@ -0,0 +1,135 @@ +use { + crate::{ + ifs::wl_seat::tablet::{PadButtonState, TabletPad}, + time::now_usec, + tree::Node, + utils::{clonecell::CloneCell, smallmap::SmallMap}, + }, + std::rc::Rc, +}; + +pub struct PadOwnerHolder { + default: Rc, + owner: CloneCell>, +} + +trait PadOwner { + fn revert_to_default(&self, pad: &Rc, time_usec: u64); + fn update_node(&self, pad: &Rc); + fn button(&self, pad: &Rc, time_usec: u64, button: u32, state: PadButtonState); +} + +struct DefaultPadOwner; + +struct GrabPadOwner { + buttons: SmallMap, + node: Rc, +} + +impl Default for PadOwnerHolder { + fn default() -> Self { + let default = Rc::new(DefaultPadOwner); + Self { + owner: CloneCell::new(default.clone()), + default, + } + } +} + +impl PadOwnerHolder { + pub fn update_node(&self, pad: &Rc) { + self.owner.get().update_node(pad); + } + + pub fn destroy(&self, pad: &Rc) { + self.owner.get().revert_to_default(pad, now_usec()); + let prev = pad.node.set(pad.seat.state.root.clone()); + prev.node_on_tablet_pad_leave(pad); + prev.node_seat_state().remove_tablet_pad_focus(pad); + } + + pub fn button(&self, pad: &Rc, time_usec: u64, button: u32, state: PadButtonState) { + self.owner.get().button(pad, time_usec, button, state); + } + + pub fn focus_root(&self, pad: &Rc) { + self.owner.get().revert_to_default(pad, now_usec()); + let node = pad.seat.state.root.clone(); + pad.focus_node(node); + } + + fn set_default_owner(&self) { + self.owner.set(self.default.clone()); + } +} + +impl TabletPad { + fn focus_node(self: &Rc, node: Rc) { + let prev = self.node.set(node.clone()); + if node.node_id() != prev.node_id() { + prev.node_on_tablet_pad_leave(self); + prev.node_seat_state().remove_tablet_pad_focus(self); + node.node_seat_state().add_tablet_pad_focus(self); + node.node_on_tablet_pad_enter(self); + } + } +} + +impl PadOwner for DefaultPadOwner { + fn revert_to_default(&self, _pad: &Rc, _time_usec: u64) { + // nothing + } + + fn update_node(&self, pad: &Rc) { + let node = pad.seat.keyboard_node.get(); + pad.focus_node(node); + } + + fn button(&self, pad: &Rc, time_usec: u64, button: u32, state: PadButtonState) { + if state != PadButtonState::Pressed { + return; + } + let node = pad.node.get(); + let owner = Rc::new(GrabPadOwner { + buttons: Default::default(), + node, + }); + pad.pad_owner.owner.set(owner.clone()); + owner.button(pad, time_usec, button, state); + } +} + +impl PadOwner for GrabPadOwner { + fn revert_to_default(&self, pad: &Rc, time_usec: u64) { + for (button, _) in &self.buttons { + self.node + .node_on_tablet_pad_button(pad, time_usec, button, PadButtonState::Released); + } + pad.pad_owner.set_default_owner(); + } + + fn update_node(&self, _pad: &Rc) { + // nothing + } + + fn button(&self, pad: &Rc, time_usec: u64, button: u32, state: PadButtonState) { + match state { + PadButtonState::Released => { + if self.buttons.remove(&button).is_none() { + return; + } + } + PadButtonState::Pressed => { + if self.buttons.insert(button, ()).is_some() { + return; + } + } + } + self.node + .node_on_tablet_pad_button(pad, time_usec, button, state); + if self.buttons.is_empty() { + pad.pad_owner.set_default_owner(); + pad.pad_owner.default.update_node(pad); + } + } +} diff --git a/src/ifs/wl_seat/tablet/tablet_bindings.rs b/src/ifs/wl_seat/tablet/tablet_bindings.rs new file mode 100644 index 00000000..aa0c10e4 --- /dev/null +++ b/src/ifs/wl_seat/tablet/tablet_bindings.rs @@ -0,0 +1,43 @@ +use { + crate::{ + client::ClientId, + ifs::wl_seat::tablet::zwp_tablet_seat_v2::ZwpTabletSeatV2, + utils::copyhashmap::{CopyHashMap, Locked}, + wire::ZwpTabletSeatV2Id, + }, + std::rc::Rc, +}; + +pub struct TabletBindings { + bindings: CopyHashMap<(ClientId, ZwpTabletSeatV2Id), Rc>, +} + +impl Default for TabletBindings { + fn default() -> Self { + Self { + bindings: Default::default(), + } + } +} + +impl TabletBindings { + pub fn add(&self, seat: &ZwpTabletSeatV2, t: &Rc) { + self.bindings.set((seat.client.id, seat.id), t.clone()); + } + + pub fn get(&self, seat: &ZwpTabletSeatV2) -> Option> { + self.bindings.get(&(seat.client.id, seat.id)) + } + + pub fn remove(&self, seat: &ZwpTabletSeatV2) -> Option> { + self.bindings.remove(&(seat.client.id, seat.id)) + } + + pub fn lock(&self) -> Locked<'_, (ClientId, ZwpTabletSeatV2Id), Rc> { + self.bindings.lock() + } + + pub fn clear(&self) { + self.bindings.clear(); + } +} diff --git a/src/ifs/wl_seat/tablet/tool.rs b/src/ifs/wl_seat/tablet/tool.rs new file mode 100644 index 00000000..9f11ea5f --- /dev/null +++ b/src/ifs/wl_seat/tablet/tool.rs @@ -0,0 +1,274 @@ +use { + crate::{ + cursor::KnownCursor, + fixed::Fixed, + ifs::{ + wl_seat::{ + tablet::{ + normalizei, normalizeu, zwp_tablet_tool_v2::ZwpTabletToolV2, + zwp_tablet_v2::ZwpTabletV2, TabletTool, TabletToolChanges, TabletToolId, + TabletToolInit, TabletToolOpt, TabletToolType, ToolButtonState, + }, + WlSeatGlobal, + }, + wl_surface::WlSurface, + }, + rect::Rect, + time::usec_to_msec, + utils::clonecell::CloneCell, + }, + std::{cell::Cell, rc::Rc}, +}; + +impl WlSeatGlobal { + pub fn tablet_handle_remove_tool(self: &Rc, time_usec: u64, id: TabletToolId) { + let Some(tool) = self.tablet.tools.remove(&id) else { + return; + }; + self.state.for_each_seat_tester(|t| { + t.send_tablet_tool_proximity_out(self.id, tool.tablet.dev, tool.id, time_usec) + }); + tool.opt.tool.take(); + tool.cursor.detach(); + tool.tool_owner.destroy(&tool); + for (_, binding) in tool.bindings.lock().drain() { + binding.send_removed(); + } + tool.tablet.tools.remove(&id); + } + + pub fn tablet_handle_new_tool(self: &Rc, time_usec: u64, init: &TabletToolInit) { + let Some(tablet) = self.tablet.tablets.get(&init.tablet_id) else { + return; + }; + let tool = Rc::new(TabletTool { + id: init.id, + opt: Default::default(), + tablet, + type_: init.type_, + hardware_serial: init.hardware_serial, + hardware_id_wacom: init.hardware_id_wacom, + capabilities: init.capabilities.clone(), + bindings: Default::default(), + node: CloneCell::new(self.state.root.clone()), + tool_owner: Default::default(), + cursor: self.cursor_user_group.create_user(), + down: Cell::new(false), + pressure: Cell::new(0.0), + distance: Cell::new(0.0), + tilt_x: Cell::new(0.0), + tilt_y: Cell::new(0.0), + rotation: Cell::new(0.0), + slider: Cell::new(0.0), + }); + tool.opt.tool.set(Some(tool.clone())); + tool.cursor.set_known(KnownCursor::Default); + self.tablet.tools.set(init.id, tool.clone()); + self.state.for_each_seat_tester(|t| { + t.send_tablet_tool_proximity_in(self.id, tool.tablet.dev, tool.id, time_usec) + }); + self.tablet_for_each_seat_obj(|s| s.announce_tool(&tool)); + } + + pub fn tablet_event_tool_button( + self: &Rc, + id: TabletToolId, + time_usec: u64, + button: u32, + state: ToolButtonState, + ) { + let Some(tool) = self.tablet.tools.get(&id) else { + return; + }; + self.state.for_each_seat_tester(|t| { + t.send_tablet_tool_button(self.id, tool.tablet.dev, &tool, time_usec, button, state); + }); + tool.cursor.activate(); + tool.tool_owner.button(&tool, time_usec, button, state); + } + + pub fn tablet_event_tool_changes( + self: &Rc, + id: TabletToolId, + time_usec: u64, + rect: Rect, + changes: &TabletToolChanges, + ) { + let Some(tool) = self.tablet.tools.get(&id) else { + return; + }; + self.state.for_each_seat_tester(|t| { + t.send_tablet_tool_changes(self.id, tool.tablet.dev, &tool, time_usec, changes); + }); + if let Some(val) = changes.down { + tool.down.set(val); + } + if let Some(val) = changes.pressure { + tool.pressure.set(val); + } + if let Some(val) = changes.distance { + tool.distance.set(val); + } + if let Some(val) = changes.tilt { + tool.tilt_x.set(val.x); + tool.tilt_y.set(val.y); + } + if let Some(val) = changes.rotation { + tool.rotation.set(val); + } + if let Some(val) = changes.slider { + tool.slider.set(val); + } + if let Some(delta) = changes.pos { + let (x, y) = match tool.type_ { + TabletToolType::Mouse | TabletToolType::Lens => { + let (mut x, mut y) = tool.cursor.position(); + x += Fixed::from_f64(delta.x.dx); + y += Fixed::from_f64(delta.y.dx); + (x, y) + } + TabletToolType::Pen + | TabletToolType::Eraser + | TabletToolType::Brush + | TabletToolType::Pencil + | TabletToolType::Airbrush + | TabletToolType::Finger => { + let x = Fixed::from_f64(rect.x1() as f64 + (rect.width() as f64 * delta.x.x)); + let y = Fixed::from_f64(rect.y1() as f64 + (rect.height() as f64 * delta.y.x)); + (x, y) + } + }; + tool.cursor.set_position(x, y); + } + tool.cursor.activate(); + tool.tool_owner + .apply_changes(&tool, time_usec, Some(changes)); + } +} + +impl TabletTool { + fn for_each_pair(&self, n: &WlSurface, mut f: impl FnMut(&ZwpTabletV2, &ZwpTabletToolV2)) { + self.tablet.seat.tablet_for_each_seat(n, |s| { + let Some(tablet) = self.tablet.bindings.get(s) else { + return; + }; + let Some(tool) = self.bindings.get(s) else { + return; + }; + f(&tablet, &tool); + }) + } + + fn for_each_entered(&self, n: &WlSurface, mut f: impl FnMut(&ZwpTabletToolV2)) { + self.tablet.seat.tablet_for_each_seat(n, |s| { + let Some(tool) = self.bindings.get(s) else { + return; + }; + if !tool.entered.get() { + return; + } + f(&tool); + }) + } + + pub fn surface_leave(&self, n: &WlSurface, time_usec: u64) { + let time = usec_to_msec(time_usec); + self.for_each_entered(n, |t| { + t.send_proximity_out(); + t.send_frame(time); + }) + } + + pub fn surface_enter(&self, n: &WlSurface, time_usec: u64, x: Fixed, y: Fixed) { + let time = usec_to_msec(time_usec); + let mut serial = n.client.pending_serial(); + self.for_each_pair(n, |tablet, tool| { + tool.send_proximity_in(serial.get(), tablet, n); + tool.send_motion(x, y); + tool.send_pressure(normalizeu(self.pressure.get())); + tool.send_distance(normalizeu(self.distance.get())); + tool.send_tilt( + Fixed::from_f64(self.tilt_x.get()), + Fixed::from_f64(self.tilt_y.get()), + ); + tool.send_rotation(Fixed::from_f64(self.rotation.get())); + tool.send_slider(normalizei(self.slider.get())); + tool.send_frame(time); + }) + } + + pub fn surface_button( + &self, + n: &WlSurface, + time_usec: u64, + button: u32, + state: ToolButtonState, + ) { + let time = usec_to_msec(time_usec); + let mut serial = n.client.pending_serial(); + self.for_each_entered(n, |tool| { + tool.send_button(serial.get(), button, state); + tool.send_frame(time); + }); + if state == ToolButtonState::Pressed { + if let Some(node) = n.get_focus_node(self.tablet.seat.id) { + self.tablet.seat.focus_node(node); + } + } + } + + pub fn surface_apply_changes( + &self, + n: &WlSurface, + time_usec: u64, + changes: Option<&TabletToolChanges>, + x: Fixed, + y: Fixed, + ) { + let mut serial = n.client.pending_serial(); + let time = usec_to_msec(time_usec); + self.for_each_entered(n, |tool| { + if let Some(changes) = changes { + if let Some(val) = changes.down { + match val { + false => tool.send_up(), + true => tool.send_down(serial.get()), + } + } + if let Some(val) = changes.pressure { + tool.send_pressure(normalizeu(val)); + } + if let Some(val) = changes.distance { + tool.send_distance(normalizeu(val)); + } + if let Some(val) = changes.tilt { + tool.send_tilt(Fixed::from_f64(val.x), Fixed::from_f64(val.y)); + } + if let Some(val) = changes.rotation { + tool.send_rotation(Fixed::from_f64(val)); + } + if let Some(val) = changes.slider { + tool.send_slider(normalizei(val)); + } + if let Some(val) = changes.wheel { + tool.send_wheel(Fixed::from_f64(val.degrees), val.clicks); + } + } + tool.send_motion(x, y); + tool.send_frame(time); + }); + if let Some(changes) = changes { + if changes.down == Some(true) { + if let Some(node) = n.get_focus_node(self.tablet.seat.id) { + self.tablet.seat.focus_node(node); + } + } + } + } +} + +impl TabletToolOpt { + pub fn get(&self) -> Option> { + self.tool.get() + } +} diff --git a/src/ifs/wl_seat/tablet/tool_owner.rs b/src/ifs/wl_seat/tablet/tool_owner.rs new file mode 100644 index 00000000..2fd963d7 --- /dev/null +++ b/src/ifs/wl_seat/tablet/tool_owner.rs @@ -0,0 +1,213 @@ +use { + crate::{ + fixed::Fixed, + ifs::wl_seat::tablet::{TabletTool, TabletToolChanges, ToolButtonState}, + time::now_usec, + tree::{FindTreeUsecase, FoundNode, Node}, + utils::{clonecell::CloneCell, smallmap::SmallMap}, + }, + std::rc::Rc, +}; + +pub struct ToolOwnerHolder { + default: Rc, + owner: CloneCell>, +} + +struct DefaultToolOwner; + +struct GrabToolOwner { + buttons: SmallMap, + node: Rc, +} + +impl Default for ToolOwnerHolder { + fn default() -> Self { + let default = Rc::new(DefaultToolOwner); + Self { + owner: CloneCell::new(default.clone()), + default, + } + } +} + +impl ToolOwnerHolder { + pub fn destroy(&self, tool: &Rc) { + let root = tool.tablet.seat.state.root.clone(); + let prev = tool.node.set(root); + prev.node_on_tablet_tool_leave(tool, now_usec()); + prev.node_seat_state().remove_tablet_tool_focus(tool); + } + + pub fn focus_root(&self, tool: &Rc) { + self.owner.set(self.default.clone()); + let root = tool.tablet.seat.state.root.clone(); + tool.set_node(root, now_usec()); + } + + pub fn button( + &self, + tool: &Rc, + time_usec: u64, + button: u32, + state: ToolButtonState, + ) { + self.owner.get().button(tool, time_usec, button, state); + } + + pub fn apply_changes( + &self, + tool: &Rc, + time_usec: u64, + changes: Option<&TabletToolChanges>, + ) { + self.owner.get().apply_changes(tool, time_usec, changes); + } +} + +trait ToolOwner { + fn button(&self, tool: &Rc, time_usec: u64, button: u32, state: ToolButtonState); + fn apply_changes( + &self, + tool: &Rc, + time_usec: u64, + changes: Option<&TabletToolChanges>, + ); +} + +impl TabletTool { + fn set_node(self: &Rc, node: Rc, time_usec: u64) { + let prev = self.node.set(node.clone()); + if prev.node_id() != node.node_id() { + prev.node_on_tablet_tool_leave(self, time_usec); + prev.node_seat_state().remove_tablet_tool_focus(self); + let (tool_x, tool_y) = self.cursor.position(); + let (node_x, node_y) = node.node_absolute_position().position(); + node.node_seat_state().add_tablet_tool_focus(self); + node.node_on_tablet_tool_enter(self, time_usec, tool_x - node_x, tool_y - node_y); + } + } +} + +impl ToolOwner for DefaultToolOwner { + fn button(&self, tool: &Rc, time_usec: u64, button: u32, state: ToolButtonState) { + if state == ToolButtonState::Released { + return; + } + let owner = Rc::new(GrabToolOwner { + buttons: Default::default(), + node: tool.node.get(), + }); + tool.tool_owner.owner.set(owner.clone()); + owner.button(tool, time_usec, button, state); + } + + fn apply_changes( + &self, + tool: &Rc, + time_usec: u64, + changes: Option<&TabletToolChanges>, + ) { + let change = handle_position_change(tool); + let node = change.node; + if change.changed { + tool.set_node(node.clone(), time_usec); + } else { + node.clone() + .node_on_tablet_tool_apply_changes(tool, time_usec, changes, change.x, change.y); + } + if tool.down.get() { + tool.tool_owner.owner.set(Rc::new(GrabToolOwner { + buttons: Default::default(), + node, + })); + } + } +} + +impl GrabToolOwner { + fn maybe_revert(&self, tool: &Rc) { + if !tool.down.get() && self.buttons.is_empty() { + tool.tool_owner.owner.set(tool.tool_owner.default.clone()); + tool.tablet.seat.tree_changed.trigger(); + } + } +} + +impl ToolOwner for GrabToolOwner { + fn button(&self, tool: &Rc, time_usec: u64, button: u32, state: ToolButtonState) { + match state { + ToolButtonState::Released => { + if self.buttons.remove(&button).is_none() { + return; + } + } + ToolButtonState::Pressed => { + if self.buttons.insert(button, ()).is_some() { + return; + } + } + } + self.node + .node_on_tablet_tool_button(tool, time_usec, button, state); + self.maybe_revert(tool); + } + + fn apply_changes( + &self, + tool: &Rc, + time_usec: u64, + changes: Option<&TabletToolChanges>, + ) { + let (x, y) = tool.cursor.position(); + let node_pos = self.node.node_absolute_position(); + self.node.clone().node_on_tablet_tool_apply_changes( + tool, + time_usec, + changes, + x - node_pos.x1(), + y - node_pos.y1(), + ); + self.maybe_revert(tool); + } +} + +fn handle_position_change(tool: &Rc) -> UpdatedNode { + let (x, y) = tool.cursor.position(); + let x_int = x.round_down(); + let y_int = y.round_down(); + let tree = &mut *tool.tablet.tree.borrow_mut(); + tree.push(FoundNode { + node: tool.tablet.seat.state.root.clone(), + x: x_int, + y: y_int, + }); + tool.tablet + .seat + .state + .root + .node_find_tree_at(x_int, y_int, tree, FindTreeUsecase::None); + let mut update = UpdatedNode { + node: tool.node.get(), + x, + y, + changed: false, + }; + if let Some(last) = tree.last() { + if last.node.node_id() != update.node.node_id() { + update.changed = true; + update.node = last.node.clone(); + } + update.x = x.apply_fract(last.x); + update.y = y.apply_fract(last.y); + } + tree.clear(); + update +} + +struct UpdatedNode { + node: Rc, + changed: bool, + x: Fixed, + y: Fixed, +} diff --git a/src/ifs/wl_seat/tablet/zwp_tablet_manager_v2.rs b/src/ifs/wl_seat/tablet/zwp_tablet_manager_v2.rs new file mode 100644 index 00000000..982f80e7 --- /dev/null +++ b/src/ifs/wl_seat/tablet/zwp_tablet_manager_v2.rs @@ -0,0 +1,104 @@ +use { + crate::{ + client::{Client, ClientError}, + globals::{Global, GlobalName}, + ifs::wl_seat::tablet::zwp_tablet_seat_v2::ZwpTabletSeatV2, + leaks::Tracker, + object::{Object, Version}, + wire::{zwp_tablet_manager_v2::*, ZwpTabletManagerV2Id}, + }, + std::rc::Rc, + thiserror::Error, +}; + +pub struct ZwpTabletManagerV2Global { + pub name: GlobalName, +} + +pub struct ZwpTabletManagerV2 { + pub id: ZwpTabletManagerV2Id, + pub client: Rc, + pub tracker: Tracker, + pub version: Version, +} + +impl ZwpTabletManagerV2Global { + pub fn new(name: GlobalName) -> Self { + Self { name } + } + + fn bind_( + self: Rc, + id: ZwpTabletManagerV2Id, + client: &Rc, + version: Version, + ) -> Result<(), ZwpTabletManagerV2Error> { + let obj = Rc::new(ZwpTabletManagerV2 { + id, + client: client.clone(), + tracker: Default::default(), + version, + }); + track!(client, obj); + client.add_client_obj(&obj)?; + Ok(()) + } +} + +global_base!( + ZwpTabletManagerV2Global, + ZwpTabletManagerV2, + ZwpTabletManagerV2Error +); + +impl Global for ZwpTabletManagerV2Global { + fn singleton(&self) -> bool { + true + } + + fn version(&self) -> u32 { + 1 + } +} + +simple_add_global!(ZwpTabletManagerV2Global); + +impl ZwpTabletManagerV2RequestHandler for ZwpTabletManagerV2 { + type Error = ZwpTabletManagerV2Error; + + fn get_tablet_seat(&self, req: GetTabletSeat, _slf: &Rc) -> Result<(), Self::Error> { + let seat = self.client.lookup(req.seat)?.global.clone(); + let obj = Rc::new(ZwpTabletSeatV2 { + id: req.tablet_seat, + client: self.client.clone(), + seat: seat.clone(), + tracker: Default::default(), + version: self.version, + }); + track!(self.client, obj); + self.client.add_client_obj(&obj)?; + seat.tablet_add_seat(&obj); + Ok(()) + } + + fn destroy(&self, _req: Destroy, _slf: &Rc) -> Result<(), Self::Error> { + self.client.remove_obj(self)?; + Ok(()) + } +} + +object_base! { + self = ZwpTabletManagerV2; + version = self.version; +} + +impl Object for ZwpTabletManagerV2 {} + +simple_add_obj!(ZwpTabletManagerV2); + +#[derive(Debug, Error)] +pub enum ZwpTabletManagerV2Error { + #[error(transparent)] + ClientError(Box), +} +efrom!(ZwpTabletManagerV2Error, ClientError); diff --git a/src/ifs/wl_seat/tablet/zwp_tablet_pad_group_v2.rs b/src/ifs/wl_seat/tablet/zwp_tablet_pad_group_v2.rs new file mode 100644 index 00000000..4fd989a3 --- /dev/null +++ b/src/ifs/wl_seat/tablet/zwp_tablet_pad_group_v2.rs @@ -0,0 +1,101 @@ +use { + crate::{ + client::{Client, ClientError}, + ifs::wl_seat::tablet::{ + zwp_tablet_pad_ring_v2::ZwpTabletPadRingV2, + zwp_tablet_pad_strip_v2::ZwpTabletPadStripV2, zwp_tablet_seat_v2::ZwpTabletSeatV2, + TabletPadGroup, + }, + leaks::Tracker, + object::{Object, Version}, + wire::{zwp_tablet_pad_group_v2::*, ZwpTabletPadGroupV2Id}, + }, + std::rc::Rc, + thiserror::Error, +}; + +pub struct ZwpTabletPadGroupV2 { + pub id: ZwpTabletPadGroupV2Id, + pub client: Rc, + pub tracker: Tracker, + pub version: Version, + pub seat: Rc, + pub group: Rc, +} + +impl ZwpTabletPadGroupV2 { + pub fn detach(&self) { + self.group.bindings.remove(&self.seat); + } + + pub fn send_buttons(&self, buttons: &[u32]) { + self.client.event(Buttons { + self_id: self.id, + buttons, + }); + } + + pub fn send_ring(&self, ring: &ZwpTabletPadRingV2) { + self.client.event(Ring { + self_id: self.id, + ring: ring.id, + }); + } + + pub fn send_strip(&self, strip: &ZwpTabletPadStripV2) { + self.client.event(Strip { + self_id: self.id, + strip: strip.id, + }); + } + + pub fn send_modes(&self, modes: u32) { + self.client.event(Modes { + self_id: self.id, + modes, + }); + } + + pub fn send_done(&self) { + self.client.event(Done { self_id: self.id }); + } + + pub fn send_mode_switch(&self, time: u32, serial: u32, mode: u32) { + self.client.event(ModeSwitch { + self_id: self.id, + time, + serial, + mode, + }); + } +} + +impl ZwpTabletPadGroupV2RequestHandler for ZwpTabletPadGroupV2 { + type Error = ZwpTabletPadGroupV2Error; + + fn destroy(&self, _req: Destroy, _slf: &Rc) -> Result<(), Self::Error> { + self.detach(); + self.client.remove_obj(self)?; + Ok(()) + } +} + +object_base! { + self = ZwpTabletPadGroupV2; + version = self.version; +} + +impl Object for ZwpTabletPadGroupV2 { + fn break_loops(&self) { + self.detach(); + } +} + +simple_add_obj!(ZwpTabletPadGroupV2); + +#[derive(Debug, Error)] +pub enum ZwpTabletPadGroupV2Error { + #[error(transparent)] + ClientError(Box), +} +efrom!(ZwpTabletPadGroupV2Error, ClientError); diff --git a/src/ifs/wl_seat/tablet/zwp_tablet_pad_ring_v2.rs b/src/ifs/wl_seat/tablet/zwp_tablet_pad_ring_v2.rs new file mode 100644 index 00000000..180e0197 --- /dev/null +++ b/src/ifs/wl_seat/tablet/zwp_tablet_pad_ring_v2.rs @@ -0,0 +1,90 @@ +use { + crate::{ + client::{Client, ClientError}, + fixed::Fixed, + ifs::wl_seat::tablet::{ + zwp_tablet_seat_v2::ZwpTabletSeatV2, TabletPadRing, TabletRingEventSource, + }, + leaks::Tracker, + object::{Object, Version}, + wire::{zwp_tablet_pad_ring_v2::*, ZwpTabletPadRingV2Id}, + }, + std::rc::Rc, + thiserror::Error, +}; + +pub struct ZwpTabletPadRingV2 { + pub id: ZwpTabletPadRingV2Id, + pub client: Rc, + pub tracker: Tracker, + pub version: Version, + pub seat: Rc, + pub ring: Rc, +} + +impl ZwpTabletPadRingV2 { + pub fn detach(&self) { + self.ring.bindings.remove(&self.seat); + } + + pub fn send_source(&self, source: TabletRingEventSource) { + self.client.event(Source { + self_id: self.id, + source: match source { + TabletRingEventSource::Finger => 1, + }, + }); + } + + pub fn send_angle(&self, degrees: Fixed) { + self.client.event(Angle { + self_id: self.id, + degrees, + }); + } + + pub fn send_stop(&self) { + self.client.event(Stop { self_id: self.id }); + } + + pub fn send_frame(&self, time: u32) { + self.client.event(Frame { + self_id: self.id, + time, + }); + } +} + +impl ZwpTabletPadRingV2RequestHandler for ZwpTabletPadRingV2 { + type Error = ZwpTabletPadRingV2Error; + + fn set_feedback(&self, _req: SetFeedback<'_>, _slf: &Rc) -> Result<(), Self::Error> { + Ok(()) + } + + fn destroy(&self, _req: Destroy, _slf: &Rc) -> Result<(), Self::Error> { + self.detach(); + self.client.remove_obj(self)?; + Ok(()) + } +} + +object_base! { + self = ZwpTabletPadRingV2; + version = self.version; +} + +impl Object for ZwpTabletPadRingV2 { + fn break_loops(&self) { + self.detach(); + } +} + +simple_add_obj!(ZwpTabletPadRingV2); + +#[derive(Debug, Error)] +pub enum ZwpTabletPadRingV2Error { + #[error(transparent)] + ClientError(Box), +} +efrom!(ZwpTabletPadRingV2Error, ClientError); diff --git a/src/ifs/wl_seat/tablet/zwp_tablet_pad_strip_v2.rs b/src/ifs/wl_seat/tablet/zwp_tablet_pad_strip_v2.rs new file mode 100644 index 00000000..ae0e8861 --- /dev/null +++ b/src/ifs/wl_seat/tablet/zwp_tablet_pad_strip_v2.rs @@ -0,0 +1,89 @@ +use { + crate::{ + client::{Client, ClientError}, + ifs::wl_seat::tablet::{ + zwp_tablet_seat_v2::ZwpTabletSeatV2, TabletPadStrip, TabletStripEventSource, + }, + leaks::Tracker, + object::{Object, Version}, + wire::{zwp_tablet_pad_strip_v2::*, ZwpTabletPadStripV2Id}, + }, + std::rc::Rc, + thiserror::Error, +}; + +pub struct ZwpTabletPadStripV2 { + pub id: ZwpTabletPadStripV2Id, + pub client: Rc, + pub tracker: Tracker, + pub version: Version, + pub seat: Rc, + pub strip: Rc, +} + +impl ZwpTabletPadStripV2 { + pub fn detach(&self) { + self.strip.bindings.remove(&self.seat); + } + + pub fn send_source(&self, source: TabletStripEventSource) { + self.client.event(Source { + self_id: self.id, + source: match source { + TabletStripEventSource::Finger => 1, + }, + }); + } + + pub fn send_position(&self, position: u32) { + self.client.event(Position { + self_id: self.id, + position, + }); + } + + pub fn send_stop(&self) { + self.client.event(Stop { self_id: self.id }); + } + + pub fn send_frame(&self, time: u32) { + self.client.event(Frame { + self_id: self.id, + time, + }); + } +} + +impl ZwpTabletPadStripV2RequestHandler for ZwpTabletPadStripV2 { + type Error = ZwpTabletPadStripV2Error; + + fn set_feedback(&self, _req: SetFeedback<'_>, _slf: &Rc) -> Result<(), Self::Error> { + Ok(()) + } + + fn destroy(&self, _req: Destroy, _slf: &Rc) -> Result<(), Self::Error> { + self.detach(); + self.client.remove_obj(self)?; + Ok(()) + } +} + +object_base! { + self = ZwpTabletPadStripV2; + version = self.version; +} + +impl Object for ZwpTabletPadStripV2 { + fn break_loops(&self) { + self.detach(); + } +} + +simple_add_obj!(ZwpTabletPadStripV2); + +#[derive(Debug, Error)] +pub enum ZwpTabletPadStripV2Error { + #[error(transparent)] + ClientError(Box), +} +efrom!(ZwpTabletPadStripV2Error, ClientError); diff --git a/src/ifs/wl_seat/tablet/zwp_tablet_pad_v2.rs b/src/ifs/wl_seat/tablet/zwp_tablet_pad_v2.rs new file mode 100644 index 00000000..6dd37b23 --- /dev/null +++ b/src/ifs/wl_seat/tablet/zwp_tablet_pad_v2.rs @@ -0,0 +1,127 @@ +use { + crate::{ + client::{Client, ClientError}, + ifs::{ + wl_seat::tablet::{ + zwp_tablet_pad_group_v2::ZwpTabletPadGroupV2, zwp_tablet_seat_v2::ZwpTabletSeatV2, + zwp_tablet_v2::ZwpTabletV2, PadButtonState, TabletPad, + }, + wl_surface::WlSurface, + }, + leaks::Tracker, + object::{Object, Version}, + wire::{zwp_tablet_pad_v2::*, ZwpTabletPadV2Id}, + }, + std::{cell::Cell, rc::Rc}, + thiserror::Error, +}; + +pub struct ZwpTabletPadV2 { + pub id: ZwpTabletPadV2Id, + pub client: Rc, + pub tracker: Tracker, + pub version: Version, + pub seat: Rc, + pub pad: Rc, + pub entered: Cell, +} + +impl ZwpTabletPadV2 { + pub fn detach(&self) { + self.pad.bindings.remove(&self.seat); + } + + pub fn send_group(&self, group: &ZwpTabletPadGroupV2) { + self.client.event(Group { + self_id: self.id, + pad_group: group.id, + }); + } + + pub fn send_path(&self, path: &str) { + self.client.event(Path { + self_id: self.id, + path, + }); + } + + pub fn send_buttons(&self, buttons: u32) { + self.client.event(Buttons { + self_id: self.id, + buttons, + }); + } + + pub fn send_done(&self) { + self.client.event(Done { self_id: self.id }); + } + + pub fn send_button(&self, time: u32, button: u32, state: PadButtonState) { + self.client.event(Button { + self_id: self.id, + time, + button, + state: match state { + PadButtonState::Released => 0, + PadButtonState::Pressed => 1, + }, + }); + } + + pub fn send_enter(&self, serial: u32, tablet: &ZwpTabletV2, surface: &WlSurface) { + self.entered.set(true); + self.client.event(Enter { + self_id: self.id, + serial, + tablet: tablet.id, + surface: surface.id, + }); + } + + pub fn send_leave(&self, serial: u32, surface: &WlSurface) { + self.entered.set(false); + self.client.event(Leave { + self_id: self.id, + serial, + surface: surface.id, + }); + } + + pub fn send_removed(&self) { + self.client.event(Removed { self_id: self.id }); + } +} + +impl ZwpTabletPadV2RequestHandler for ZwpTabletPadV2 { + type Error = ZwpTabletPadV2Error; + + fn set_feedback(&self, _req: SetFeedback<'_>, _slf: &Rc) -> Result<(), Self::Error> { + Ok(()) + } + + fn destroy(&self, _req: Destroy, _slf: &Rc) -> Result<(), Self::Error> { + self.detach(); + self.client.remove_obj(self)?; + Ok(()) + } +} + +object_base! { + self = ZwpTabletPadV2; + version = self.version; +} + +impl Object for ZwpTabletPadV2 { + fn break_loops(&self) { + self.detach(); + } +} + +simple_add_obj!(ZwpTabletPadV2); + +#[derive(Debug, Error)] +pub enum ZwpTabletPadV2Error { + #[error(transparent)] + ClientError(Box), +} +efrom!(ZwpTabletPadV2Error, ClientError); diff --git a/src/ifs/wl_seat/tablet/zwp_tablet_seat_v2.rs b/src/ifs/wl_seat/tablet/zwp_tablet_seat_v2.rs new file mode 100644 index 00000000..493f5bfd --- /dev/null +++ b/src/ifs/wl_seat/tablet/zwp_tablet_seat_v2.rs @@ -0,0 +1,220 @@ +use { + crate::{ + client::{Client, ClientError}, + ifs::wl_seat::{ + tablet::{ + zwp_tablet_pad_group_v2::ZwpTabletPadGroupV2, + zwp_tablet_pad_ring_v2::ZwpTabletPadRingV2, + zwp_tablet_pad_strip_v2::ZwpTabletPadStripV2, zwp_tablet_pad_v2::ZwpTabletPadV2, + zwp_tablet_tool_v2::ZwpTabletToolV2, zwp_tablet_v2::ZwpTabletV2, Tablet, TabletPad, + TabletTool, + }, + WlSeatGlobal, + }, + leaks::Tracker, + object::{Object, Version}, + wire::{zwp_tablet_seat_v2::*, ZwpTabletSeatV2Id}, + }, + std::{cell::Cell, rc::Rc}, + thiserror::Error, +}; + +pub struct ZwpTabletSeatV2 { + pub id: ZwpTabletSeatV2Id, + pub client: Rc, + pub seat: Rc, + pub tracker: Tracker, + pub version: Version, +} + +impl ZwpTabletSeatV2 { + pub fn detach(&self) { + self.seat.tablet.seats.remove(&self.client, self); + } + + pub fn announce_tablet(self: &Rc, tablet: &Rc) { + let id = match self.client.new_id() { + Ok(id) => id, + Err(e) => { + self.client.error(e); + return; + } + }; + let obj = Rc::new(ZwpTabletV2 { + id, + client: self.client.clone(), + seat: self.clone(), + tracker: Default::default(), + version: self.version, + tablet: tablet.clone(), + }); + track!(self.client, obj); + self.client.add_server_obj(&obj); + self.send_tablet_added(&obj); + obj.send_name(&tablet.name); + obj.send_id(tablet.vid, tablet.pid); + obj.send_path(&tablet.path); + obj.send_done(); + tablet.bindings.add(self, &obj); + } + + pub fn announce_tool(self: &Rc, tool: &Rc) { + let id = match self.client.new_id() { + Ok(id) => id, + Err(e) => { + self.client.error(e); + return; + } + }; + let obj = Rc::new(ZwpTabletToolV2 { + id, + client: self.client.clone(), + seat: self.clone(), + tool: tool.opt.clone(), + tracker: Default::default(), + version: self.version, + entered: Cell::new(false), + }); + track!(self.client, obj); + self.client.add_server_obj(&obj); + self.send_tool_added(&obj); + obj.send_type(tool.type_); + obj.send_hardware_serial(tool.hardware_serial); + obj.send_hardware_id_wacom(tool.hardware_id_wacom); + for cap in &tool.capabilities { + obj.send_capability(*cap); + } + obj.send_done(); + tool.bindings.add(self, &obj); + } + + pub fn announce_pad(self: &Rc, pad: &Rc) { + macro_rules! id { + () => { + match self.client.new_id() { + Ok(id) => id, + Err(e) => { + self.client.error(e); + return; + } + } + }; + } + let obj = Rc::new(ZwpTabletPadV2 { + id: id!(), + client: self.client.clone(), + seat: self.clone(), + tracker: Default::default(), + version: self.version, + pad: pad.clone(), + entered: Cell::new(false), + }); + track!(self.client, obj); + self.client.add_server_obj(&obj); + self.send_pad_added(&obj); + obj.send_path(&pad.path); + obj.send_buttons(pad.buttons); + for group in &pad.groups { + let group_obj = Rc::new(ZwpTabletPadGroupV2 { + id: id!(), + client: self.client.clone(), + seat: self.clone(), + tracker: Default::default(), + version: self.version, + group: group.clone(), + }); + track!(self.client, group_obj); + self.client.add_server_obj(&group_obj); + obj.send_group(&group_obj); + group_obj.send_buttons(&group.buttons); + group_obj.send_modes(group.modes); + for ring in &group.rings { + let Some(ring) = pad.rings.get(*ring as usize) else { + continue; + }; + let ring_obj = Rc::new(ZwpTabletPadRingV2 { + id: id!(), + client: self.client.clone(), + seat: self.clone(), + tracker: Default::default(), + version: self.version, + ring: ring.clone(), + }); + track!(self.client, ring_obj); + self.client.add_server_obj(&ring_obj); + group_obj.send_ring(&ring_obj); + ring.bindings.add(self, &ring_obj); + } + for strip in &group.strips { + let Some(strip) = pad.strips.get(*strip as usize) else { + continue; + }; + let strip_obj = Rc::new(ZwpTabletPadStripV2 { + id: id!(), + client: self.client.clone(), + seat: self.clone(), + tracker: Default::default(), + version: self.version, + strip: strip.clone(), + }); + track!(self.client, strip_obj); + self.client.add_server_obj(&strip_obj); + group_obj.send_strip(&strip_obj); + strip.bindings.add(self, &strip_obj); + } + group_obj.send_done(); + } + obj.send_done(); + } + + fn send_tablet_added(&self, tablet: &ZwpTabletV2) { + self.client.event(TabletAdded { + self_id: self.id, + id: tablet.id, + }); + } + + fn send_tool_added(&self, tool: &ZwpTabletToolV2) { + self.client.event(ToolAdded { + self_id: self.id, + id: tool.id, + }); + } + + fn send_pad_added(&self, pad: &ZwpTabletPadV2) { + self.client.event(PadAdded { + self_id: self.id, + id: pad.id, + }); + } +} + +impl ZwpTabletSeatV2RequestHandler for ZwpTabletSeatV2 { + type Error = ZwpTabletSeatV2Error; + + fn destroy(&self, _req: Destroy, _slf: &Rc) -> Result<(), Self::Error> { + self.detach(); + self.client.remove_obj(self)?; + Ok(()) + } +} + +object_base! { + self = ZwpTabletSeatV2; + version = self.version; +} + +impl Object for ZwpTabletSeatV2 { + fn break_loops(&self) { + self.detach(); + } +} + +simple_add_obj!(ZwpTabletSeatV2); + +#[derive(Debug, Error)] +pub enum ZwpTabletSeatV2Error { + #[error(transparent)] + ClientError(Box), +} +efrom!(ZwpTabletSeatV2Error, ClientError); diff --git a/src/ifs/wl_seat/tablet/zwp_tablet_tool_v2.rs b/src/ifs/wl_seat/tablet/zwp_tablet_tool_v2.rs new file mode 100644 index 00000000..199732bb --- /dev/null +++ b/src/ifs/wl_seat/tablet/zwp_tablet_tool_v2.rs @@ -0,0 +1,252 @@ +use { + crate::{ + client::{Client, ClientError}, + cursor::Cursor, + fixed::Fixed, + ifs::{ + wl_seat::tablet::{ + zwp_tablet_seat_v2::ZwpTabletSeatV2, zwp_tablet_v2::ZwpTabletV2, + TabletToolCapability, TabletToolOpt, TabletToolType, ToolButtonState, + }, + wl_surface::{WlSurface, WlSurfaceError}, + }, + leaks::Tracker, + object::{Object, Version}, + wire::{zwp_tablet_tool_v2::*, ZwpTabletToolV2Id}, + }, + std::{cell::Cell, rc::Rc}, + thiserror::Error, +}; + +pub struct ZwpTabletToolV2 { + pub id: ZwpTabletToolV2Id, + pub client: Rc, + pub tracker: Tracker, + pub version: Version, + pub seat: Rc, + pub tool: Rc, + pub entered: Cell, +} + +pub const BTN_TOOL_PEN: u32 = 0x140; +pub const BTN_TOOL_RUBBER: u32 = 0x141; +pub const BTN_TOOL_BRUSH: u32 = 0x142; +pub const BTN_TOOL_PENCIL: u32 = 0x143; +pub const BTN_TOOL_AIRBRUSH: u32 = 0x144; +pub const BTN_TOOL_FINGER: u32 = 0x145; +pub const BTN_TOOL_MOUSE: u32 = 0x146; +pub const BTN_TOOL_LENS: u32 = 0x147; + +impl ZwpTabletToolV2 { + pub fn detach(&self) { + if let Some(tool) = self.tool.get() { + tool.bindings.remove(&self.seat); + } + } + + pub fn send_type(&self, tool_type: TabletToolType) { + self.client.event(Type { + self_id: self.id, + tool_type: match tool_type { + TabletToolType::Pen => BTN_TOOL_PEN, + TabletToolType::Eraser => BTN_TOOL_RUBBER, + TabletToolType::Brush => BTN_TOOL_BRUSH, + TabletToolType::Pencil => BTN_TOOL_PENCIL, + TabletToolType::Airbrush => BTN_TOOL_AIRBRUSH, + TabletToolType::Finger => BTN_TOOL_FINGER, + TabletToolType::Mouse => BTN_TOOL_MOUSE, + TabletToolType::Lens => BTN_TOOL_LENS, + }, + }); + } + + pub fn send_hardware_serial(&self, serial: u64) { + self.client.event(HardwareSerial { + self_id: self.id, + hardware_serial_hi: (serial >> 32) as _, + hardware_serial_lo: serial as _, + }); + } + + pub fn send_hardware_id_wacom(&self, id: u64) { + self.client.event(HardwareIdWacom { + self_id: self.id, + hardware_id_hi: (id >> 32) as _, + hardware_id_lo: id as _, + }); + } + + pub fn send_capability(&self, capability: TabletToolCapability) { + self.client.event(Capability { + self_id: self.id, + capability: match capability { + TabletToolCapability::Tilt => 1, + TabletToolCapability::Pressure => 2, + TabletToolCapability::Distance => 3, + TabletToolCapability::Rotation => 4, + TabletToolCapability::Slider => 5, + TabletToolCapability::Wheel => 6, + }, + }); + } + + pub fn send_done(&self) { + self.client.event(Done { self_id: self.id }); + } + + pub fn send_removed(&self) { + self.client.event(Removed { self_id: self.id }); + } + + pub fn send_proximity_in(&self, serial: u32, tablet: &ZwpTabletV2, surface: &WlSurface) { + self.entered.set(true); + self.client.event(ProximityIn { + self_id: self.id, + serial, + tablet: tablet.id, + surface: surface.id, + }); + } + + pub fn send_proximity_out(&self) { + self.entered.set(false); + self.client.event(ProximityOut { self_id: self.id }); + } + + pub fn send_down(&self, serial: u32) { + self.client.event(Down { + self_id: self.id, + serial, + }); + } + + pub fn send_up(&self) { + self.client.event(Up { self_id: self.id }); + } + + pub fn send_motion(&self, x: Fixed, y: Fixed) { + self.client.event(Motion { + self_id: self.id, + x, + y, + }); + } + + pub fn send_pressure(&self, pressure: u32) { + self.client.event(Pressure { + self_id: self.id, + pressure, + }); + } + + pub fn send_distance(&self, distance: u32) { + self.client.event(Distance { + self_id: self.id, + distance, + }); + } + + pub fn send_tilt(&self, tilt_x: Fixed, tilt_y: Fixed) { + self.client.event(Tilt { + self_id: self.id, + tilt_x, + tilt_y, + }); + } + + pub fn send_rotation(&self, degrees: Fixed) { + self.client.event(Rotation { + self_id: self.id, + degrees, + }); + } + + pub fn send_slider(&self, position: i32) { + self.client.event(Slider { + self_id: self.id, + position, + }); + } + + pub fn send_wheel(&self, degrees: Fixed, clicks: i32) { + self.client.event(Wheel { + self_id: self.id, + degrees, + clicks, + }); + } + + pub fn send_button(&self, serial: u32, button: u32, state: ToolButtonState) { + self.client.event(Button { + self_id: self.id, + serial, + button, + state: match state { + ToolButtonState::Released => 0, + ToolButtonState::Pressed => 1, + }, + }); + } + + pub fn send_frame(&self, time: u32) { + self.client.event(Frame { + self_id: self.id, + time, + }); + } +} + +impl ZwpTabletToolV2RequestHandler for ZwpTabletToolV2 { + type Error = ZwpTabletToolV2Error; + + fn set_cursor(&self, req: SetCursor, _slf: &Rc) -> Result<(), Self::Error> { + let Some(tool) = self.tool.get() else { + return Ok(()); + }; + if !self.seat.client.valid_serial(req.serial) { + log::warn!("Client tried to set_cursor with an invalid serial"); + return Ok(()); + } + let mut cursor_opt = None; + if req.surface.is_some() { + let surface = self.seat.client.lookup(req.surface)?; + let cursor = surface.get_cursor(&tool.cursor)?; + cursor.set_hotspot(req.hotspot_x, req.hotspot_y); + cursor_opt = Some(cursor as Rc); + } + if tool.node.get().node_client_id() != Some(self.seat.client.id) { + return Ok(()); + } + tool.cursor.set(cursor_opt); + Ok(()) + } + + fn destroy(&self, _req: Destroy, _slf: &Rc) -> Result<(), Self::Error> { + self.detach(); + self.client.remove_obj(self)?; + Ok(()) + } +} + +object_base! { + self = ZwpTabletToolV2; + version = self.version; +} + +impl Object for ZwpTabletToolV2 { + fn break_loops(&self) { + self.detach(); + } +} + +dedicated_add_obj!(ZwpTabletToolV2, ZwpTabletToolV2Id, tablet_tools); + +#[derive(Debug, Error)] +pub enum ZwpTabletToolV2Error { + #[error(transparent)] + ClientError(Box), + #[error(transparent)] + WlSurfaceError(Box), +} +efrom!(ZwpTabletToolV2Error, ClientError); +efrom!(ZwpTabletToolV2Error, WlSurfaceError); diff --git a/src/ifs/wl_seat/tablet/zwp_tablet_v2.rs b/src/ifs/wl_seat/tablet/zwp_tablet_v2.rs new file mode 100644 index 00000000..4a2157b5 --- /dev/null +++ b/src/ifs/wl_seat/tablet/zwp_tablet_v2.rs @@ -0,0 +1,86 @@ +use { + crate::{ + client::{Client, ClientError}, + ifs::wl_seat::tablet::{zwp_tablet_seat_v2::ZwpTabletSeatV2, Tablet}, + leaks::Tracker, + object::{Object, Version}, + wire::{zwp_tablet_v2::*, ZwpTabletV2Id}, + }, + std::rc::Rc, + thiserror::Error, +}; + +pub struct ZwpTabletV2 { + pub id: ZwpTabletV2Id, + pub client: Rc, + pub tracker: Tracker, + pub version: Version, + pub seat: Rc, + pub tablet: Rc, +} + +impl ZwpTabletV2 { + fn detach(&self) { + self.tablet.bindings.remove(&self.seat); + } + + pub fn send_name(&self, name: &str) { + self.client.event(Name { + self_id: self.id, + name, + }); + } + + pub fn send_id(&self, vid: u32, pid: u32) { + self.client.event(Id { + self_id: self.id, + vid, + pid, + }); + } + + pub fn send_path(&self, path: &str) { + self.client.event(Path { + self_id: self.id, + path, + }); + } + + pub fn send_done(&self) { + self.client.event(Done { self_id: self.id }); + } + + pub fn send_removed(&self) { + self.client.event(Removed { self_id: self.id }); + } +} + +impl ZwpTabletV2RequestHandler for ZwpTabletV2 { + type Error = ZwpTabletV2Error; + + fn destroy(&self, _req: Destroy, _slf: &Rc) -> Result<(), Self::Error> { + self.detach(); + self.client.remove_obj(self)?; + Ok(()) + } +} + +object_base! { + self = ZwpTabletV2; + version = self.version; +} + +impl Object for ZwpTabletV2 { + fn break_loops(&self) { + self.detach(); + } +} + +simple_add_obj!(ZwpTabletV2); + +#[derive(Debug, Error)] +pub enum ZwpTabletV2Error { + #[error(transparent)] + ClientError(Box), +} +efrom!(ZwpTabletV2Error, ClientError); diff --git a/src/ifs/wl_surface.rs b/src/ifs/wl_surface.rs index a32b5e93..5e32e964 100644 --- a/src/ifs/wl_surface.rs +++ b/src/ifs/wl_surface.rs @@ -26,9 +26,15 @@ use { wl_buffer::WlBuffer, wl_callback::WlCallback, wl_seat::{ - text_input::TextInputConnection, wl_pointer::PendingScroll, - zwp_pointer_constraints_v1::SeatConstraint, Dnd, NodeSeatState, SeatId, - WlSeatGlobal, + tablet::{ + PadButtonState, TabletPad, TabletPadGroup, TabletPadRing, TabletPadStrip, + TabletRingEventSource, TabletStripEventSource, TabletTool, TabletToolChanges, + ToolButtonState, + }, + text_input::TextInputConnection, + wl_pointer::PendingScroll, + zwp_pointer_constraints_v1::SeatConstraint, + Dnd, NodeSeatState, SeatId, WlSeatGlobal, }, wl_surface::{ commit_timeline::{ClearReason, CommitTimeline, CommitTimelineError}, @@ -1501,14 +1507,6 @@ impl Node for WlSurface { dnd.seat.dnd_surface_motion(self, dnd, time_usec, x, y); } - fn node_into_surface(self: Rc) -> Option> { - Some(self.clone()) - } - - fn node_is_xwayland_surface(&self) -> bool { - self.client.is_xwayland - } - fn node_on_swipe_begin(&self, seat: &Rc, time_usec: u64, finger_count: u32) { seat.swipe_begin_surface(self, time_usec, finger_count) } @@ -1548,6 +1546,99 @@ impl Node for WlSurface { fn node_on_hold_end(&self, seat: &Rc, time_usec: u64, cancelled: bool) { seat.hold_end_surface(self, time_usec, cancelled) } + + fn node_on_tablet_pad_enter(&self, pad: &Rc) { + pad.surface_enter(self); + } + + fn node_on_tablet_pad_leave(&self, pad: &Rc) { + pad.surface_leave(self); + } + + fn node_on_tablet_pad_button( + &self, + pad: &Rc, + time_usec: u64, + button: u32, + state: PadButtonState, + ) { + pad.surface_button(self, time_usec, button, state); + } + + fn node_on_tablet_pad_mode_switch( + &self, + pad: &Rc, + group: &Rc, + time_usec: u64, + mode: u32, + ) { + pad.surface_mode_switch(self, group, time_usec, mode); + } + + fn node_on_tablet_pad_ring( + &self, + pad: &Rc, + ring: &Rc, + source: Option, + angle: Option, + time_usec: u64, + ) { + pad.surface_ring(self, ring, source, angle, time_usec); + } + + fn node_on_tablet_pad_strip( + &self, + pad: &Rc, + strip: &Rc, + source: Option, + position: Option, + time_usec: u64, + ) { + pad.surface_strip(self, strip, source, position, time_usec); + } + + fn node_on_tablet_tool_leave(&self, tool: &Rc, time_usec: u64) { + tool.surface_leave(self, time_usec); + } + + fn node_on_tablet_tool_enter( + self: Rc, + tool: &Rc, + time_usec: u64, + x: Fixed, + y: Fixed, + ) { + tool.surface_enter(&self, time_usec, x, y); + } + + fn node_on_tablet_tool_button( + &self, + tool: &Rc, + time_usec: u64, + button: u32, + state: ToolButtonState, + ) { + tool.surface_button(self, time_usec, button, state); + } + + fn node_on_tablet_tool_apply_changes( + self: Rc, + tool: &Rc, + time_usec: u64, + changes: Option<&TabletToolChanges>, + x: Fixed, + y: Fixed, + ) { + tool.surface_apply_changes(&self, time_usec, changes, x, y); + } + + fn node_into_surface(self: Rc) -> Option> { + Some(self.clone()) + } + + fn node_is_xwayland_surface(&self) -> bool { + self.client.is_xwayland + } } #[derive(Debug, Error)] diff --git a/src/ifs/wl_surface/x_surface/xwindow.rs b/src/ifs/wl_surface/x_surface/xwindow.rs index 850f3625..d9506fbe 100644 --- a/src/ifs/wl_surface/x_surface/xwindow.rs +++ b/src/ifs/wl_surface/x_surface/xwindow.rs @@ -4,7 +4,7 @@ use { cursor::KnownCursor, fixed::Fixed, ifs::{ - wl_seat::{NodeSeatState, SeatId, WlSeatGlobal}, + wl_seat::{tablet::TabletTool, NodeSeatState, SeatId, WlSeatGlobal}, wl_surface::{x_surface::XSurface, WlSurface, WlSurfaceError}, }, rect::Rect, @@ -369,6 +369,16 @@ impl Node for Xwindow { seat.pointer_cursor().set_known(KnownCursor::Default); } + fn node_on_tablet_tool_enter( + self: Rc, + tool: &Rc, + _time_usec: u64, + _x: Fixed, + _y: Fixed, + ) { + tool.cursor().set_known(KnownCursor::Default) + } + fn node_into_toplevel(self: Rc) -> Option> { Some(self) } diff --git a/src/ifs/wl_surface/xdg_surface/xdg_popup.rs b/src/ifs/wl_surface/xdg_surface/xdg_popup.rs index 33c9b690..5b4f1ab8 100644 --- a/src/ifs/wl_surface/xdg_surface/xdg_popup.rs +++ b/src/ifs/wl_surface/xdg_surface/xdg_popup.rs @@ -4,7 +4,7 @@ use { cursor::KnownCursor, fixed::Fixed, ifs::{ - wl_seat::{NodeSeatState, WlSeatGlobal}, + wl_seat::{tablet::TabletTool, NodeSeatState, WlSeatGlobal}, wl_surface::xdg_surface::{XdgSurface, XdgSurfaceError, XdgSurfaceExt}, xdg_positioner::{ XdgPositioned, XdgPositioner, CA_FLIP_X, CA_FLIP_Y, CA_RESIZE_X, CA_RESIZE_Y, @@ -346,6 +346,16 @@ impl Node for XdgPopup { // log::info!("xdg-popup focus"); seat.pointer_cursor().set_known(KnownCursor::Default); } + + fn node_on_tablet_tool_enter( + self: Rc, + tool: &Rc, + _time_usec: u64, + _x: Fixed, + _y: Fixed, + ) { + tool.cursor().set_known(KnownCursor::Default) + } } impl StackedNode for XdgPopup { diff --git a/src/ifs/wl_surface/xdg_surface/xdg_toplevel.rs b/src/ifs/wl_surface/xdg_surface/xdg_toplevel.rs index 50f4d41f..ae1a377d 100644 --- a/src/ifs/wl_surface/xdg_surface/xdg_toplevel.rs +++ b/src/ifs/wl_surface/xdg_surface/xdg_toplevel.rs @@ -9,7 +9,7 @@ use { fixed::Fixed, ifs::{ ext_foreign_toplevel_list_v1::ExtForeignToplevelListV1, - wl_seat::{NodeSeatState, SeatId, WlSeatGlobal}, + wl_seat::{tablet::TabletTool, NodeSeatState, SeatId, WlSeatGlobal}, wl_surface::{ xdg_surface::{ xdg_toplevel::xdg_dialog_v1::XdgDialogV1, XdgSurface, XdgSurfaceError, @@ -529,6 +529,16 @@ impl Node for XdgToplevel { seat.pointer_cursor().set_known(KnownCursor::Default); } + fn node_on_tablet_tool_enter( + self: Rc, + tool: &Rc, + _time_usec: u64, + _x: Fixed, + _y: Fixed, + ) { + tool.cursor().set_known(KnownCursor::Default) + } + fn node_into_toplevel(self: Rc) -> Option> { Some(self) } diff --git a/src/ifs/wp_cursor_shape_device_v1.rs b/src/ifs/wp_cursor_shape_device_v1.rs index 8524d2ae..aa47e892 100644 --- a/src/ifs/wp_cursor_shape_device_v1.rs +++ b/src/ifs/wp_cursor_shape_device_v1.rs @@ -2,7 +2,7 @@ use { crate::{ client::{Client, ClientError}, cursor::KnownCursor, - ifs::wl_seat::WlSeatGlobal, + ifs::wl_seat::{tablet::TabletToolOpt, WlSeatGlobal}, leaks::Tracker, object::{Object, Version}, wire::{wp_cursor_shape_device_v1::*, WpCursorShapeDeviceV1Id}, @@ -46,10 +46,15 @@ const ALL_SCROLL: u32 = 32; const ZOOM_IN: u32 = 33; const ZOOM_OUT: u32 = 34; +pub enum CursorShapeCursorUser { + Seat(Rc), + TabletTool(Rc), +} + pub struct WpCursorShapeDeviceV1 { pub id: WpCursorShapeDeviceV1Id, pub client: Rc, - pub seat: Rc, + pub cursor_user: CursorShapeCursorUser, pub tracker: Tracker, pub version: Version, } @@ -100,14 +105,24 @@ impl WpCursorShapeDeviceV1RequestHandler for WpCursorShapeDeviceV1 { ZOOM_OUT => KnownCursor::ZoomOut, _ => return Err(WpCursorShapeDeviceV1Error::UnknownShape(req.shape)), }; - let pointer_node = match self.seat.pointer_node() { - Some(n) => n, - _ => return Ok(()), + let tablet_tool; + let (node_client_id, user) = match &self.cursor_user { + CursorShapeCursorUser::Seat(s) => match s.pointer_node() { + Some(n) => (n.node_client_id(), s.pointer_cursor()), + _ => return Ok(()), + }, + CursorShapeCursorUser::TabletTool(t) => match t.get() { + Some(t) => { + tablet_tool = t; + (tablet_tool.node().node_client_id(), tablet_tool.cursor()) + } + _ => return Ok(()), + }, }; - if pointer_node.node_client_id() != Some(self.client.id) { + if node_client_id != Some(self.client.id) { return Ok(()); } - self.seat.pointer_cursor().set_known(cursor); + user.set_known(cursor); Ok(()) } } diff --git a/src/ifs/wp_cursor_shape_manager_v1.rs b/src/ifs/wp_cursor_shape_manager_v1.rs index 301a939b..b9c612cd 100644 --- a/src/ifs/wp_cursor_shape_manager_v1.rs +++ b/src/ifs/wp_cursor_shape_manager_v1.rs @@ -2,10 +2,10 @@ use { crate::{ client::{Client, ClientError}, globals::{Global, GlobalName}, - ifs::wp_cursor_shape_device_v1::WpCursorShapeDeviceV1, + ifs::wp_cursor_shape_device_v1::{CursorShapeCursorUser, WpCursorShapeDeviceV1}, leaks::Tracker, object::{Object, Version}, - wire::{wp_cursor_shape_manager_v1::*, WpCursorShapeManagerV1Id}, + wire::{wp_cursor_shape_manager_v1::*, WpCursorShapeDeviceV1Id, WpCursorShapeManagerV1Id}, }, std::rc::Rc, thiserror::Error, @@ -63,6 +63,25 @@ pub struct WpCursorShapeManagerV1 { pub version: Version, } +impl WpCursorShapeManagerV1 { + fn get( + &self, + id: WpCursorShapeDeviceV1Id, + cursor_user: CursorShapeCursorUser, + ) -> Result<(), WpCursorShapeManagerV1Error> { + let device = Rc::new(WpCursorShapeDeviceV1 { + id, + client: self.client.clone(), + cursor_user, + tracker: Default::default(), + version: self.version, + }); + track!(self.client, device); + self.client.add_client_obj(&device)?; + Ok(()) + } +} + impl WpCursorShapeManagerV1RequestHandler for WpCursorShapeManagerV1 { type Error = WpCursorShapeManagerV1Error; @@ -73,24 +92,18 @@ impl WpCursorShapeManagerV1RequestHandler for WpCursorShapeManagerV1 { fn get_pointer(&self, req: GetPointer, _slf: &Rc) -> Result<(), Self::Error> { let pointer = self.client.lookup(req.pointer)?; - let device = Rc::new(WpCursorShapeDeviceV1 { - id: req.cursor_shape_device, - client: self.client.clone(), - seat: pointer.seat.global.clone(), - tracker: Default::default(), - version: self.version, - }); - track!(self.client, device); - self.client.add_client_obj(&device)?; - Ok(()) + self.get( + req.cursor_shape_device, + CursorShapeCursorUser::Seat(pointer.seat.global.clone()), + ) } - fn get_tablet_tool_v2( - &self, - _req: GetTabletToolV2, - _slf: &Rc, - ) -> Result<(), Self::Error> { - Err(WpCursorShapeManagerV1Error::TabletToolNotSupported) + fn get_tablet_tool_v2(&self, req: GetTabletToolV2, _slf: &Rc) -> Result<(), Self::Error> { + let tool = self.client.lookup(req.tablet_tool)?; + self.get( + req.cursor_shape_device, + CursorShapeCursorUser::TabletTool(tool.tool.clone()), + ) } } @@ -107,7 +120,5 @@ simple_add_obj!(WpCursorShapeManagerV1); pub enum WpCursorShapeManagerV1Error { #[error(transparent)] ClientError(Box), - #[error("This compositor does not support tablet tools")] - TabletToolNotSupported, } efrom!(WpCursorShapeManagerV1Error, ClientError); diff --git a/src/libinput/device.rs b/src/libinput/device.rs index 73eb1965..76f813f9 100644 --- a/src/libinput/device.rs +++ b/src/libinput/device.rs @@ -21,9 +21,19 @@ use { libinput_device_config_tap_get_enabled, libinput_device_config_tap_get_finger_count, libinput_device_config_tap_set_drag_enabled, libinput_device_config_tap_set_drag_lock_enabled, - libinput_device_config_tap_set_enabled, libinput_device_get_name, - libinput_device_get_user_data, libinput_device_has_capability, - libinput_device_set_user_data, libinput_device_unref, libinput_path_remove_device, + libinput_device_config_tap_set_enabled, libinput_device_get_device_group, + libinput_device_get_id_product, libinput_device_get_id_vendor, + libinput_device_get_name, libinput_device_get_user_data, libinput_device_group, + libinput_device_group_get_user_data, libinput_device_group_set_user_data, + libinput_device_has_capability, libinput_device_set_user_data, + libinput_device_tablet_pad_get_mode_group, libinput_device_tablet_pad_get_num_buttons, + libinput_device_tablet_pad_get_num_mode_groups, + libinput_device_tablet_pad_get_num_rings, libinput_device_tablet_pad_get_num_strips, + libinput_device_unref, libinput_path_remove_device, libinput_tablet_pad_mode_group, + libinput_tablet_pad_mode_group_get_index, libinput_tablet_pad_mode_group_get_mode, + libinput_tablet_pad_mode_group_get_num_modes, + libinput_tablet_pad_mode_group_has_button, libinput_tablet_pad_mode_group_has_ring, + libinput_tablet_pad_mode_group_has_strip, }, LibInput, }, @@ -36,6 +46,16 @@ pub struct LibInputDevice<'a> { pub(super) _phantom: PhantomData<&'a ()>, } +pub struct LibInputDeviceGroup<'a> { + pub(super) group: *mut libinput_device_group, + pub(super) _phantom: PhantomData<&'a ()>, +} + +pub struct LibInputTabletPadModeGroup<'a> { + pub(super) group: *mut libinput_tablet_pad_mode_group, + pub(super) _phantom: PhantomData<&'a ()>, +} + pub struct RegisteredDevice { pub(super) _li: Rc, pub(super) dev: *mut libinput_device, @@ -191,6 +211,96 @@ impl<'a> LibInputDevice<'a> { pub fn has_natural_scrolling(&self) -> bool { unsafe { libinput_device_config_scroll_has_natural_scroll(self.dev) != 0 } } + + pub fn device_group(&self) -> LibInputDeviceGroup<'_> { + LibInputDeviceGroup { + group: unsafe { libinput_device_get_device_group(self.dev) }, + _phantom: Default::default(), + } + } + + pub fn product(&self) -> u32 { + unsafe { libinput_device_get_id_product(self.dev) as u32 } + } + + pub fn vendor(&self) -> u32 { + unsafe { libinput_device_get_id_vendor(self.dev) as u32 } + } + + pub fn pad_num_buttons(&self) -> u32 { + match unsafe { libinput_device_tablet_pad_get_num_buttons(self.dev) } { + -1 => 0, + n => n as u32, + } + } + + pub fn pad_num_rings(&self) -> u32 { + match unsafe { libinput_device_tablet_pad_get_num_rings(self.dev) } { + -1 => 0, + n => n as u32, + } + } + + pub fn pad_num_strips(&self) -> u32 { + match unsafe { libinput_device_tablet_pad_get_num_strips(self.dev) } { + -1 => 0, + n => n as u32, + } + } + + pub fn pad_num_mode_groups(&self) -> u32 { + match unsafe { libinput_device_tablet_pad_get_num_mode_groups(self.dev) } { + -1 => 0, + n => n as u32, + } + } + + pub fn pad_mode_group(&self, group: u32) -> Option> { + let group = unsafe { libinput_device_tablet_pad_get_mode_group(self.dev, group as _) }; + if group.is_null() { + return None; + } + Some(LibInputTabletPadModeGroup { + group, + _phantom: Default::default(), + }) + } +} + +impl<'a> LibInputDeviceGroup<'a> { + pub fn user_data(&self) -> usize { + unsafe { libinput_device_group_get_user_data(self.group) } + } + + pub fn set_user_data(&self, user_data: usize) { + unsafe { libinput_device_group_set_user_data(self.group, user_data) } + } +} + +impl<'a> LibInputTabletPadModeGroup<'a> { + pub fn index(&self) -> u32 { + unsafe { libinput_tablet_pad_mode_group_get_index(self.group) as u32 } + } + + pub fn num_modes(&self) -> u32 { + unsafe { libinput_tablet_pad_mode_group_get_num_modes(self.group) as u32 } + } + + pub fn mode(&self) -> u32 { + unsafe { libinput_tablet_pad_mode_group_get_mode(self.group) as u32 } + } + + pub fn has_button(&self, button: u32) -> bool { + unsafe { libinput_tablet_pad_mode_group_has_button(self.group, button as _) != 0 } + } + + pub fn has_ring(&self, ring: u32) -> bool { + unsafe { libinput_tablet_pad_mode_group_has_ring(self.group, ring as _) != 0 } + } + + pub fn has_strip(&self, strip: u32) -> bool { + unsafe { libinput_tablet_pad_mode_group_has_strip(self.group, strip as _) != 0 } + } } impl RegisteredDevice { diff --git a/src/libinput/event.rs b/src/libinput/event.rs index 60038fb6..45885c48 100644 --- a/src/libinput/event.rs +++ b/src/libinput/event.rs @@ -1,7 +1,11 @@ use { crate::libinput::{ - consts::{ButtonState, EventType, KeyState, PointerAxis, Switch, SwitchState}, - device::LibInputDevice, + consts::{ + ButtonState, EventType, KeyState, PointerAxis, Switch, SwitchState, + TabletPadRingAxisSource, TabletPadStripAxisSource, TabletToolProximityState, + TabletToolTipState, TabletToolType, + }, + device::{LibInputDevice, LibInputTabletPadModeGroup}, sys::{ libinput_event, libinput_event_destroy, libinput_event_gesture, libinput_event_gesture_get_angle_delta, libinput_event_gesture_get_cancelled, @@ -11,6 +15,7 @@ use { libinput_event_gesture_get_time_usec, libinput_event_get_device, libinput_event_get_gesture_event, libinput_event_get_keyboard_event, libinput_event_get_pointer_event, libinput_event_get_switch_event, + libinput_event_get_tablet_pad_event, libinput_event_get_tablet_tool_event, libinput_event_get_type, libinput_event_keyboard, libinput_event_keyboard_get_key, libinput_event_keyboard_get_key_state, libinput_event_keyboard_get_time_usec, libinput_event_pointer, libinput_event_pointer_get_button, @@ -20,7 +25,25 @@ use { libinput_event_pointer_get_scroll_value_v120, libinput_event_pointer_get_time_usec, libinput_event_pointer_has_axis, libinput_event_switch, libinput_event_switch_get_switch, libinput_event_switch_get_switch_state, - libinput_event_switch_get_time_usec, + libinput_event_switch_get_time_usec, libinput_event_tablet_pad, + libinput_event_tablet_pad_get_button_number, + libinput_event_tablet_pad_get_button_state, libinput_event_tablet_pad_get_mode, + libinput_event_tablet_pad_get_mode_group, libinput_event_tablet_pad_get_ring_number, + libinput_event_tablet_pad_get_ring_position, libinput_event_tablet_pad_get_ring_source, + libinput_event_tablet_pad_get_strip_number, + libinput_event_tablet_pad_get_strip_position, + libinput_event_tablet_pad_get_strip_source, libinput_event_tablet_pad_get_time_usec, + libinput_event_tablet_tool, libinput_event_tablet_tool_get_button, + libinput_event_tablet_tool_get_button_state, + libinput_event_tablet_tool_get_proximity_state, + libinput_event_tablet_tool_get_time_usec, libinput_event_tablet_tool_get_tip_state, + libinput_event_tablet_tool_get_tool, + libinput_event_tablet_tool_get_wheel_delta_discrete, + libinput_event_tablet_tool_get_x_transformed, + libinput_event_tablet_tool_get_y_transformed, libinput_tablet_tool, + libinput_tablet_tool_get_serial, libinput_tablet_tool_get_tool_id, + libinput_tablet_tool_get_type, libinput_tablet_tool_get_user_data, + libinput_tablet_tool_set_user_data, }, }, std::marker::PhantomData, @@ -51,6 +74,21 @@ pub struct LibInputEventSwitch<'a> { pub(super) _phantom: PhantomData<&'a ()>, } +pub struct LibInputEventTabletTool<'a> { + pub(super) event: *mut libinput_event_tablet_tool, + pub(super) _phantom: PhantomData<&'a ()>, +} + +pub struct LibInputEventTabletPad<'a> { + pub(super) event: *mut libinput_event_tablet_pad, + pub(super) _phantom: PhantomData<&'a ()>, +} + +pub struct LibInputTabletTool<'a> { + pub(super) tool: *mut libinput_tablet_tool, + pub(super) _phantom: PhantomData<&'a ()>, +} + impl<'a> Drop for LibInputEvent<'a> { fn drop(&mut self) { unsafe { @@ -59,6 +97,22 @@ impl<'a> Drop for LibInputEvent<'a> { } } +macro_rules! converter { + ($name:ident, $out:ident, $f:ident) => { + pub fn $name(&self) -> Option<$out> { + let res = unsafe { $f(self.event) }; + if res.is_null() { + None + } else { + Some($out { + event: res, + _phantom: Default::default(), + }) + } + } + }; +} + impl<'a> LibInputEvent<'a> { pub fn ty(&self) -> EventType { unsafe { EventType(libinput_event_get_type(self.event)) } @@ -71,53 +125,36 @@ impl<'a> LibInputEvent<'a> { } } - pub fn keyboard_event(&self) -> Option { - let res = unsafe { libinput_event_get_keyboard_event(self.event) }; - if res.is_null() { - None - } else { - Some(LibInputEventKeyboard { - event: res, - _phantom: Default::default(), - }) - } - } - - pub fn pointer_event(&self) -> Option { - let res = unsafe { libinput_event_get_pointer_event(self.event) }; - if res.is_null() { - None - } else { - Some(LibInputEventPointer { - event: res, - _phantom: Default::default(), - }) - } - } - - pub fn gesture_event(&self) -> Option { - let res = unsafe { libinput_event_get_gesture_event(self.event) }; - if res.is_null() { - None - } else { - Some(LibInputEventGesture { - event: res, - _phantom: Default::default(), - }) - } - } - - pub fn switch_event(&self) -> Option { - let res = unsafe { libinput_event_get_switch_event(self.event) }; - if res.is_null() { - None - } else { - Some(LibInputEventSwitch { - event: res, - _phantom: Default::default(), - }) - } - } + converter!( + keyboard_event, + LibInputEventKeyboard, + libinput_event_get_keyboard_event + ); + converter!( + pointer_event, + LibInputEventPointer, + libinput_event_get_pointer_event + ); + converter!( + gesture_event, + LibInputEventGesture, + libinput_event_get_gesture_event + ); + converter!( + switch_event, + LibInputEventSwitch, + libinput_event_get_switch_event + ); + converter!( + tablet_tool_event, + LibInputEventTabletTool, + libinput_event_get_tablet_tool_event + ); + converter!( + tablet_pad_event, + LibInputEventTabletPad, + libinput_event_get_tablet_pad_event + ); } impl<'a> LibInputEventKeyboard<'a> { @@ -228,3 +265,205 @@ impl<'a> LibInputEventSwitch<'a> { unsafe { SwitchState(libinput_event_switch_get_switch_state(self.event)) } } } + +macro_rules! has_changed { + ($name:ident, $f:ident) => { + pub fn $name(&self) -> bool { + unsafe { crate::libinput::sys::$f(self.event) != 0 } + } + }; +} + +macro_rules! get_double { + ($name:ident, $f:ident) => { + pub fn $name(&self) -> f64 { + unsafe { crate::libinput::sys::$f(self.event) } + } + }; +} + +macro_rules! has_capability { + ($name:ident, $f:ident) => { + pub fn $name(&self) -> bool { + unsafe { crate::libinput::sys::$f(self.tool) != 0 } + } + }; +} + +impl<'a> LibInputTabletTool<'a> { + pub fn user_data(&self) -> usize { + unsafe { libinput_tablet_tool_get_user_data(self.tool) } + } + + pub fn set_user_data(&self, user_data: usize) { + unsafe { libinput_tablet_tool_set_user_data(self.tool, user_data) } + } + + pub fn type_(&self) -> TabletToolType { + unsafe { TabletToolType(libinput_tablet_tool_get_type(self.tool)) } + } + + pub fn tool_id(&self) -> u64 { + unsafe { libinput_tablet_tool_get_tool_id(self.tool) } + } + + pub fn serial(&self) -> u64 { + unsafe { libinput_tablet_tool_get_serial(self.tool) } + } + + has_capability!(has_pressure, libinput_tablet_tool_has_pressure); + has_capability!(has_distance, libinput_tablet_tool_has_distance); + has_capability!(has_tilt, libinput_tablet_tool_has_tilt); + has_capability!(has_rotation, libinput_tablet_tool_has_rotation); + has_capability!(has_slider, libinput_tablet_tool_has_slider); + // has_capability!(has_size, libinput_tablet_tool_has_size); + has_capability!(has_wheel, libinput_tablet_tool_has_wheel); +} + +impl<'a> LibInputEventTabletTool<'a> { + pub fn tool(&self) -> LibInputTabletTool<'_> { + LibInputTabletTool { + tool: unsafe { libinput_event_tablet_tool_get_tool(self.event) }, + _phantom: Default::default(), + } + } + + pub fn time_usec(&self) -> u64 { + unsafe { libinput_event_tablet_tool_get_time_usec(self.event) } + } + + has_changed!(x_has_changed, libinput_event_tablet_tool_x_has_changed); + has_changed!(y_has_changed, libinput_event_tablet_tool_y_has_changed); + has_changed!( + pressure_has_changed, + libinput_event_tablet_tool_pressure_has_changed + ); + has_changed!( + distance_has_changed, + libinput_event_tablet_tool_distance_has_changed + ); + has_changed!( + tilt_x_has_changed, + libinput_event_tablet_tool_tilt_x_has_changed + ); + has_changed!( + tilt_y_has_changed, + libinput_event_tablet_tool_tilt_y_has_changed + ); + has_changed!( + rotation_has_changed, + libinput_event_tablet_tool_rotation_has_changed + ); + has_changed!( + slider_has_changed, + libinput_event_tablet_tool_slider_has_changed + ); + // has_changed!( + // size_major_has_changed, + // libinput_event_tablet_tool_size_major_has_changed + // ); + // has_changed!( + // size_minor_has_changed, + // libinput_event_tablet_tool_size_minor_has_changed + // ); + has_changed!( + wheel_has_changed, + libinput_event_tablet_tool_wheel_has_changed + ); + + // get_double!(x, libinput_event_tablet_tool_get_x); + // get_double!(y, libinput_event_tablet_tool_get_y); + get_double!(dx, libinput_event_tablet_tool_get_dx); + get_double!(dy, libinput_event_tablet_tool_get_dy); + get_double!(pressure, libinput_event_tablet_tool_get_pressure); + get_double!(distance, libinput_event_tablet_tool_get_distance); + get_double!(tilt_x, libinput_event_tablet_tool_get_tilt_x); + get_double!(tilt_y, libinput_event_tablet_tool_get_tilt_y); + get_double!(rotation, libinput_event_tablet_tool_get_rotation); + get_double!( + slider_position, + libinput_event_tablet_tool_get_slider_position + ); + // get_double!(size_major, libinput_event_tablet_tool_get_size_major); + // get_double!(size_minor, libinput_event_tablet_tool_get_size_minor); + get_double!(wheel_delta, libinput_event_tablet_tool_get_wheel_delta); + + pub fn wheel_delta_discrete(&self) -> i32 { + unsafe { libinput_event_tablet_tool_get_wheel_delta_discrete(self.event) as _ } + } + + pub fn x_transformed(&self, width: u32) -> f64 { + unsafe { libinput_event_tablet_tool_get_x_transformed(self.event, width) } + } + + pub fn y_transformed(&self, width: u32) -> f64 { + unsafe { libinput_event_tablet_tool_get_y_transformed(self.event, width) } + } + + pub fn proximity_state(&self) -> TabletToolProximityState { + unsafe { + TabletToolProximityState(libinput_event_tablet_tool_get_proximity_state(self.event)) + } + } + + pub fn tip_state(&self) -> TabletToolTipState { + unsafe { TabletToolTipState(libinput_event_tablet_tool_get_tip_state(self.event)) } + } + + pub fn button(&self) -> u32 { + unsafe { libinput_event_tablet_tool_get_button(self.event) } + } + + pub fn button_state(&self) -> ButtonState { + unsafe { ButtonState(libinput_event_tablet_tool_get_button_state(self.event)) } + } +} + +impl<'a> LibInputEventTabletPad<'a> { + pub fn time_usec(&self) -> u64 { + unsafe { libinput_event_tablet_pad_get_time_usec(self.event) } + } + + pub fn ring_position(&self) -> f64 { + unsafe { libinput_event_tablet_pad_get_ring_position(self.event) } + } + + pub fn ring_number(&self) -> u32 { + unsafe { libinput_event_tablet_pad_get_ring_number(self.event) as u32 } + } + + pub fn ring_source(&self) -> TabletPadRingAxisSource { + unsafe { TabletPadRingAxisSource(libinput_event_tablet_pad_get_ring_source(self.event)) } + } + + pub fn strip_position(&self) -> f64 { + unsafe { libinput_event_tablet_pad_get_strip_position(self.event) } + } + + pub fn strip_number(&self) -> u32 { + unsafe { libinput_event_tablet_pad_get_strip_number(self.event) as u32 } + } + + pub fn strip_source(&self) -> TabletPadStripAxisSource { + unsafe { TabletPadStripAxisSource(libinput_event_tablet_pad_get_strip_source(self.event)) } + } + + pub fn button_number(&self) -> u32 { + unsafe { libinput_event_tablet_pad_get_button_number(self.event) } + } + + pub fn button_state(&self) -> ButtonState { + unsafe { ButtonState(libinput_event_tablet_pad_get_button_state(self.event)) } + } + + pub fn mode(&self) -> u32 { + unsafe { libinput_event_tablet_pad_get_mode(self.event) as u32 } + } + + pub fn mode_group(&self) -> LibInputTabletPadModeGroup { + LibInputTabletPadModeGroup { + group: unsafe { libinput_event_tablet_pad_get_mode_group(self.event) }, + _phantom: Default::default(), + } + } +} diff --git a/src/libinput/sys.rs b/src/libinput/sys.rs index 31d860f2..c937abb3 100644 --- a/src/libinput/sys.rs +++ b/src/libinput/sys.rs @@ -9,6 +9,8 @@ pub struct libinput(u8); #[repr(transparent)] pub struct libinput_device(u8); #[repr(transparent)] +pub struct libinput_device_group(u8); +#[repr(transparent)] pub struct libinput_event(u8); #[repr(transparent)] pub struct libinput_event_keyboard(u8); @@ -18,6 +20,16 @@ pub struct libinput_event_pointer(u8); pub struct libinput_event_gesture(u8); #[repr(transparent)] pub struct libinput_event_switch(u8); +#[repr(transparent)] +pub struct libinput_event_tablet_tool(u8); +#[repr(transparent)] +pub struct libinput_event_tablet_pad(u8); +#[repr(transparent)] +pub struct libinput_tablet_pad_mode_group(u8); +#[repr(transparent)] +pub struct libinput_tablet_tool(u8); +// #[repr(transparent)] +// pub struct libinput_tablet_pad(u8); #[link(name = "input")] extern "C" { @@ -166,6 +178,185 @@ extern "C" { event: *mut libinput_event_switch, ) -> libinput_switch_state; pub fn libinput_event_switch_get_time_usec(event: *mut libinput_event_switch) -> u64; + + pub fn libinput_device_get_device_group( + device: *mut libinput_device, + ) -> *mut libinput_device_group; + pub fn libinput_device_group_set_user_data(group: *mut libinput_device_group, user_data: usize); + pub fn libinput_device_group_get_user_data(group: *mut libinput_device_group) -> usize; + + pub fn libinput_device_get_id_product(device: *mut libinput_device) -> c::c_uint; + pub fn libinput_device_get_id_vendor(device: *mut libinput_device) -> c::c_uint; + + pub fn libinput_event_get_tablet_tool_event( + event: *mut libinput_event, + ) -> *mut libinput_event_tablet_tool; + pub fn libinput_event_get_tablet_pad_event( + event: *mut libinput_event, + ) -> *mut libinput_event_tablet_pad; + pub fn libinput_event_tablet_tool_get_tool( + event: *mut libinput_event_tablet_tool, + ) -> *mut libinput_tablet_tool; + pub fn libinput_event_tablet_pad_get_mode_group( + event: *mut libinput_event_tablet_pad, + ) -> *mut libinput_tablet_pad_mode_group; + pub fn libinput_event_tablet_tool_x_has_changed( + event: *mut libinput_event_tablet_tool, + ) -> c::c_int; + pub fn libinput_event_tablet_tool_y_has_changed( + event: *mut libinput_event_tablet_tool, + ) -> c::c_int; + pub fn libinput_event_tablet_tool_pressure_has_changed( + event: *mut libinput_event_tablet_tool, + ) -> c::c_int; + pub fn libinput_event_tablet_tool_distance_has_changed( + event: *mut libinput_event_tablet_tool, + ) -> c::c_int; + pub fn libinput_event_tablet_tool_tilt_x_has_changed( + event: *mut libinput_event_tablet_tool, + ) -> c::c_int; + pub fn libinput_event_tablet_tool_tilt_y_has_changed( + event: *mut libinput_event_tablet_tool, + ) -> c::c_int; + pub fn libinput_event_tablet_tool_rotation_has_changed( + event: *mut libinput_event_tablet_tool, + ) -> c::c_int; + pub fn libinput_event_tablet_tool_slider_has_changed( + event: *mut libinput_event_tablet_tool, + ) -> c::c_int; + // pub fn libinput_event_tablet_tool_size_major_has_changed( + // event: *mut libinput_event_tablet_tool, + // ) -> c::c_int; + // pub fn libinput_event_tablet_tool_size_minor_has_changed( + // event: *mut libinput_event_tablet_tool, + // ) -> c::c_int; + pub fn libinput_event_tablet_tool_wheel_has_changed( + event: *mut libinput_event_tablet_tool, + ) -> c::c_int; + // pub fn libinput_event_tablet_tool_get_x(event: *mut libinput_event_tablet_tool) -> f64; + // pub fn libinput_event_tablet_tool_get_y(event: *mut libinput_event_tablet_tool) -> f64; + pub fn libinput_event_tablet_tool_get_dx(event: *mut libinput_event_tablet_tool) -> f64; + pub fn libinput_event_tablet_tool_get_dy(event: *mut libinput_event_tablet_tool) -> f64; + pub fn libinput_event_tablet_tool_get_pressure(event: *mut libinput_event_tablet_tool) -> f64; + pub fn libinput_event_tablet_tool_get_distance(event: *mut libinput_event_tablet_tool) -> f64; + pub fn libinput_event_tablet_tool_get_tilt_x(event: *mut libinput_event_tablet_tool) -> f64; + pub fn libinput_event_tablet_tool_get_tilt_y(event: *mut libinput_event_tablet_tool) -> f64; + pub fn libinput_event_tablet_tool_get_rotation(event: *mut libinput_event_tablet_tool) -> f64; + pub fn libinput_event_tablet_tool_get_slider_position( + event: *mut libinput_event_tablet_tool, + ) -> f64; + // pub fn libinput_event_tablet_tool_get_size_major(event: *mut libinput_event_tablet_tool) + // -> f64; + // pub fn libinput_event_tablet_tool_get_size_minor(event: *mut libinput_event_tablet_tool) + // -> f64; + pub fn libinput_event_tablet_tool_get_wheel_delta( + event: *mut libinput_event_tablet_tool, + ) -> f64; + pub fn libinput_event_tablet_tool_get_wheel_delta_discrete( + event: *mut libinput_event_tablet_tool, + ) -> c::c_int; + pub fn libinput_event_tablet_tool_get_x_transformed( + event: *mut libinput_event_tablet_tool, + width: u32, + ) -> f64; + pub fn libinput_event_tablet_tool_get_y_transformed( + event: *mut libinput_event_tablet_tool, + width: u32, + ) -> f64; + pub fn libinput_event_tablet_tool_get_proximity_state( + event: *mut libinput_event_tablet_tool, + ) -> libinput_tablet_tool_proximity_state; + pub fn libinput_event_tablet_tool_get_tip_state( + event: *mut libinput_event_tablet_tool, + ) -> libinput_tablet_tool_tip_state; + pub fn libinput_event_tablet_tool_get_button(event: *mut libinput_event_tablet_tool) -> u32; + pub fn libinput_event_tablet_tool_get_button_state( + event: *mut libinput_event_tablet_tool, + ) -> libinput_button_state; + // pub fn libinput_event_tablet_tool_get_seat_button_count( + // event: *mut libinput_event_tablet_tool, + // ) -> u32; + pub fn libinput_event_tablet_tool_get_time_usec(event: *mut libinput_event_tablet_tool) -> u64; + pub fn libinput_tablet_tool_get_type( + tool: *mut libinput_tablet_tool, + ) -> libinput_tablet_tool_type; + pub fn libinput_tablet_tool_get_tool_id(tool: *mut libinput_tablet_tool) -> u64; + pub fn libinput_tablet_tool_has_pressure(tool: *mut libinput_tablet_tool) -> c::c_int; + pub fn libinput_tablet_tool_has_distance(tool: *mut libinput_tablet_tool) -> c::c_int; + pub fn libinput_tablet_tool_has_tilt(tool: *mut libinput_tablet_tool) -> c::c_int; + pub fn libinput_tablet_tool_has_rotation(tool: *mut libinput_tablet_tool) -> c::c_int; + pub fn libinput_tablet_tool_has_slider(tool: *mut libinput_tablet_tool) -> c::c_int; + // pub fn libinput_tablet_tool_has_size(tool: *mut libinput_tablet_tool) -> c::c_int; + pub fn libinput_tablet_tool_has_wheel(tool: *mut libinput_tablet_tool) -> c::c_int; + // pub fn libinput_tablet_tool_has_button(tool: *mut libinput_tablet_tool, code: u32) -> c::c_int; + // pub fn libinput_tablet_tool_is_unique(tool: *mut libinput_tablet_tool) -> c::c_int; + pub fn libinput_tablet_tool_get_serial(tool: *mut libinput_tablet_tool) -> u64; + pub fn libinput_tablet_tool_get_user_data(tool: *mut libinput_tablet_tool) -> usize; + pub fn libinput_tablet_tool_set_user_data(tool: *mut libinput_tablet_tool, user_data: usize); + pub fn libinput_event_tablet_pad_get_ring_position( + event: *mut libinput_event_tablet_pad, + ) -> f64; + pub fn libinput_event_tablet_pad_get_ring_number( + event: *mut libinput_event_tablet_pad, + ) -> c::c_uint; + pub fn libinput_event_tablet_pad_get_ring_source( + event: *mut libinput_event_tablet_pad, + ) -> libinput_tablet_pad_ring_axis_source; + pub fn libinput_event_tablet_pad_get_strip_position( + event: *mut libinput_event_tablet_pad, + ) -> f64; + pub fn libinput_event_tablet_pad_get_strip_number( + event: *mut libinput_event_tablet_pad, + ) -> c::c_uint; + pub fn libinput_event_tablet_pad_get_strip_source( + event: *mut libinput_event_tablet_pad, + ) -> libinput_tablet_pad_strip_axis_source; + pub fn libinput_event_tablet_pad_get_button_number( + event: *mut libinput_event_tablet_pad, + ) -> u32; + pub fn libinput_event_tablet_pad_get_button_state( + event: *mut libinput_event_tablet_pad, + ) -> libinput_button_state; + // pub fn libinput_event_tablet_pad_get_key(event: *mut libinput_event_tablet_pad) -> u32; + // pub fn libinput_event_tablet_pad_get_key_state( + // event: *mut libinput_event_tablet_pad, + // ) -> libinput_key_state; + pub fn libinput_event_tablet_pad_get_mode(event: *mut libinput_event_tablet_pad) -> c::c_uint; + pub fn libinput_event_tablet_pad_get_time_usec(event: *mut libinput_event_tablet_pad) -> u64; + pub fn libinput_device_tablet_pad_get_mode_group( + device: *mut libinput_device, + index: c::c_uint, + ) -> *mut libinput_tablet_pad_mode_group; + pub fn libinput_device_tablet_pad_get_num_mode_groups(device: *mut libinput_device) + -> c::c_int; + pub fn libinput_device_tablet_pad_get_num_buttons(device: *mut libinput_device) -> c::c_int; + pub fn libinput_device_tablet_pad_get_num_rings(device: *mut libinput_device) -> c::c_int; + pub fn libinput_device_tablet_pad_get_num_strips(device: *mut libinput_device) -> c::c_int; + pub fn libinput_tablet_pad_mode_group_get_index( + group: *mut libinput_tablet_pad_mode_group, + ) -> c::c_uint; + pub fn libinput_tablet_pad_mode_group_get_num_modes( + group: *mut libinput_tablet_pad_mode_group, + ) -> c::c_uint; + pub fn libinput_tablet_pad_mode_group_get_mode( + group: *mut libinput_tablet_pad_mode_group, + ) -> c::c_uint; + pub fn libinput_tablet_pad_mode_group_has_button( + group: *mut libinput_tablet_pad_mode_group, + button: c::c_uint, + ) -> c::c_int; + pub fn libinput_tablet_pad_mode_group_has_ring( + group: *mut libinput_tablet_pad_mode_group, + ring: c::c_uint, + ) -> c::c_int; + pub fn libinput_tablet_pad_mode_group_has_strip( + group: *mut libinput_tablet_pad_mode_group, + strip: c::c_uint, + ) -> c::c_int; + // pub fn libinput_tablet_pad_mode_group_button_is_toggle( + // group: *mut libinput_tablet_pad_mode_group, + // button: c::c_uint, + // ) -> c::c_int; } #[repr(C)] diff --git a/src/state.rs b/src/state.rs index ce823911..e6a9c5f3 100644 --- a/src/state.rs +++ b/src/state.rs @@ -4,7 +4,8 @@ use { async_engine::{AsyncEngine, SpawnedFuture}, backend::{ Backend, BackendDrmDevice, BackendEvent, Connector, ConnectorId, ConnectorIds, - DrmDeviceId, DrmDeviceIds, InputDevice, InputDeviceId, InputDeviceIds, MonitorInfo, + DrmDeviceId, DrmDeviceIds, InputDevice, InputDeviceGroupIds, InputDeviceId, + InputDeviceIds, MonitorInfo, }, backends::dummy::DummyBackend, cli::RunArgs, @@ -34,7 +35,10 @@ use { jay_workspace_watcher::JayWorkspaceWatcher, wl_drm::WlDrmGlobal, wl_output::{OutputGlobalOpt, OutputId, PersistentOutputState}, - wl_seat::{SeatIds, WlSeatGlobal}, + wl_seat::{ + tablet::{TabletIds, TabletInit, TabletPadIds, TabletPadInit, TabletToolIds}, + SeatIds, WlSeatGlobal, + }, wl_surface::{ wl_subsurface::SubsurfaceIds, zwp_idle_inhibitor_v1::{IdleInhibitorId, IdleInhibitorIds, ZwpIdleInhibitorV1}, @@ -188,9 +192,12 @@ pub struct State { pub security_context_acceptors: SecurityContextAcceptors, pub cursor_user_group_ids: CursorUserGroupIds, pub cursor_user_ids: CursorUserIds, - pub cursor_users: CopyHashMap>, pub cursor_user_groups: CopyHashMap>, pub cursor_user_group_hardware_cursor: CloneCell>>, + pub input_device_group_ids: InputDeviceGroupIds, + pub tablet_ids: TabletIds, + pub tablet_tool_ids: TabletToolIds, + pub tablet_pad_ids: TabletPadIds, } // impl Drop for State { @@ -263,6 +270,8 @@ pub struct DeviceHandlerData { pub keymap: CloneCell>>, pub xkb_state: CloneCell>>>, pub output: CloneCell>>, + pub tablet_init: Option>, + pub tablet_pad_init: Option>, } pub struct ConnectorData { diff --git a/src/tasks/input_device.rs b/src/tasks/input_device.rs index ee8d7250..28b53961 100644 --- a/src/tasks/input_device.rs +++ b/src/tasks/input_device.rs @@ -24,6 +24,8 @@ pub fn handle(state: &Rc, dev: Rc) { keymap: Default::default(), xkb_state: Default::default(), output: Default::default(), + tablet_init: dev.tablet_info(), + tablet_pad_init: dev.tablet_pad_info(), }); let ae = Rc::new(AsyncEvent::default()); let oh = DeviceHandler { diff --git a/src/time.rs b/src/time.rs index c4da837d..9537c2f0 100644 --- a/src/time.rs +++ b/src/time.rs @@ -122,3 +122,7 @@ impl Add for Time { pub fn now_usec() -> u64 { Time::now_unchecked().usec() } + +pub fn usec_to_msec(usec: u64) -> u32 { + (usec / 1000) as u32 +} diff --git a/src/tree.rs b/src/tree.rs index 33566120..cae6905d 100644 --- a/src/tree.rs +++ b/src/tree.rs @@ -4,7 +4,15 @@ use { client::{Client, ClientId}, fixed::Fixed, ifs::{ - wl_seat::{wl_pointer::PendingScroll, Dnd, NodeSeatState, WlSeatGlobal}, + wl_seat::{ + tablet::{ + PadButtonState, TabletPad, TabletPadGroup, TabletPadRing, TabletPadStrip, + TabletRingEventSource, TabletStripEventSource, TabletTool, TabletToolChanges, + ToolButtonState, + }, + wl_pointer::PendingScroll, + Dnd, NodeSeatState, WlSeatGlobal, + }, wl_surface::WlSurface, }, rect::Rect, @@ -344,6 +352,116 @@ pub trait Node: 'static { let _ = cancelled; } + fn node_on_tablet_pad_enter(&self, pad: &Rc) { + let _ = pad; + } + + fn node_on_tablet_pad_leave(&self, pad: &Rc) { + let _ = pad; + } + + fn node_on_tablet_pad_button( + &self, + pad: &Rc, + time_usec: u64, + button: u32, + state: PadButtonState, + ) { + let _ = pad; + let _ = time_usec; + let _ = button; + let _ = state; + } + + fn node_on_tablet_pad_mode_switch( + &self, + pad: &Rc, + group: &Rc, + time_usec: u64, + mode: u32, + ) { + let _ = pad; + let _ = group; + let _ = time_usec; + let _ = mode; + } + + fn node_on_tablet_pad_ring( + &self, + pad: &Rc, + ring: &Rc, + source: Option, + angle: Option, + time_usec: u64, + ) { + let _ = pad; + let _ = time_usec; + let _ = ring; + let _ = source; + let _ = angle; + } + + fn node_on_tablet_pad_strip( + &self, + pad: &Rc, + strip: &Rc, + source: Option, + position: Option, + time_usec: u64, + ) { + let _ = pad; + let _ = time_usec; + let _ = strip; + let _ = source; + let _ = position; + } + + fn node_on_tablet_tool_leave(&self, tool: &Rc, time_usec: u64) { + let _ = tool; + let _ = time_usec; + } + + fn node_on_tablet_tool_enter( + self: Rc, + tool: &Rc, + time_usec: u64, + x: Fixed, + y: Fixed, + ) { + let _ = tool; + let _ = time_usec; + let _ = x; + let _ = y; + } + + fn node_on_tablet_tool_button( + &self, + tool: &Rc, + time_usec: u64, + button: u32, + state: ToolButtonState, + ) { + let _ = tool; + let _ = time_usec; + let _ = button; + let _ = state; + } + + fn node_on_tablet_tool_apply_changes( + self: Rc, + tool: &Rc, + time_usec: u64, + changes: Option<&TabletToolChanges>, + x: Fixed, + y: Fixed, + ) { + let _ = tool; + let _ = time_usec; + let _ = changes; + let _ = x; + let _ = y; + } + // TYPE CONVERTERS fn node_into_float(self: Rc) -> Option> { diff --git a/src/tree/container.rs b/src/tree/container.rs index 7294b880..49cbe356 100644 --- a/src/tree/container.rs +++ b/src/tree/container.rs @@ -2,10 +2,13 @@ use { crate::{ backend::KeyState, cursor::KnownCursor, + cursor_user::CursorUser, fixed::Fixed, ifs::wl_seat::{ - collect_kb_foci, collect_kb_foci2, wl_pointer::PendingScroll, NodeSeatState, SeatId, - WlSeatGlobal, BTN_LEFT, + collect_kb_foci, collect_kb_foci2, + tablet::{TabletTool, TabletToolChanges, TabletToolId}, + wl_pointer::PendingScroll, + NodeSeatState, SeatId, WlSeatGlobal, BTN_LEFT, }, rect::Rect, renderer::Renderer, @@ -112,7 +115,7 @@ pub struct ContainerNode { focus_history: LinkedList>, child_nodes: RefCell>>, workspace: CloneCell>, - seats: RefCell>, + cursors: RefCell>, state: Rc, pub render_data: RefCell, scroller: Scroller, @@ -141,7 +144,13 @@ pub struct ContainerChild { factor: Cell, } -struct SeatState { +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +enum CursorType { + Seat(SeatId), + TabletTool(TabletToolId), +} + +struct CursorState { cursor: KnownCursor, target: bool, x: i32, @@ -210,7 +219,7 @@ impl ContainerNode { focus_history: Default::default(), child_nodes: RefCell::new(child_nodes), workspace: CloneCell::new(workspace.clone()), - seats: RefCell::new(Default::default()), + cursors: RefCell::new(Default::default()), state: state.clone(), render_data: Default::default(), scroller: Default::default(), @@ -328,7 +337,7 @@ impl ContainerNode { } fn cancel_seat_ops(&self) { - let mut seats = self.seats.borrow_mut(); + let mut seats = self.cursors.borrow_mut(); for seat in seats.values_mut() { seat.op = None; } @@ -514,12 +523,21 @@ impl ContainerNode { ); } - fn pointer_move(self: &Rc, seat: &Rc, mut x: i32, mut y: i32) { + fn pointer_move( + self: &Rc, + id: CursorType, + cursor: &CursorUser, + x: Fixed, + y: Fixed, + target: bool, + ) { + let mut x = x.round_down(); + let mut y = y.round_down(); let title_height = self.state.theme.sizes.title_height.get(); - let mut seats = self.seats.borrow_mut(); - let seat_state = seats.entry(seat.id()).or_insert_with(|| SeatState { + let mut seats = self.cursors.borrow_mut(); + let seat_state = seats.entry(id).or_insert_with(|| CursorState { cursor: KnownCursor::Default, - target: false, + target, x, y, op: None, @@ -601,7 +619,7 @@ impl ContainerNode { }; if new_cursor != mem::replace(&mut seat_state.cursor, new_cursor) { if seat_state.target { - seat.pointer_cursor().set_known(new_cursor); + cursor.set_known(new_cursor); } } } @@ -1036,6 +1054,80 @@ impl ContainerNode { } } } + + fn button( + self: Rc, + id: CursorType, + seat: &Rc, + time_usec: u64, + pressed: bool, + ) { + let mut seat_datas = self.cursors.borrow_mut(); + let seat_data = match seat_datas.get_mut(&id) { + Some(s) => s, + _ => return, + }; + if seat_data.op.is_none() { + if !pressed { + return; + } + let (kind, child) = 'res: { + let mono = self.mono_child.is_some(); + for child in self.children.iter() { + let rect = child.title_rect.get(); + if rect.contains(seat_data.x, seat_data.y) { + self.activate_child(&child); + child + .node + .clone() + .node_do_focus(seat, Direction::Unspecified); + break 'res (SeatOpKind::Move, child); + } else if !mono { + if self.split.get() == ContainerSplit::Horizontal { + if seat_data.x < rect.x1() { + break 'res ( + SeatOpKind::Resize { + dist_left: seat_data.x + - child.prev().unwrap().body.get().x2(), + dist_right: child.body.get().x1() - seat_data.x, + }, + child, + ); + } + } else { + if seat_data.y < rect.y1() { + break 'res ( + SeatOpKind::Resize { + dist_left: seat_data.y + - child.prev().unwrap().body.get().y2(), + dist_right: child.body.get().y1() - seat_data.y, + }, + child, + ); + } + } + } + } + return; + }; + if seat_data + .double_click_state + .click(&self.state, time_usec, seat_data.x, seat_data.y) + && kind == SeatOpKind::Move + { + drop(seat_datas); + seat.set_tl_floating(child.node.clone(), true); + return; + } + seat_data.op = Some(SeatOp { child, kind }) + } else if !pressed { + let op = seat_data.op.take().unwrap(); + drop(seat_datas); + if op.kind == SeatOpKind::Move { + // todo + } + } + } } struct SeatOp { @@ -1194,76 +1286,14 @@ impl Node for ContainerNode { if button != BTN_LEFT { return; } - let mut seat_datas = self.seats.borrow_mut(); - let seat_data = match seat_datas.get_mut(&seat.id()) { - Some(s) => s, - _ => return, - }; - if seat_data.op.is_none() { - if state != KeyState::Pressed { - return; - } - let (kind, child) = 'res: { - let mono = self.mono_child.is_some(); - for child in self.children.iter() { - let rect = child.title_rect.get(); - if rect.contains(seat_data.x, seat_data.y) { - self.activate_child(&child); - child - .node - .clone() - .node_do_focus(seat, Direction::Unspecified); - break 'res (SeatOpKind::Move, child); - } else if !mono { - if self.split.get() == ContainerSplit::Horizontal { - if seat_data.x < rect.x1() { - break 'res ( - SeatOpKind::Resize { - dist_left: seat_data.x - - child.prev().unwrap().body.get().x2(), - dist_right: child.body.get().x1() - seat_data.x, - }, - child, - ); - } - } else { - if seat_data.y < rect.y1() { - break 'res ( - SeatOpKind::Resize { - dist_left: seat_data.y - - child.prev().unwrap().body.get().y2(), - dist_right: child.body.get().y1() - seat_data.y, - }, - child, - ); - } - } - } - } - return; - }; - if seat_data - .double_click_state - .click(&self.state, time_usec, seat_data.x, seat_data.y) - && kind == SeatOpKind::Move - { - drop(seat_datas); - seat.set_tl_floating(child.node.clone(), true); - return; - } - seat_data.op = Some(SeatOp { child, kind }) - } else if state == KeyState::Released { - let op = seat_data.op.take().unwrap(); - drop(seat_datas); - if op.kind == SeatOpKind::Move { - // todo - } - } + let id = CursorType::Seat(seat.id()); + self.button(id, seat, time_usec, state == KeyState::Pressed); } fn node_on_axis_event(self: Rc, seat: &Rc, event: &PendingScroll) { - let mut seat_datas = self.seats.borrow_mut(); - let seat_data = match seat_datas.get_mut(&seat.id()) { + let mut seat_datas = self.cursors.borrow_mut(); + let id = CursorType::Seat(seat.id()); + let seat_data = match seat_datas.get_mut(&id) { Some(s) => s, _ => return, }; @@ -1299,21 +1329,29 @@ impl Node for ContainerNode { fn node_on_pointer_enter(self: Rc, seat: &Rc, x: Fixed, y: Fixed) { // log::info!("node_on_pointer_enter"); - self.pointer_move(seat, x.round_down(), y.round_down()); + self.pointer_move( + CursorType::Seat(seat.id()), + seat.pointer_cursor(), + x, + y, + false, + ); } fn node_on_pointer_unfocus(&self, seat: &Rc) { // log::info!("unfocus"); - let mut seats = self.seats.borrow_mut(); - if let Some(seat_state) = seats.get_mut(&seat.id()) { + let mut seats = self.cursors.borrow_mut(); + let id = CursorType::Seat(seat.id()); + if let Some(seat_state) = seats.get_mut(&id) { seat_state.target = false; } } fn node_on_pointer_focus(&self, seat: &Rc) { // log::info!("container focus"); - let mut seats = self.seats.borrow_mut(); - if let Some(seat_state) = seats.get_mut(&seat.id()) { + let mut seats = self.cursors.borrow_mut(); + let id = CursorType::Seat(seat.id()); + if let Some(seat_state) = seats.get_mut(&id) { seat_state.target = true; seat.pointer_cursor().set_known(seat_state.cursor); } @@ -1321,7 +1359,46 @@ impl Node for ContainerNode { fn node_on_pointer_motion(self: Rc, seat: &Rc, x: Fixed, y: Fixed) { // log::info!("node_on_pointer_motion"); - self.pointer_move(seat, x.round_down(), y.round_down()); + self.pointer_move( + CursorType::Seat(seat.id()), + seat.pointer_cursor(), + x, + y, + false, + ); + } + + fn node_on_tablet_tool_leave(&self, tool: &Rc, _time_usec: u64) { + let id = CursorType::TabletTool(tool.id); + self.cursors.borrow_mut().remove(&id); + } + + fn node_on_tablet_tool_enter( + self: Rc, + tool: &Rc, + _time_usec: u64, + x: Fixed, + y: Fixed, + ) { + tool.cursor().set_known(KnownCursor::Default); + self.pointer_move(CursorType::TabletTool(tool.id), tool.cursor(), x, y, true); + } + + fn node_on_tablet_tool_apply_changes( + self: Rc, + tool: &Rc, + time_usec: u64, + changes: Option<&TabletToolChanges>, + x: Fixed, + y: Fixed, + ) { + let id = CursorType::TabletTool(tool.id); + self.pointer_move(id, tool.cursor(), x, y, false); + if let Some(changes) = changes { + if let Some(pressed) = changes.down { + self.button(id, tool.seat(), time_usec, pressed); + } + } } fn node_into_container(self: Rc) -> Option> { @@ -1332,13 +1409,13 @@ impl Node for ContainerNode { Some(self) } - fn node_is_container(&self) -> bool { - true - } - fn node_into_toplevel(self: Rc) -> Option> { Some(self) } + + fn node_is_container(&self) -> bool { + true + } } impl ContainingNode for ContainerNode { @@ -1536,7 +1613,7 @@ impl ToplevelNodeBase for ContainerNode { } fn tl_destroy_impl(&self) { - mem::take(self.seats.borrow_mut().deref_mut()); + mem::take(self.cursors.borrow_mut().deref_mut()); let mut cn = self.child_nodes.borrow_mut(); for (_, n) in cn.drain() { n.node.tl_destroy(); diff --git a/src/tree/display.rs b/src/tree/display.rs index 610d6480..4cd2039f 100644 --- a/src/tree/display.rs +++ b/src/tree/display.rs @@ -2,7 +2,8 @@ use { crate::{ backend::ConnectorId, cursor::KnownCursor, - ifs::wl_seat::{NodeSeatState, WlSeatGlobal}, + fixed::Fixed, + ifs::wl_seat::{tablet::TabletTool, NodeSeatState, WlSeatGlobal}, rect::Rect, renderer::Renderer, state::State, @@ -142,4 +143,14 @@ impl Node for DisplayNode { // log::info!("display focus"); seat.pointer_cursor().set_known(KnownCursor::Default); } + + fn node_on_tablet_tool_enter( + self: Rc, + tool: &Rc, + _time_usec: u64, + _x: Fixed, + _y: Fixed, + ) { + tool.cursor().set_known(KnownCursor::Default) + } } diff --git a/src/tree/float.rs b/src/tree/float.rs index 7aa64d1e..014ef05d 100644 --- a/src/tree/float.rs +++ b/src/tree/float.rs @@ -2,8 +2,12 @@ use { crate::{ backend::KeyState, cursor::KnownCursor, + cursor_user::CursorUser, fixed::Fixed, - ifs::wl_seat::{NodeSeatState, SeatId, WlSeatGlobal, BTN_LEFT}, + ifs::wl_seat::{ + tablet::{TabletTool, TabletToolChanges, TabletToolId}, + NodeSeatState, SeatId, WlSeatGlobal, BTN_LEFT, + }, rect::Rect, renderer::Renderer, scale::Scale, @@ -44,11 +48,17 @@ pub struct FloatNode { pub render_titles_scheduled: Cell, pub title: RefCell, pub title_textures: CopyHashMap, - seats: RefCell>, + cursors: RefCell>, pub attention_requested: Cell, } -struct SeatState { +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +enum CursorType { + Seat(SeatId), + TabletTool(TabletToolId), +} + +struct CursorState { cursor: KnownCursor, target: bool, x: i32, @@ -113,7 +123,7 @@ impl FloatNode { render_titles_scheduled: Cell::new(false), title: Default::default(), title_textures: Default::default(), - seats: Default::default(), + cursors: Default::default(), attention_requested: Cell::new(false), }); floater.pull_child_properties(); @@ -214,14 +224,23 @@ impl FloatNode { } } - fn pointer_move(self: &Rc, seat: &Rc, x: i32, y: i32) { + fn pointer_move( + self: &Rc, + id: CursorType, + cursor: &CursorUser, + x: Fixed, + y: Fixed, + target: bool, + ) { + let x = x.round_down(); + let y = y.round_down(); let theme = &self.state.theme; let bw = theme.sizes.border_width.get(); let th = theme.sizes.title_height.get(); - let mut seats = self.seats.borrow_mut(); - let seat_state = seats.entry(seat.id()).or_insert_with(|| SeatState { + let mut seats = self.cursors.borrow_mut(); + let seat_state = seats.entry(id).or_insert_with(|| CursorState { cursor: KnownCursor::Default, - target: false, + target, x, y, op_type: OpType::Move, @@ -289,6 +308,7 @@ impl FloatNode { } } self.position.set(Rect::new(x1, y1, x2, y2).unwrap()); + self.state.damage(); self.schedule_layout(); return; } @@ -334,7 +354,7 @@ impl FloatNode { seat_state.op_type = op_type; if new_cursor != mem::replace(&mut seat_state.cursor, new_cursor) { if seat_state.target { - seat.pointer_cursor().set_known(new_cursor); + cursor.set_known(new_cursor); } } } @@ -398,6 +418,77 @@ impl FloatNode { self.state.tree_changed(); } } + + fn button( + self: Rc, + id: CursorType, + cursor: &CursorUser, + seat: &Rc, + time_usec: u64, + pressed: bool, + ) { + let mut cursors = self.cursors.borrow_mut(); + let cursor_data = match cursors.get_mut(&id) { + Some(s) => s, + _ => return, + }; + if !cursor_data.op_active { + if !pressed { + return; + } + if cursor_data.op_type == OpType::Move { + if let Some(tl) = self.child.get() { + tl.node_do_focus(seat, Direction::Unspecified); + } + } + if cursor_data.double_click_state.click( + &self.state, + time_usec, + cursor_data.x, + cursor_data.y, + ) && cursor_data.op_type == OpType::Move + { + if let Some(tl) = self.child.get() { + drop(cursors); + seat.set_tl_floating(tl, false); + return; + } + } + cursor_data.op_active = true; + let pos = self.position.get(); + match cursor_data.op_type { + OpType::Move => { + self.restack(); + cursor_data.dist_hor = cursor_data.x; + cursor_data.dist_ver = cursor_data.y; + } + OpType::ResizeLeft => cursor_data.dist_hor = cursor_data.x, + OpType::ResizeTop => cursor_data.dist_ver = cursor_data.y, + OpType::ResizeRight => cursor_data.dist_hor = pos.width() - cursor_data.x, + OpType::ResizeBottom => cursor_data.dist_ver = pos.height() - cursor_data.y, + OpType::ResizeTopLeft => { + cursor_data.dist_hor = cursor_data.x; + cursor_data.dist_ver = cursor_data.y; + } + OpType::ResizeTopRight => { + cursor_data.dist_hor = pos.width() - cursor_data.x; + cursor_data.dist_ver = cursor_data.y; + } + OpType::ResizeBottomLeft => { + cursor_data.dist_hor = cursor_data.x; + cursor_data.dist_ver = pos.height() - cursor_data.y; + } + OpType::ResizeBottomRight => { + cursor_data.dist_hor = pos.width() - cursor_data.x; + cursor_data.dist_ver = pos.height() - cursor_data.y; + } + } + } else if !pressed { + cursor_data.op_active = false; + let ws = cursor.output().ensure_workspace(); + self.set_workspace(&ws); + } + } } impl Debug for FloatNode { @@ -487,89 +578,89 @@ impl Node for FloatNode { if button != BTN_LEFT { return; } - let mut seat_datas = self.seats.borrow_mut(); - let seat_data = match seat_datas.get_mut(&seat.id()) { - Some(s) => s, - _ => return, - }; - if !seat_data.op_active { - if state != KeyState::Pressed { - return; - } - if seat_data.op_type == OpType::Move { - if let Some(tl) = self.child.get() { - tl.node_do_focus(seat, Direction::Unspecified); - } - } - if seat_data - .double_click_state - .click(&self.state, time_usec, seat_data.x, seat_data.y) - && seat_data.op_type == OpType::Move - { - if let Some(tl) = self.child.get() { - drop(seat_datas); - seat.set_tl_floating(tl, false); - return; - } - } - seat_data.op_active = true; - let pos = self.position.get(); - match seat_data.op_type { - OpType::Move => { - self.restack(); - seat_data.dist_hor = seat_data.x; - seat_data.dist_ver = seat_data.y; - } - OpType::ResizeLeft => seat_data.dist_hor = seat_data.x, - OpType::ResizeTop => seat_data.dist_ver = seat_data.y, - OpType::ResizeRight => seat_data.dist_hor = pos.width() - seat_data.x, - OpType::ResizeBottom => seat_data.dist_ver = pos.height() - seat_data.y, - OpType::ResizeTopLeft => { - seat_data.dist_hor = seat_data.x; - seat_data.dist_ver = seat_data.y; - } - OpType::ResizeTopRight => { - seat_data.dist_hor = pos.width() - seat_data.x; - seat_data.dist_ver = seat_data.y; - } - OpType::ResizeBottomLeft => { - seat_data.dist_hor = seat_data.x; - seat_data.dist_ver = pos.height() - seat_data.y; - } - OpType::ResizeBottomRight => { - seat_data.dist_hor = pos.width() - seat_data.x; - seat_data.dist_ver = pos.height() - seat_data.y; - } - } - } else if state == KeyState::Released { - seat_data.op_active = false; - let ws = seat.get_output().ensure_workspace(); - self.set_workspace(&ws); - } + self.button( + CursorType::Seat(seat.id()), + seat.pointer_cursor(), + seat, + time_usec, + state == KeyState::Pressed, + ); } fn node_on_pointer_enter(self: Rc, seat: &Rc, x: Fixed, y: Fixed) { - self.pointer_move(seat, x.round_down(), y.round_down()); + self.pointer_move( + CursorType::Seat(seat.id()), + seat.pointer_cursor(), + x, + y, + false, + ); } fn node_on_pointer_unfocus(&self, seat: &Rc) { - let mut seats = self.seats.borrow_mut(); - if let Some(seat_state) = seats.get_mut(&seat.id()) { + let mut cursors = self.cursors.borrow_mut(); + let id = CursorType::Seat(seat.id()); + if let Some(seat_state) = cursors.get_mut(&id) { seat_state.target = false; } } fn node_on_pointer_focus(&self, seat: &Rc) { // log::info!("float focus"); - let mut seats = self.seats.borrow_mut(); - if let Some(seat_state) = seats.get_mut(&seat.id()) { + let mut cursors = self.cursors.borrow_mut(); + let id = CursorType::Seat(seat.id()); + if let Some(seat_state) = cursors.get_mut(&id) { seat_state.target = true; seat.pointer_cursor().set_known(seat_state.cursor); } } fn node_on_pointer_motion(self: Rc, seat: &Rc, x: Fixed, y: Fixed) { - self.pointer_move(seat, x.round_down(), y.round_down()); + self.pointer_move( + CursorType::Seat(seat.id()), + seat.pointer_cursor(), + x, + y, + false, + ); + } + + fn node_on_tablet_tool_leave(&self, tool: &Rc, _time_usec: u64) { + let id = CursorType::TabletTool(tool.id); + self.cursors.borrow_mut().remove(&id); + } + + fn node_on_tablet_tool_enter( + self: Rc, + tool: &Rc, + _time_usec: u64, + x: Fixed, + y: Fixed, + ) { + tool.cursor().set_known(KnownCursor::Default); + self.pointer_move(CursorType::TabletTool(tool.id), tool.cursor(), x, y, true); + } + + fn node_on_tablet_tool_apply_changes( + self: Rc, + tool: &Rc, + time_usec: u64, + changes: Option<&TabletToolChanges>, + x: Fixed, + y: Fixed, + ) { + self.pointer_move(CursorType::TabletTool(tool.id), tool.cursor(), x, y, false); + if let Some(changes) = changes { + if let Some(pressed) = changes.down { + self.button( + CursorType::TabletTool(tool.id), + tool.cursor(), + tool.seat(), + time_usec, + pressed, + ); + } + } } fn node_into_float(self: Rc) -> Option> { diff --git a/src/tree/output.rs b/src/tree/output.rs index 4d9c3dad..2e8672b7 100644 --- a/src/tree/output.rs +++ b/src/tree/output.rs @@ -11,8 +11,10 @@ use { wl_buffer::WlBufferStorage, wl_output::WlOutputGlobal, wl_seat::{ - collect_kb_foci2, wl_pointer::PendingScroll, NodeSeatState, SeatId, WlSeatGlobal, - BTN_LEFT, + collect_kb_foci2, + tablet::{TabletTool, TabletToolChanges, TabletToolId}, + wl_pointer::PendingScroll, + NodeSeatState, SeatId, WlSeatGlobal, BTN_LEFT, }, wl_surface::{ ext_session_lock_surface_v1::ExtSessionLockSurfaceV1, @@ -63,7 +65,7 @@ pub struct OutputNode { pub is_dummy: bool, pub status: CloneCell>, pub scroll: Scroller, - pub pointer_positions: CopyHashMap, + pub pointer_positions: CopyHashMap, pub lock_surface: CloneCell>>, pub hardware_cursor: CloneCell>>, pub hardware_cursor_needs_render: Cell, @@ -72,6 +74,12 @@ pub struct OutputNode { pub screencopies: CopyHashMap<(ClientId, ZwlrScreencopyFrameV1Id), Rc>, } +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub enum PointerType { + Seat(SeatId), + TabletTool(TabletToolId), +} + pub async fn output_render_data(state: Rc) { loop { let container = state.pending_output_render_data.pop().await; @@ -593,8 +601,9 @@ impl OutputNode { self.schedule_update_render_data(); } - fn pointer_move(self: &Rc, seat: &Rc, x: i32, y: i32) { - self.pointer_positions.set(seat.id(), (x, y)); + fn pointer_move(self: &Rc, id: PointerType, x: Fixed, y: Fixed) { + self.pointer_positions + .set(id, (x.round_down(), y.round_down())); } pub fn has_fullscreen(&self) -> bool { @@ -641,6 +650,29 @@ impl OutputNode { set_layer_visible!(self.layers[2], visible); set_layer_visible!(self.layers[3], visible); } + + fn button(self: Rc, id: PointerType) { + let (x, y) = match self.pointer_positions.get(&id) { + Some(p) => p, + _ => return, + }; + if y >= self.state.theme.sizes.title_height.get() { + return; + } + let ws = 'ws: { + let rd = self.render_data.borrow_mut(); + for title in &rd.titles { + if x >= title.x1 && x < title.x2 { + break 'ws title.ws.clone(); + } + } + return; + }; + self.show_workspace(&ws); + ws.flush_jay_workspaces(); + self.schedule_update_render_data(); + self.state.tree_changed(); + } } pub struct OutputTitle { @@ -844,26 +876,7 @@ impl Node for OutputNode { if state != KeyState::Pressed || button != BTN_LEFT { return; } - let (x, y) = match self.pointer_positions.get(&seat.id()) { - Some(p) => p, - _ => return, - }; - if y >= self.state.theme.sizes.title_height.get() { - return; - } - let ws = 'ws: { - let rd = self.render_data.borrow_mut(); - for title in &rd.titles { - if x >= title.x1 && x < title.x2 { - break 'ws title.ws.clone(); - } - } - return; - }; - self.show_workspace(&ws); - ws.flush_jay_workspaces(); - self.schedule_update_render_data(); - self.state.tree_changed(); + self.button(PointerType::Seat(seat.id())); } fn node_on_axis_event(self: Rc, seat: &Rc, event: &PendingScroll) { @@ -905,7 +918,7 @@ impl Node for OutputNode { } fn node_on_pointer_enter(self: Rc, seat: &Rc, x: Fixed, y: Fixed) { - self.pointer_move(seat, x.round_down(), y.round_down()); + self.pointer_move(PointerType::Seat(seat.id()), x, y); } fn node_on_pointer_focus(&self, seat: &Rc) { @@ -914,7 +927,40 @@ impl Node for OutputNode { } fn node_on_pointer_motion(self: Rc, seat: &Rc, x: Fixed, y: Fixed) { - self.pointer_move(seat, x.round_down(), y.round_down()); + self.pointer_move(PointerType::Seat(seat.id()), x, y); + } + + fn node_on_tablet_tool_leave(&self, tool: &Rc, _time_usec: u64) { + self.pointer_positions + .remove(&PointerType::TabletTool(tool.id)); + } + + fn node_on_tablet_tool_enter( + self: Rc, + tool: &Rc, + _time_usec: u64, + x: Fixed, + y: Fixed, + ) { + tool.cursor().set_known(KnownCursor::Default); + self.pointer_move(PointerType::TabletTool(tool.id), x, y); + } + + fn node_on_tablet_tool_apply_changes( + self: Rc, + tool: &Rc, + _time_usec: u64, + changes: Option<&TabletToolChanges>, + x: Fixed, + y: Fixed, + ) { + let id = PointerType::TabletTool(tool.id); + self.pointer_move(id, x, y); + if let Some(changes) = changes { + if changes.down == Some(true) { + self.button(id); + } + } } fn node_into_output(self: Rc) -> Option> { diff --git a/src/tree/workspace.rs b/src/tree/workspace.rs index c0d4f553..460f0a75 100644 --- a/src/tree/workspace.rs +++ b/src/tree/workspace.rs @@ -2,10 +2,11 @@ use { crate::{ client::ClientId, cursor::KnownCursor, + fixed::Fixed, ifs::{ jay_workspace::JayWorkspace, wl_output::OutputId, - wl_seat::{NodeSeatState, WlSeatGlobal}, + wl_seat::{tablet::TabletTool, NodeSeatState, WlSeatGlobal}, wl_surface::WlSurface, }, rect::Rect, @@ -277,6 +278,16 @@ impl Node for WorkspaceNode { seat.pointer_cursor().set_known(KnownCursor::Default); } + fn node_on_tablet_tool_enter( + self: Rc, + tool: &Rc, + _time_usec: u64, + _x: Fixed, + _y: Fixed, + ) { + tool.cursor().set_known(KnownCursor::Default) + } + fn node_into_workspace(self: Rc) -> Option> { Some(self.clone()) } diff --git a/src/utils.rs b/src/utils.rs index 2d198bb7..8531378c 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -31,6 +31,7 @@ pub mod opaque_cell; pub mod option_ext; pub mod oserror; pub mod page_size; +pub mod pending_serial; pub mod process_name; pub mod ptr_ext; pub mod queue; diff --git a/src/utils/bindings.rs b/src/utils/bindings.rs index c939f75b..899246ae 100644 --- a/src/utils/bindings.rs +++ b/src/utils/bindings.rs @@ -5,7 +5,11 @@ use { utils::copyhashmap::{CopyHashMap, Locked}, }, ahash::AHashMap, - std::{cell::RefCell, collections::hash_map::Entry, rc::Rc}, + std::{ + cell::{Ref, RefCell}, + collections::hash_map::Entry, + rc::Rc, + }, }; pub struct Bindings

{ @@ -84,4 +88,8 @@ impl PerClientBindings

{ } } } + + pub fn borrow(&self) -> Ref>>> { + self.bindings.borrow() + } } diff --git a/src/utils/copyhashmap.rs b/src/utils/copyhashmap.rs index b56edbc2..92d3b298 100644 --- a/src/utils/copyhashmap.rs +++ b/src/utils/copyhashmap.rs @@ -66,7 +66,7 @@ impl CopyHashMap { unsafe { self.map.get().deref().contains_key(k) } } - pub fn lock(&self) -> Locked { + pub fn lock(&self) -> Locked<'_, K, V> { Locked { source: self, map: self.clear(), diff --git a/src/utils/pending_serial.rs b/src/utils/pending_serial.rs new file mode 100644 index 00000000..d7890014 --- /dev/null +++ b/src/utils/pending_serial.rs @@ -0,0 +1,19 @@ +use crate::client::Client; + +pub struct PendingSerial<'a> { + serial: Option, + client: &'a Client, +} + +impl<'a> PendingSerial<'a> { + pub fn new(client: &'a Client) -> Self { + Self { + serial: None, + client, + } + } + + pub fn get(&mut self) -> u32 { + *self.serial.get_or_insert_with(|| self.client.next_serial()) + } +} diff --git a/wire/jay_seat_events.txt b/wire/jay_seat_events.txt index 06bbdbb3..f7cb0163 100644 --- a/wire/jay_seat_events.txt +++ b/wire/jay_seat_events.txt @@ -132,3 +132,110 @@ event switch_event { input_device: u32, event: u32, } + +event tablet_tool_proximity_in { +} + +event tablet_tool_proximity_out { +} + +event tablet_tool_down { +} + +event tablet_tool_up { +} + +event tablet_tool_motion { + x: fixed, + y: fixed, +} + +event tablet_tool_pressure { + pressure: pod(f64), +} + +event tablet_tool_distance { + distance: pod(f64), +} + +event tablet_tool_tilt { + tilt_x: pod(f64), + tilt_y: pod(f64), +} + +event tablet_tool_rotation { + degrees: pod(f64), +} + +event tablet_tool_slider { + position: pod(f64), +} + +event tablet_tool_wheel { + degrees: pod(f64), + clicks: i32, +} + +event tablet_tool_button { + button: u32, + state: u32, +} + +event tablet_tool_frame { + seat: u32, + time_usec: pod(u64), + input_device: u32, + tool: u32, +} + +event tablet_pad_mode_switch { + seat: u32, + time_usec: pod(u64), + input_device: u32, + group: u32, + mode: u32, +} + +event tablet_pad_button { + seat: u32, + time_usec: pod(u64), + input_device: u32, + button: u32, + state: u32, +} + +event tablet_pad_strip_source { + source: u32, +} + +event tablet_pad_strip_position { + position: pod(f64), +} + +event tablet_pad_strip_stop { +} + +event tablet_pad_strip_frame { + seat: u32, + time_usec: pod(u64), + input_device: u32, + strip: u32, +} + +event tablet_pad_ring_source { + source: u32, +} + +event tablet_pad_ring_angle { + degrees: pod(f64), +} + +event tablet_pad_ring_stop { +} + +event tablet_pad_ring_frame { + seat: u32, + time_usec: pod(u64), + input_device: u32, + ring: u32, +} diff --git a/wire/zwp_tablet_manager_v2.txt b/wire/zwp_tablet_manager_v2.txt new file mode 100644 index 00000000..015a3da0 --- /dev/null +++ b/wire/zwp_tablet_manager_v2.txt @@ -0,0 +1,7 @@ +request get_tablet_seat { + tablet_seat: id(zwp_tablet_seat_v2), + seat: id(wl_seat), +} + +request destroy { +} diff --git a/wire/zwp_tablet_pad_group_v2.txt b/wire/zwp_tablet_pad_group_v2.txt new file mode 100644 index 00000000..320e165a --- /dev/null +++ b/wire/zwp_tablet_pad_group_v2.txt @@ -0,0 +1,27 @@ +request destroy { +} + +event buttons { + buttons: array(u32), +} + +event ring { + ring: id(zwp_tablet_pad_ring_v2), +} + +event strip { + strip: id(zwp_tablet_pad_strip_v2), +} + +event modes { + modes: u32, +} + +event done { +} + +event mode_switch { + time: u32, + serial: u32, + mode: u32, +} diff --git a/wire/zwp_tablet_pad_ring_v2.txt b/wire/zwp_tablet_pad_ring_v2.txt new file mode 100644 index 00000000..9013350e --- /dev/null +++ b/wire/zwp_tablet_pad_ring_v2.txt @@ -0,0 +1,22 @@ +request set_feedback { + description: str, + serial: u32, +} + +request destroy { +} + +event source { + source: u32, +} + +event angle { + degrees: fixed, +} + +event stop { +} + +event frame { + time: u32, +} diff --git a/wire/zwp_tablet_pad_strip_v2.txt b/wire/zwp_tablet_pad_strip_v2.txt new file mode 100644 index 00000000..222efda0 --- /dev/null +++ b/wire/zwp_tablet_pad_strip_v2.txt @@ -0,0 +1,22 @@ +request set_feedback { + description: str, + serial: u32, +} + +request destroy { +} + +event source { + source: u32, +} + +event position { + position: u32, +} + +event stop { +} + +event frame { + time: u32, +} diff --git a/wire/zwp_tablet_pad_v2.txt b/wire/zwp_tablet_pad_v2.txt new file mode 100644 index 00000000..08770652 --- /dev/null +++ b/wire/zwp_tablet_pad_v2.txt @@ -0,0 +1,43 @@ +request set_feedback { + button: u32, + description: str, + serial: u32, +} + +request destroy { +} + +event group { + pad_group: id(zwp_tablet_pad_group_v2), +} + +event path { + path: str, +} + +event buttons { + buttons: u32, +} + +event done { +} + +event button { + time: u32, + button: u32, + state: u32, +} + +event enter { + serial: u32, + tablet: id(zwp_tablet_v2), + surface: id(wl_surface), +} + +event leave { + serial: u32, + surface: id(wl_surface), +} + +event removed { +} diff --git a/wire/zwp_tablet_seat_v2.txt b/wire/zwp_tablet_seat_v2.txt new file mode 100644 index 00000000..bbc4e331 --- /dev/null +++ b/wire/zwp_tablet_seat_v2.txt @@ -0,0 +1,14 @@ +request destroy { +} + +event tablet_added { + id: id(zwp_tablet_v2), +} + +event tool_added { + id: id(zwp_tablet_tool_v2), +} + +event pad_added { + id: id(zwp_tablet_pad_v2), +} diff --git a/wire/zwp_tablet_tool_v2.txt b/wire/zwp_tablet_tool_v2.txt index 67f0da77..9351dc80 100644 --- a/wire/zwp_tablet_tool_v2.txt +++ b/wire/zwp_tablet_tool_v2.txt @@ -1,5 +1,3 @@ -# requests - request set_cursor { serial: u32, surface: id(wl_surface), @@ -9,3 +7,84 @@ request set_cursor { request destroy { } + +event type { + tool_type: u32, +} + +event hardware_serial { + hardware_serial_hi: u32, + hardware_serial_lo: u32, +} + +event hardware_id_wacom { + hardware_id_hi: u32, + hardware_id_lo: u32, +} + +event capability { + capability: u32, +} + +event done { +} + +event removed { +} + +event proximity_in { + serial: u32, + tablet: id(zwp_tablet_v2), + surface: id(wl_surface), +} + +event proximity_out { +} + +event down { + serial: u32, +} + +event up { +} + +event motion { + x: fixed, + y: fixed, +} + +event pressure { + pressure: u32, +} + +event distance { + distance: u32, +} + +event tilt { + tilt_x: fixed, + tilt_y: fixed, +} + +event rotation { + degrees: fixed, +} + +event slider { + position: i32, +} + +event wheel { + degrees: fixed, + clicks: i32, +} + +event button { + serial: u32, + button: u32, + state: u32, +} + +event frame { + time: u32, +} diff --git a/wire/zwp_tablet_v2.txt b/wire/zwp_tablet_v2.txt new file mode 100644 index 00000000..5c758d0d --- /dev/null +++ b/wire/zwp_tablet_v2.txt @@ -0,0 +1,21 @@ +request destroy { +} + +event name { + name: str, +} + +event id { + vid: u32, + pid: u32, +} + +event path { + path: str, +} + +event done { +} + +event removed { +}