1
0
Fork 0
forked from wry/wry

wayland: implement tablet-v2

This commit is contained in:
Julian Orth 2024-05-01 00:09:16 +02:00
parent 86e283d255
commit 7ed499eabd
62 changed files with 5174 additions and 318 deletions

View file

@ -723,10 +723,6 @@ fn write_request_handler<W: Write>(
messages: &ParseResult, messages: &ParseResult,
) -> Result<()> { ) -> Result<()> {
writeln!(f)?; writeln!(f)?;
// TODO: remove this after https://github.com/mahkoh/jay/pull/190
if camel_obj_name == "ZwpTabletToolV2" {
writeln!(f, " #[allow(dead_code)]")?;
}
writeln!( writeln!(
f, f,
" pub trait {camel_obj_name}RequestHandler: crate::object::Object + Sized {{" " pub trait {camel_obj_name}RequestHandler: crate::object::Object + Sized {{"

View file

@ -474,6 +474,22 @@ You can use the `configure-input` action to change these settings at runtime.
See the specification for more details. 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 # Theming
You can configure the colors, sizes, and fonts used by the compositor with the top-level `theme` table. You can configure the colors, sizes, and fonts used by the compositor with the top-level `theme` table.

View file

@ -165,6 +165,7 @@ Jay supports the following wayland protocols:
| zwp_pointer_gestures_v1 | 3 | | | zwp_pointer_gestures_v1 | 3 | |
| zwp_primary_selection_device_manager_v1 | 1 | | | zwp_primary_selection_device_manager_v1 | 1 | |
| zwp_relative_pointer_manager_v1 | 1 | | | zwp_relative_pointer_manager_v1 | 1 | |
| zwp_tablet_manager_v2 | 1 | |
| zwp_text_input_manager_v3 | 1 | | | zwp_text_input_manager_v3 | 1 | |
| zwp_virtual_keyboard_manager_v1 | 1 | Yes | | zwp_virtual_keyboard_manager_v1 | 1 | Yes |
| zxdg_decoration_manager_v1 | 1 | | | 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. - Fine-grained damage tracking.
- Touch support. - Touch support.
- Tablet support.
- Tearing updates of fullscreen games. - Tearing updates of fullscreen games.

View file

@ -7,6 +7,7 @@
- Focus-follows-mouse can now be disabled. - Focus-follows-mouse can now be disabled.
- Add support for pointer-gestures-unstable-v1. - Add support for pointer-gestures-unstable-v1.
- Configs can now handle switch events (laptop lid closed/opened). - Configs can now handle switch events (laptop lid closed/opened).
- Add support for tablet-v2.
# 1.1.0 (2024-04-22) # 1.1.0 (2024-04-22)

View file

@ -4,7 +4,14 @@ use {
drm_feedback::DrmFeedback, drm_feedback::DrmFeedback,
fixed::Fixed, fixed::Fixed,
gfx_api::{GfxFramebuffer, SyncFile}, 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, libinput::consts::DeviceCapability,
video::drm::{ConnectorType, DrmConnector, DrmError, DrmVersion}, video::drm::{ConnectorType, DrmConnector, DrmError, DrmVersion},
}, },
@ -126,6 +133,8 @@ pub trait HardwareCursor: Debug {
pub type TransformMatrix = [[f64; 2]; 2]; pub type TransformMatrix = [[f64; 2]; 2];
linear_ids!(InputDeviceGroupIds, InputDeviceGroupId, usize);
pub trait InputDevice { pub trait InputDevice {
fn id(&self) -> InputDeviceId; fn id(&self) -> InputDeviceId;
fn removed(&self) -> bool; fn removed(&self) -> bool;
@ -169,6 +178,12 @@ pub trait InputDevice {
None None
} }
fn set_natural_scrolling_enabled(&self, enabled: bool); fn set_natural_scrolling_enabled(&self, enabled: bool);
fn tablet_info(&self) -> Option<Box<TabletInit>> {
None
}
fn tablet_pad_info(&self) -> Option<Box<TabletPadInit>> {
None
}
} }
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)] #[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)]
@ -321,6 +336,53 @@ pub enum InputEvent {
time_usec: u64, time_usec: u64,
event: SwitchEvent, event: SwitchEvent,
}, },
TabletToolAdded {
time_usec: u64,
init: Box<TabletToolInit>,
},
TabletToolChanged {
time_usec: u64,
id: TabletToolId,
changes: Box<TabletToolChanges>,
},
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<TabletRingEventSource>,
angle: Option<f64>,
},
TabletPadStrip {
time_usec: u64,
pad: TabletPadId,
strip: u32,
source: Option<TabletStripEventSource>,
position: Option<f64>,
},
} }
pub enum DrmEvent { pub enum DrmEvent {

View file

@ -6,8 +6,8 @@ use {
crate::{ crate::{
async_engine::SpawnedFuture, async_engine::SpawnedFuture,
backend::{ backend::{
Backend, InputDevice, InputDeviceAccelProfile, InputDeviceCapability, InputDeviceId, Backend, InputDevice, InputDeviceAccelProfile, InputDeviceCapability,
InputEvent, KeyState, TransformMatrix, InputDeviceGroupId, InputDeviceId, InputEvent, KeyState, TransformMatrix,
}, },
backends::metal::video::{ backends::metal::video::{
MetalDrmDeviceData, MetalLeaseData, MetalRenderContext, PendingDrmDevice, MetalDrmDeviceData, MetalLeaseData, MetalRenderContext, PendingDrmDevice,
@ -15,12 +15,16 @@ use {
dbus::{DbusError, SignalHandler}, dbus::{DbusError, SignalHandler},
drm_feedback::DrmFeedback, drm_feedback::DrmFeedback,
gfx_api::GfxError, gfx_api::GfxError,
ifs::wl_seat::tablet::{
TabletId, TabletInit, TabletPadGroupInit, TabletPadId, TabletPadInit,
},
libinput::{ libinput::{
consts::{ consts::{
AccelProfile, LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE, 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, LibInput, LibInputAdapter, LibInputError,
}, },
logind::{LogindError, Session}, logind::{LogindError, Session},
@ -41,6 +45,7 @@ use {
gbm::GbmError, gbm::GbmError,
}, },
}, },
bstr::ByteSlice,
std::{ std::{
any::Any, any::Any,
cell::{Cell, RefCell}, cell::{Cell, RefCell},
@ -335,6 +340,7 @@ pub async fn create(state: &Rc<State>) -> Result<Rc<MetalBackend>, MetalError> {
} }
struct MetalInputDevice { struct MetalInputDevice {
state: Rc<State>,
slot: usize, slot: usize,
id: InputDeviceId, id: InputDeviceId,
devnum: c::dev_t, devnum: c::dev_t,
@ -342,11 +348,14 @@ struct MetalInputDevice {
inputdev: CloneCell<Option<Rc<RegisteredDevice>>>, inputdev: CloneCell<Option<Rc<RegisteredDevice>>>,
devnode: CString, devnode: CString,
_sysname: CString, _sysname: CString,
syspath: CString,
removed: Cell<bool>, removed: Cell<bool>,
events: SyncQueue<InputEvent>, events: SyncQueue<InputEvent>,
cb: CloneCell<Option<Rc<dyn Fn()>>>, cb: CloneCell<Option<Rc<dyn Fn()>>>,
name: CloneCell<Rc<String>>, name: CloneCell<Rc<String>>,
transform_matrix: Cell<Option<TransformMatrix>>, transform_matrix: Cell<Option<TransformMatrix>>,
tablet_id: Cell<Option<TabletId>>,
tablet_pad_id: Cell<Option<TabletPadId>>,
// state // state
pressed_keys: SmallMap<u32, (), 5>, pressed_keys: SmallMap<u32, (), 5>,
@ -646,6 +655,71 @@ impl InputDevice for MetalInputDevice {
fn natural_scrolling_enabled(&self) -> Option<bool> { fn natural_scrolling_enabled(&self) -> Option<bool> {
self.effective.natural_scrolling_enabled.get() self.effective.natural_scrolling_enabled.get()
} }
fn tablet_info(&self) -> Option<Box<TabletInit>> {
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<Box<TabletPadInit>> {
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 { impl MetalInputDevice {
@ -655,4 +729,14 @@ impl MetalInputDevice {
cb(); 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)
}
} }

View file

@ -3,14 +3,25 @@ use {
backend::{AxisSource, InputEvent, KeyState, ScrollAxis}, backend::{AxisSource, InputEvent, KeyState, ScrollAxis},
backends::metal::MetalBackend, backends::metal::MetalBackend,
fixed::Fixed, fixed::Fixed,
ifs::wl_seat::tablet::{
PadButtonState, TabletRingEventSource, TabletStripEventSource, TabletTool2dChange,
TabletToolCapability, TabletToolChanges, TabletToolId, TabletToolInit,
TabletToolPositionChange, TabletToolType, TabletToolWheelChange, ToolButtonState,
},
libinput::{ libinput::{
consts::{ consts::{
LIBINPUT_BUTTON_STATE_PRESSED, LIBINPUT_KEY_STATE_PRESSED, LIBINPUT_BUTTON_STATE_PRESSED, LIBINPUT_BUTTON_STATE_RELEASED,
LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL, LIBINPUT_KEY_STATE_PRESSED, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL,
LIBINPUT_SWITCH_LID, LIBINPUT_SWITCH_STATE_OFF, LIBINPUT_SWITCH_STATE_ON, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL, LIBINPUT_SWITCH_LID,
LIBINPUT_SWITCH_TABLET_MODE, 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}, 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_BEGIN => self.handle_gesture_hold_begin(event),
c::LIBINPUT_EVENT_GESTURE_HOLD_END => self.handle_gesture_hold_end(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_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, 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<bool>,
) -> InputEvent {
let mut changes = Box::<TabletToolChanges>::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<Self>, 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<Self>, 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<Self>, 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<Self>, 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<Self>, 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<Self>, 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<Self>, 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),
},
});
}
} }

View file

@ -293,6 +293,7 @@ impl MetalBackend {
let devnum = dev.devnum(); let devnum = dev.devnum();
let devnode = dev.devnode()?; let devnode = dev.devnode()?;
let sysname = dev.sysname()?; let sysname = dev.sysname()?;
let syspath = dev.syspath()?;
log::info!("Device added: {}", devnode.to_bytes().as_bstr()); log::info!("Device added: {}", devnode.to_bytes().as_bstr());
let mut slots = self.device_holder.input_devices.borrow_mut(); let mut slots = self.device_holder.input_devices.borrow_mut();
let slot = 'slot: { let slot = 'slot: {
@ -305,6 +306,7 @@ impl MetalBackend {
slots.len() - 1 slots.len() - 1
}; };
let dev = Rc::new(MetalInputDevice { let dev = Rc::new(MetalInputDevice {
state: self.state.clone(),
slot, slot,
id: device_id, id: device_id,
devnum, devnum,
@ -312,6 +314,7 @@ impl MetalBackend {
inputdev: Default::default(), inputdev: Default::default(),
devnode: devnode.to_owned(), devnode: devnode.to_owned(),
_sysname: sysname.to_owned(), _sysname: sysname.to_owned(),
syspath: syspath.to_owned(),
removed: Cell::new(false), removed: Cell::new(false),
events: Default::default(), events: Default::default(),
cb: Default::default(), cb: Default::default(),
@ -321,6 +324,8 @@ impl MetalBackend {
desired: Default::default(), desired: Default::default(),
transform_matrix: Default::default(), transform_matrix: Default::default(),
effective: Default::default(), effective: Default::default(),
tablet_id: Default::default(),
tablet_pad_id: Default::default(),
}); });
slots[slot] = Some(dev.clone()); slots[slot] = Some(dev.clone());
self.device_holder self.device_holder

View file

@ -1,6 +1,7 @@
use { use {
crate::{ crate::{
cli::{GlobalArgs, SeatTestArgs}, cli::{GlobalArgs, SeatTestArgs},
fixed::Fixed,
ifs::wl_seat::wl_pointer::{PendingScroll, CONTINUOUS, FINGER, WHEEL}, ifs::wl_seat::wl_pointer::{PendingScroll, CONTINUOUS, FINGER, WHEEL},
tools::tool_client::{with_tool_client, Handle, ToolClient}, tools::tool_client::{with_tool_client, Handle, ToolClient},
wire::{ wire::{
@ -8,7 +9,13 @@ use {
jay_seat_events::{ jay_seat_events::{
Axis120, AxisFrame, AxisInverted, AxisPx, AxisSource, AxisStop, Button, HoldBegin, Axis120, AxisFrame, AxisInverted, AxisPx, AxisSource, AxisStop, Button, HoldBegin,
HoldEnd, Key, Modifiers, PinchBegin, PinchEnd, PinchUpdate, PointerAbs, PointerRel, 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<f64>,
distance: Option<f64>,
tilt: Option<(f64, f64)>,
rotation: Option<f64>,
slider: Option<f64>,
wheel: Option<(f64, i32)>,
button: Option<(u32, u32)>,
}
#[derive(Default, Debug, Copy, Clone)]
pub struct PendingTabletPadStrip {
source: u32,
pos: Option<f64>,
stop: bool,
}
#[derive(Default, Debug, Copy, Clone)]
pub struct PendingTabletPadRing {
source: u32,
degrees: Option<f64>,
stop: bool,
}
async fn run(seat_test: Rc<SeatTest>) { async fn run(seat_test: Rc<SeatTest>) {
let tc = &seat_test.tc; let tc = &seat_test.tc;
let comp = tc.jay_compositor().await; let comp = tc.jay_compositor().await;
@ -344,6 +381,208 @@ async fn run(seat_test: Rc<SeatTest>) {
); );
} }
}); });
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; pending::<()>().await;
} }

View file

@ -17,6 +17,7 @@ use {
copyhashmap::{CopyHashMap, Locked}, copyhashmap::{CopyHashMap, Locked},
errorfmt::ErrorFmt, errorfmt::ErrorFmt,
numcell::NumCell, numcell::NumCell,
pending_serial::PendingSerial,
trim::AsciiTrim, trim::AsciiTrim,
}, },
wire::WlRegistryId, wire::WlRegistryId,
@ -350,6 +351,10 @@ impl Client {
self.state.next_serial(Some(self)) self.state.next_serial(Some(self))
} }
pub fn pending_serial(&self) -> PendingSerial<'_> {
PendingSerial::new(self)
}
pub fn new_id<T: From<ObjectId>>(&self) -> Result<T, ClientError> { pub fn new_id<T: From<ObjectId>>(&self) -> Result<T, ClientError> {
self.objects.id(self) self.objects.id(self)
} }

View file

@ -15,7 +15,7 @@ use {
wl_output::WlOutput, wl_output::WlOutput,
wl_region::WlRegion, wl_region::WlRegion,
wl_registry::WlRegistry, wl_registry::WlRegistry,
wl_seat::{wl_pointer::WlPointer, WlSeat}, wl_seat::{tablet::zwp_tablet_tool_v2::ZwpTabletToolV2, wl_pointer::WlPointer, WlSeat},
wl_surface::{ wl_surface::{
xdg_surface::{xdg_toplevel::XdgToplevel, XdgSurface}, xdg_surface::{xdg_toplevel::XdgToplevel, XdgSurface},
WlSurface, WlSurface,
@ -35,7 +35,7 @@ use {
WlDataSourceId, WlOutputId, WlPointerId, WlRegionId, WlRegistryId, WlSeatId, WlDataSourceId, WlOutputId, WlPointerId, WlRegionId, WlRegistryId, WlSeatId,
WlSurfaceId, WpDrmLeaseConnectorV1Id, WpLinuxDrmSyncobjTimelineV1Id, XdgPositionerId, WlSurfaceId, WpDrmLeaseConnectorV1Id, WpLinuxDrmSyncobjTimelineV1Id, XdgPositionerId,
XdgSurfaceId, XdgToplevelId, XdgWmBaseId, ZwlrDataControlSourceV1Id, XdgSurfaceId, XdgToplevelId, XdgWmBaseId, ZwlrDataControlSourceV1Id,
ZwpPrimarySelectionSourceV1Id, ZwpPrimarySelectionSourceV1Id, ZwpTabletToolV2Id,
}, },
}, },
std::{cell::RefCell, mem, rc::Rc}, std::{cell::RefCell, mem, rc::Rc},
@ -65,6 +65,7 @@ pub struct Objects {
pub zwlr_data_sources: CopyHashMap<ZwlrDataControlSourceV1Id, Rc<ZwlrDataControlSourceV1>>, pub zwlr_data_sources: CopyHashMap<ZwlrDataControlSourceV1Id, Rc<ZwlrDataControlSourceV1>>,
pub jay_toplevels: CopyHashMap<JayToplevelId, Rc<JayToplevel>>, pub jay_toplevels: CopyHashMap<JayToplevelId, Rc<JayToplevel>>,
pub drm_lease_outputs: CopyHashMap<WpDrmLeaseConnectorV1Id, Rc<WpDrmLeaseConnectorV1>>, pub drm_lease_outputs: CopyHashMap<WpDrmLeaseConnectorV1Id, Rc<WpDrmLeaseConnectorV1>>,
pub tablet_tools: CopyHashMap<ZwpTabletToolV2Id, Rc<ZwpTabletToolV2>>,
ids: RefCell<Vec<usize>>, ids: RefCell<Vec<usize>>,
} }
@ -96,6 +97,7 @@ impl Objects {
zwlr_data_sources: Default::default(), zwlr_data_sources: Default::default(),
jay_toplevels: Default::default(), jay_toplevels: Default::default(),
drm_lease_outputs: Default::default(), drm_lease_outputs: Default::default(),
tablet_tools: Default::default(),
ids: RefCell::new(vec![]), ids: RefCell::new(vec![]),
} }
} }
@ -131,6 +133,7 @@ impl Objects {
self.zwlr_data_sources.clear(); self.zwlr_data_sources.clear();
self.jay_toplevels.clear(); self.jay_toplevels.clear();
self.drm_lease_outputs.clear(); self.drm_lease_outputs.clear();
self.tablet_tools.clear();
} }
pub fn id<T>(&self, client_data: &Client) -> Result<T, ClientError> pub fn id<T>(&self, client_data: &Client) -> Result<T, ClientError>

View file

@ -238,9 +238,12 @@ fn start_compositor2(
security_context_acceptors: Default::default(), security_context_acceptors: Default::default(),
cursor_user_group_ids: Default::default(), cursor_user_group_ids: Default::default(),
cursor_user_ids: Default::default(), cursor_user_ids: Default::default(),
cursor_users: Default::default(),
cursor_user_groups: Default::default(), cursor_user_groups: Default::default(),
cursor_user_group_hardware_cursor: 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)); state.tracker.register(ClientId::from_raw(0));
create_dummy_output(&state); create_dummy_output(&state);

View file

@ -18,6 +18,7 @@ use {
wl_registry::WlRegistry, wl_registry::WlRegistry,
wl_seat::{ wl_seat::{
ext_transient_seat_manager_v1::ExtTransientSeatManagerV1Global, ext_transient_seat_manager_v1::ExtTransientSeatManagerV1Global,
tablet::zwp_tablet_manager_v2::ZwpTabletManagerV2Global,
text_input::{ text_input::{
zwp_input_method_manager_v2::ZwpInputMethodManagerV2Global, zwp_input_method_manager_v2::ZwpInputMethodManagerV2Global,
zwp_text_input_manager_v3::ZwpTextInputManagerV3Global, zwp_text_input_manager_v3::ZwpTextInputManagerV3Global,
@ -193,6 +194,7 @@ impl Globals {
add_singleton!(XdgWmDialogV1Global); add_singleton!(XdgWmDialogV1Global);
add_singleton!(ExtTransientSeatManagerV1Global); add_singleton!(ExtTransientSeatManagerV1Global);
add_singleton!(ZwpPointerGesturesV1Global); add_singleton!(ZwpPointerGesturesV1Global);
add_singleton!(ZwpTabletManagerV2Global);
} }
pub fn add_backend_singletons(&self, backend: &Rc<dyn Backend>) { pub fn add_backend_singletons(&self, backend: &Rc<dyn Backend>) {

View file

@ -3,7 +3,14 @@ use {
backend::{InputDeviceId, KeyState}, backend::{InputDeviceId, KeyState},
client::Client, client::Client,
fixed::Fixed, 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, leaks::Tracker,
object::{Object, Version}, object::{Object, Version},
wire::{jay_seat_events::*, JaySeatEventsId}, wire::{jay_seat_events::*, JaySeatEventsId},
@ -236,6 +243,231 @@ impl JaySeatEvents {
event: event as _, 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<TabletStripEventSource>,
position: Option<f64>,
) {
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<TabletRingEventSource>,
degrees: Option<f64>,
) {
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 { impl JaySeatEventsRequestHandler for JaySeatEvents {

View file

@ -4,6 +4,7 @@ pub mod ext_transient_seat_v1;
mod gesture_owner; mod gesture_owner;
mod kb_owner; mod kb_owner;
mod pointer_owner; mod pointer_owner;
pub mod tablet;
pub mod text_input; pub mod text_input;
pub mod wl_keyboard; pub mod wl_keyboard;
pub mod wl_pointer; pub mod wl_pointer;
@ -46,6 +47,7 @@ use {
gesture_owner::GestureOwnerHolder, gesture_owner::GestureOwnerHolder,
kb_owner::KbOwnerHolder, kb_owner::KbOwnerHolder,
pointer_owner::PointerOwnerHolder, pointer_owner::PointerOwnerHolder,
tablet::TabletSeatData,
text_input::{ text_input::{
zwp_input_method_keyboard_grab_v2::ZwpInputMethodKeyboardGrabV2, zwp_input_method_keyboard_grab_v2::ZwpInputMethodKeyboardGrabV2,
zwp_input_method_v2::ZwpInputMethodV2, zwp_text_input_v3::ZwpTextInputV3, zwp_input_method_v2::ZwpInputMethodV2, zwp_text_input_v3::ZwpTextInputV3,
@ -64,6 +66,7 @@ use {
}, },
leaks::Tracker, leaks::Tracker,
object::{Object, Version}, object::{Object, Version},
rect::Rect,
state::{DeviceHandlerData, State}, state::{DeviceHandlerData, State},
time::now_usec, time::now_usec,
tree::{ tree::{
@ -188,6 +191,7 @@ pub struct WlSeatGlobal {
swipe_bindings: PerClientBindings<ZwpPointerGestureSwipeV1>, swipe_bindings: PerClientBindings<ZwpPointerGestureSwipeV1>,
pinch_bindings: PerClientBindings<ZwpPointerGesturePinchV1>, pinch_bindings: PerClientBindings<ZwpPointerGesturePinchV1>,
hold_bindings: PerClientBindings<ZwpPointerGestureHoldV1>, hold_bindings: PerClientBindings<ZwpPointerGestureHoldV1>,
tablet: TabletSeatData,
} }
const CHANGE_CURSOR_MOVED: u32 = 1 << 0; const CHANGE_CURSOR_MOVED: u32 = 1 << 0;
@ -252,6 +256,7 @@ impl WlSeatGlobal {
swipe_bindings: Default::default(), swipe_bindings: Default::default(),
pinch_bindings: Default::default(), pinch_bindings: Default::default(),
hold_bindings: Default::default(), hold_bindings: Default::default(),
tablet: Default::default(),
}); });
slf.pointer_cursor.set_owner(slf.clone()); slf.pointer_cursor.set_owner(slf.clone());
let seat = slf.clone(); let seat = slf.clone();
@ -861,6 +866,7 @@ impl WlSeatGlobal {
self.pinch_bindings.clear(); self.pinch_bindings.clear();
self.hold_bindings.clear(); self.hold_bindings.clear();
self.cursor_user_group.detach(); self.cursor_user_group.detach();
self.tablet_clear();
} }
pub fn id(&self) -> SeatId { pub fn id(&self) -> SeatId {
@ -1122,7 +1128,7 @@ impl DeviceHandlerData {
pub fn set_seat(&self, seat: Option<Rc<WlSeatGlobal>>) { pub fn set_seat(&self, seat: Option<Rc<WlSeatGlobal>>) {
let old = self.seat.set(seat.clone()); let old = self.seat.set(seat.clone());
if let Some(old) = old { if let Some(old) = old {
if let Some(new) = seat { if let Some(new) = &seat {
if old.id() == new.id() { if old.id() == new.id() {
return; return;
} }
@ -1131,8 +1137,22 @@ impl DeviceHandlerData {
let xkb_state = &mut *xkb_state.borrow_mut(); let xkb_state = &mut *xkb_state.borrow_mut();
xkb_state.reset(); xkb_state.reset();
old.handle_xkb_state_change(xkb_state, xkb_state); 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(); 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<Rc<XkbKeymap>>) { pub fn set_keymap(&self, keymap: Option<Rc<XkbKeymap>>) {
@ -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()
}
} }

View file

@ -15,6 +15,7 @@ use {
DynDataSource, DynDataSource,
}, },
wl_seat::{ wl_seat::{
tablet::{TabletPad, TabletPadId, TabletTool, TabletToolId},
text_input::TextDisconnectReason, text_input::TextDisconnectReason,
wl_keyboard::{self, WlKeyboard}, wl_keyboard::{self, WlKeyboard},
wl_pointer::{ wl_pointer::{
@ -25,7 +26,7 @@ use {
}, },
zwp_pointer_constraints_v1::{ConstraintType, SeatConstraintStatus}, zwp_pointer_constraints_v1::{ConstraintType, SeatConstraintStatus},
zwp_relative_pointer_v1::ZwpRelativePointerV1, 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}, wl_surface::{xdg_surface::xdg_popup::XdgPopup, WlSurface},
}, },
@ -55,6 +56,8 @@ pub struct NodeSeatState {
gesture_foci: SmallMap<SeatId, Rc<WlSeatGlobal>, 1>, gesture_foci: SmallMap<SeatId, Rc<WlSeatGlobal>, 1>,
pointer_grabs: SmallMap<SeatId, Rc<WlSeatGlobal>, 1>, pointer_grabs: SmallMap<SeatId, Rc<WlSeatGlobal>, 1>,
dnd_targets: SmallMap<SeatId, Rc<WlSeatGlobal>, 1>, dnd_targets: SmallMap<SeatId, Rc<WlSeatGlobal>, 1>,
tablet_pad_foci: SmallMap<TabletPadId, Rc<TabletPad>, 1>,
tablet_tool_foci: SmallMap<TabletToolId, Rc<TabletTool>, 1>,
} }
impl NodeSeatState { impl NodeSeatState {
@ -92,6 +95,22 @@ impl NodeSeatState {
self.pointer_grabs.remove(&seat.id); self.pointer_grabs.remove(&seat.id);
} }
pub(super) fn add_tablet_pad_focus(&self, pad: &Rc<TabletPad>) {
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<TabletTool>) {
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<WlSeatGlobal>) { pub(super) fn add_dnd_target(&self, seat: &Rc<WlSeatGlobal>) {
self.dnd_targets.insert(seat.id, seat.clone()); self.dnd_targets.insert(seat.id, seat.clone());
} }
@ -163,6 +182,12 @@ impl NodeSeatState {
seat.pointer_stack_modified.set(true); seat.pointer_stack_modified.set(true);
seat.state.tree_changed(); 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); self.release_kb_focus2(focus_last);
} }
@ -203,7 +228,13 @@ impl WlSeatGlobal {
| InputEvent::PinchEnd { time_usec, .. } | InputEvent::PinchEnd { time_usec, .. }
| InputEvent::HoldBegin { time_usec, .. } | InputEvent::HoldBegin { time_usec, .. }
| InputEvent::HoldEnd { 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); self.last_input_usec.set(time_usec);
if self.idle_notifications.is_not_empty() { if self.idle_notifications.is_not_empty() {
for (_, notification) in self.idle_notifications.lock().drain() { for (_, notification) in self.idle_notifications.lock().drain() {
@ -214,7 +245,39 @@ impl WlSeatGlobal {
InputEvent::AxisPx { .. } InputEvent::AxisPx { .. }
| InputEvent::AxisSource { .. } | InputEvent::AxisSource { .. }
| InputEvent::AxisStop { .. } | 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 { match event {
InputEvent::Key { InputEvent::Key {
@ -305,6 +368,49 @@ impl WlSeatGlobal {
InputEvent::SwitchEvent { time_usec, event } => { InputEvent::SwitchEvent { time_usec, event } => {
self.switch_event(dev.device.id(), 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>) { pub(super) fn apply_changes(self: &Rc<Self>) {
self.state.damage(); self.state.damage();
self.pointer_owner.apply_changes(self); self.pointer_owner.apply_changes(self);
if self.changes.get().contains(CHANGE_TREE) {
self.tablet_apply_changes();
}
self.changes.set(0); self.changes.set(0);
} }
} }

View file

@ -79,6 +79,7 @@ impl KbOwner for DefaultKbOwner {
// log::info!("focus {}", node.node_id()); // log::info!("focus {}", node.node_id());
node.clone().node_on_focus(seat); node.clone().node_on_focus(seat);
seat.keyboard_node.set(node.clone()); seat.keyboard_node.set(node.clone());
seat.tablet_on_keyboard_node_change();
} }
} }

393
src/ifs/wl_seat/tablet.rs Normal file
View file

@ -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<ZwpTabletSeatV2>,
tablets: CopyHashMap<TabletId, Rc<Tablet>>,
tools: CopyHashMap<TabletToolId, Rc<TabletTool>>,
pads: CopyHashMap<TabletPadId, Rc<TabletPad>>,
}
#[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<TabletToolCapability>,
}
#[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<TabletPadGroupInit>,
}
#[derive(Debug, Clone)]
pub struct TabletPadGroupInit {
pub buttons: Vec<u32>,
pub rings: Vec<u32>,
pub strips: Vec<u32>,
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<ZwpTabletV2>,
tools: CopyHashMap<TabletToolId, Rc<TabletTool>>,
pads: CopyHashMap<TabletPadId, Rc<TabletPad>>,
tree: RefCell<Vec<FoundNode>>,
seat: Rc<WlSeatGlobal>,
}
#[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<Option<Rc<TabletTool>>>,
}
pub struct TabletTool {
pub id: TabletToolId,
opt: Rc<TabletToolOpt>,
tablet: Rc<Tablet>,
type_: TabletToolType,
hardware_serial: u64,
hardware_id_wacom: u64,
capabilities: Vec<TabletToolCapability>,
bindings: TabletBindings<ZwpTabletToolV2>,
node: CloneCell<Rc<dyn Node>>,
pub(super) tool_owner: ToolOwnerHolder,
cursor: Rc<CursorUser>,
down: Cell<bool>,
pressure: Cell<f64>,
distance: Cell<f64>,
tilt_x: Cell<f64>,
tilt_y: Cell<f64>,
rotation: Cell<f64>,
slider: Cell<f64>,
}
linear_ids!(TabletPadIds, TabletPadId);
pub struct TabletPad {
pub id: TabletPadId,
dev: InputDeviceId,
seat: Rc<WlSeatGlobal>,
group: InputDeviceGroupId,
tablet: CloneCell<Option<Rc<Tablet>>>,
path: String,
buttons: u32,
bindings: TabletBindings<ZwpTabletPadV2>,
groups: Vec<Rc<TabletPadGroup>>,
strips: Vec<Rc<TabletPadStrip>>,
rings: Vec<Rc<TabletPadRing>>,
node: CloneCell<Rc<dyn Node>>,
pub(super) pad_owner: PadOwnerHolder,
}
pub struct TabletPadGroup {
buttons: Vec<u32>,
mode: Cell<u32>,
modes: u32,
rings: Vec<u32>,
strips: Vec<u32>,
bindings: TabletBindings<ZwpTabletPadGroupV2>,
}
pub struct TabletPadStrip {
bindings: TabletBindings<ZwpTabletPadStripV2>,
}
pub struct TabletPadRing {
bindings: TabletBindings<ZwpTabletPadRingV2>,
}
#[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<bool>,
pub pos: Option<TabletTool2dChange<TabletToolPositionChange>>,
pub pressure: Option<f64>,
pub distance: Option<f64>,
pub tilt: Option<TabletTool2dChange<f64>>,
pub rotation: Option<f64>,
pub slider: Option<f64>,
pub wheel: Option<TabletToolWheelChange>,
}
#[derive(Copy, Clone, Debug)]
pub struct TabletTool2dChange<T> {
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<ZwpTabletSeatV2>) {
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<Self>, 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<ZwpTabletSeatV2>)) {
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<Self>, 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<Self>, tablet: &Rc<Tablet>, pad: &Rc<TabletPad>) {
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<Self>) {
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<Self>) {
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<CursorUser> {
&self.cursor
}
pub fn node(&self) -> Rc<dyn Node> {
self.node.get()
}
pub fn seat(&self) -> &Rc<WlSeatGlobal> {
&self.tablet.seat
}
}

View file

@ -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<Self>, 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<Self>, 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<Self>,
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<Self>,
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<Self>,
pad: TabletPadId,
ring: u32,
source: Option<TabletRingEventSource>,
angle: Option<f64>,
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<Self>,
pad: TabletPadId,
strip: u32,
source: Option<TabletStripEventSource>,
position: Option<f64>,
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<Self>, 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<Self>, 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<Self>,
n: &WlSurface,
ring: &Rc<TabletPadRing>,
source: Option<TabletRingEventSource>,
angle: Option<f64>,
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<Self>,
n: &WlSurface,
strip: &Rc<TabletPadStrip>,
source: Option<TabletStripEventSource>,
position: Option<f64>,
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<Self>,
n: &WlSurface,
group: &Rc<TabletPadGroup>,
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<Self>,
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);
})
}
}

View file

@ -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<DefaultPadOwner>,
owner: CloneCell<Rc<dyn PadOwner>>,
}
trait PadOwner {
fn revert_to_default(&self, pad: &Rc<TabletPad>, time_usec: u64);
fn update_node(&self, pad: &Rc<TabletPad>);
fn button(&self, pad: &Rc<TabletPad>, time_usec: u64, button: u32, state: PadButtonState);
}
struct DefaultPadOwner;
struct GrabPadOwner {
buttons: SmallMap<u32, (), 4>,
node: Rc<dyn Node>,
}
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<TabletPad>) {
self.owner.get().update_node(pad);
}
pub fn destroy(&self, pad: &Rc<TabletPad>) {
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<TabletPad>, time_usec: u64, button: u32, state: PadButtonState) {
self.owner.get().button(pad, time_usec, button, state);
}
pub fn focus_root(&self, pad: &Rc<TabletPad>) {
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<Self>, node: Rc<dyn Node>) {
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<TabletPad>, _time_usec: u64) {
// nothing
}
fn update_node(&self, pad: &Rc<TabletPad>) {
let node = pad.seat.keyboard_node.get();
pad.focus_node(node);
}
fn button(&self, pad: &Rc<TabletPad>, 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<TabletPad>, 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<TabletPad>) {
// nothing
}
fn button(&self, pad: &Rc<TabletPad>, 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);
}
}
}

View file

@ -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<T> {
bindings: CopyHashMap<(ClientId, ZwpTabletSeatV2Id), Rc<T>>,
}
impl<T> Default for TabletBindings<T> {
fn default() -> Self {
Self {
bindings: Default::default(),
}
}
}
impl<T> TabletBindings<T> {
pub fn add(&self, seat: &ZwpTabletSeatV2, t: &Rc<T>) {
self.bindings.set((seat.client.id, seat.id), t.clone());
}
pub fn get(&self, seat: &ZwpTabletSeatV2) -> Option<Rc<T>> {
self.bindings.get(&(seat.client.id, seat.id))
}
pub fn remove(&self, seat: &ZwpTabletSeatV2) -> Option<Rc<T>> {
self.bindings.remove(&(seat.client.id, seat.id))
}
pub fn lock(&self) -> Locked<'_, (ClientId, ZwpTabletSeatV2Id), Rc<T>> {
self.bindings.lock()
}
pub fn clear(&self) {
self.bindings.clear();
}
}

View file

@ -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<Self>, 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<Self>, 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<Self>,
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<Self>,
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<Rc<TabletTool>> {
self.tool.get()
}
}

View file

@ -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<DefaultToolOwner>,
owner: CloneCell<Rc<dyn ToolOwner>>,
}
struct DefaultToolOwner;
struct GrabToolOwner {
buttons: SmallMap<u32, (), 4>,
node: Rc<dyn Node>,
}
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<TabletTool>) {
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<TabletTool>) {
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<TabletTool>,
time_usec: u64,
button: u32,
state: ToolButtonState,
) {
self.owner.get().button(tool, time_usec, button, state);
}
pub fn apply_changes(
&self,
tool: &Rc<TabletTool>,
time_usec: u64,
changes: Option<&TabletToolChanges>,
) {
self.owner.get().apply_changes(tool, time_usec, changes);
}
}
trait ToolOwner {
fn button(&self, tool: &Rc<TabletTool>, time_usec: u64, button: u32, state: ToolButtonState);
fn apply_changes(
&self,
tool: &Rc<TabletTool>,
time_usec: u64,
changes: Option<&TabletToolChanges>,
);
}
impl TabletTool {
fn set_node(self: &Rc<Self>, node: Rc<dyn Node>, 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<TabletTool>, 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<TabletTool>,
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<TabletTool>) {
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<TabletTool>, 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<TabletTool>,
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<TabletTool>) -> 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<dyn Node>,
changed: bool,
x: Fixed,
y: Fixed,
}

View file

@ -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<Client>,
pub tracker: Tracker<Self>,
pub version: Version,
}
impl ZwpTabletManagerV2Global {
pub fn new(name: GlobalName) -> Self {
Self { name }
}
fn bind_(
self: Rc<Self>,
id: ZwpTabletManagerV2Id,
client: &Rc<Client>,
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<Self>) -> 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<Self>) -> 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<ClientError>),
}
efrom!(ZwpTabletManagerV2Error, ClientError);

View file

@ -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<Client>,
pub tracker: Tracker<Self>,
pub version: Version,
pub seat: Rc<ZwpTabletSeatV2>,
pub group: Rc<TabletPadGroup>,
}
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<Self>) -> 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<ClientError>),
}
efrom!(ZwpTabletPadGroupV2Error, ClientError);

View file

@ -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<Client>,
pub tracker: Tracker<Self>,
pub version: Version,
pub seat: Rc<ZwpTabletSeatV2>,
pub ring: Rc<TabletPadRing>,
}
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<Self>) -> Result<(), Self::Error> {
Ok(())
}
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> 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<ClientError>),
}
efrom!(ZwpTabletPadRingV2Error, ClientError);

View file

@ -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<Client>,
pub tracker: Tracker<Self>,
pub version: Version,
pub seat: Rc<ZwpTabletSeatV2>,
pub strip: Rc<TabletPadStrip>,
}
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<Self>) -> Result<(), Self::Error> {
Ok(())
}
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> 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<ClientError>),
}
efrom!(ZwpTabletPadStripV2Error, ClientError);

View file

@ -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<Client>,
pub tracker: Tracker<Self>,
pub version: Version,
pub seat: Rc<ZwpTabletSeatV2>,
pub pad: Rc<TabletPad>,
pub entered: Cell<bool>,
}
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<Self>) -> Result<(), Self::Error> {
Ok(())
}
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> 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<ClientError>),
}
efrom!(ZwpTabletPadV2Error, ClientError);

View file

@ -0,0 +1,221 @@
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<Client>,
pub seat: Rc<WlSeatGlobal>,
pub tracker: Tracker<Self>,
pub version: Version,
}
impl ZwpTabletSeatV2 {
pub fn detach(&self) {
self.seat.tablet.seats.remove(&self.client, self);
}
pub fn announce_tablet(self: &Rc<Self>, tablet: &Rc<Tablet>) {
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<Self>, tool: &Rc<TabletTool>) {
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<Self>, pad: &Rc<TabletPad>) {
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();
pad.bindings.add(self, &obj);
}
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<Self>) -> 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<ClientError>),
}
efrom!(ZwpTabletSeatV2Error, ClientError);

View file

@ -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<Client>,
pub tracker: Tracker<Self>,
pub version: Version,
pub seat: Rc<ZwpTabletSeatV2>,
pub tool: Rc<TabletToolOpt>,
pub entered: Cell<bool>,
}
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<Self>) -> 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<dyn Cursor>);
}
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<Self>) -> 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<ClientError>),
#[error(transparent)]
WlSurfaceError(Box<WlSurfaceError>),
}
efrom!(ZwpTabletToolV2Error, ClientError);
efrom!(ZwpTabletToolV2Error, WlSurfaceError);

View file

@ -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<Client>,
pub tracker: Tracker<Self>,
pub version: Version,
pub seat: Rc<ZwpTabletSeatV2>,
pub tablet: Rc<Tablet>,
}
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<Self>) -> 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<ClientError>),
}
efrom!(ZwpTabletV2Error, ClientError);

View file

@ -26,9 +26,15 @@ use {
wl_buffer::WlBuffer, wl_buffer::WlBuffer,
wl_callback::WlCallback, wl_callback::WlCallback,
wl_seat::{ wl_seat::{
text_input::TextInputConnection, wl_pointer::PendingScroll, tablet::{
zwp_pointer_constraints_v1::SeatConstraint, Dnd, NodeSeatState, SeatId, PadButtonState, TabletPad, TabletPadGroup, TabletPadRing, TabletPadStrip,
WlSeatGlobal, TabletRingEventSource, TabletStripEventSource, TabletTool, TabletToolChanges,
ToolButtonState,
},
text_input::TextInputConnection,
wl_pointer::PendingScroll,
zwp_pointer_constraints_v1::SeatConstraint,
Dnd, NodeSeatState, SeatId, WlSeatGlobal,
}, },
wl_surface::{ wl_surface::{
commit_timeline::{ClearReason, CommitTimeline, CommitTimelineError}, commit_timeline::{ClearReason, CommitTimeline, CommitTimelineError},
@ -1497,14 +1503,6 @@ impl Node for WlSurface {
dnd.seat.dnd_surface_motion(self, dnd, time_usec, x, y); dnd.seat.dnd_surface_motion(self, dnd, time_usec, x, y);
} }
fn node_into_surface(self: Rc<Self>) -> Option<Rc<WlSurface>> {
Some(self.clone())
}
fn node_is_xwayland_surface(&self) -> bool {
self.client.is_xwayland
}
fn node_on_swipe_begin(&self, seat: &Rc<WlSeatGlobal>, time_usec: u64, finger_count: u32) { fn node_on_swipe_begin(&self, seat: &Rc<WlSeatGlobal>, time_usec: u64, finger_count: u32) {
seat.swipe_begin_surface(self, time_usec, finger_count) seat.swipe_begin_surface(self, time_usec, finger_count)
} }
@ -1544,6 +1542,99 @@ impl Node for WlSurface {
fn node_on_hold_end(&self, seat: &Rc<WlSeatGlobal>, time_usec: u64, cancelled: bool) { fn node_on_hold_end(&self, seat: &Rc<WlSeatGlobal>, time_usec: u64, cancelled: bool) {
seat.hold_end_surface(self, time_usec, cancelled) seat.hold_end_surface(self, time_usec, cancelled)
} }
fn node_on_tablet_pad_enter(&self, pad: &Rc<TabletPad>) {
pad.surface_enter(self);
}
fn node_on_tablet_pad_leave(&self, pad: &Rc<TabletPad>) {
pad.surface_leave(self);
}
fn node_on_tablet_pad_button(
&self,
pad: &Rc<TabletPad>,
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<TabletPad>,
group: &Rc<TabletPadGroup>,
time_usec: u64,
mode: u32,
) {
pad.surface_mode_switch(self, group, time_usec, mode);
}
fn node_on_tablet_pad_ring(
&self,
pad: &Rc<TabletPad>,
ring: &Rc<TabletPadRing>,
source: Option<TabletRingEventSource>,
angle: Option<f64>,
time_usec: u64,
) {
pad.surface_ring(self, ring, source, angle, time_usec);
}
fn node_on_tablet_pad_strip(
&self,
pad: &Rc<TabletPad>,
strip: &Rc<TabletPadStrip>,
source: Option<TabletStripEventSource>,
position: Option<f64>,
time_usec: u64,
) {
pad.surface_strip(self, strip, source, position, time_usec);
}
fn node_on_tablet_tool_leave(&self, tool: &Rc<TabletTool>, time_usec: u64) {
tool.surface_leave(self, time_usec);
}
fn node_on_tablet_tool_enter(
self: Rc<Self>,
tool: &Rc<TabletTool>,
time_usec: u64,
x: Fixed,
y: Fixed,
) {
tool.surface_enter(&self, time_usec, x, y);
}
fn node_on_tablet_tool_button(
&self,
tool: &Rc<TabletTool>,
time_usec: u64,
button: u32,
state: ToolButtonState,
) {
tool.surface_button(self, time_usec, button, state);
}
fn node_on_tablet_tool_apply_changes(
self: Rc<Self>,
tool: &Rc<TabletTool>,
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<Self>) -> Option<Rc<WlSurface>> {
Some(self.clone())
}
fn node_is_xwayland_surface(&self) -> bool {
self.client.is_xwayland
}
} }
#[derive(Debug, Error)] #[derive(Debug, Error)]

View file

@ -4,7 +4,7 @@ use {
cursor::KnownCursor, cursor::KnownCursor,
fixed::Fixed, fixed::Fixed,
ifs::{ ifs::{
wl_seat::{NodeSeatState, SeatId, WlSeatGlobal}, wl_seat::{tablet::TabletTool, NodeSeatState, SeatId, WlSeatGlobal},
wl_surface::{x_surface::XSurface, WlSurface, WlSurfaceError}, wl_surface::{x_surface::XSurface, WlSurface, WlSurfaceError},
}, },
rect::Rect, rect::Rect,
@ -369,6 +369,16 @@ impl Node for Xwindow {
seat.pointer_cursor().set_known(KnownCursor::Default); seat.pointer_cursor().set_known(KnownCursor::Default);
} }
fn node_on_tablet_tool_enter(
self: Rc<Self>,
tool: &Rc<TabletTool>,
_time_usec: u64,
_x: Fixed,
_y: Fixed,
) {
tool.cursor().set_known(KnownCursor::Default)
}
fn node_into_toplevel(self: Rc<Self>) -> Option<Rc<dyn ToplevelNode>> { fn node_into_toplevel(self: Rc<Self>) -> Option<Rc<dyn ToplevelNode>> {
Some(self) Some(self)
} }

View file

@ -4,7 +4,7 @@ use {
cursor::KnownCursor, cursor::KnownCursor,
fixed::Fixed, fixed::Fixed,
ifs::{ ifs::{
wl_seat::{NodeSeatState, WlSeatGlobal}, wl_seat::{tablet::TabletTool, NodeSeatState, WlSeatGlobal},
wl_surface::xdg_surface::{XdgSurface, XdgSurfaceError, XdgSurfaceExt}, wl_surface::xdg_surface::{XdgSurface, XdgSurfaceError, XdgSurfaceExt},
xdg_positioner::{ xdg_positioner::{
XdgPositioned, XdgPositioner, CA_FLIP_X, CA_FLIP_Y, CA_RESIZE_X, CA_RESIZE_Y, 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"); // log::info!("xdg-popup focus");
seat.pointer_cursor().set_known(KnownCursor::Default); seat.pointer_cursor().set_known(KnownCursor::Default);
} }
fn node_on_tablet_tool_enter(
self: Rc<Self>,
tool: &Rc<TabletTool>,
_time_usec: u64,
_x: Fixed,
_y: Fixed,
) {
tool.cursor().set_known(KnownCursor::Default)
}
} }
impl StackedNode for XdgPopup { impl StackedNode for XdgPopup {

View file

@ -9,7 +9,7 @@ use {
fixed::Fixed, fixed::Fixed,
ifs::{ ifs::{
ext_foreign_toplevel_list_v1::ExtForeignToplevelListV1, ext_foreign_toplevel_list_v1::ExtForeignToplevelListV1,
wl_seat::{NodeSeatState, SeatId, WlSeatGlobal}, wl_seat::{tablet::TabletTool, NodeSeatState, SeatId, WlSeatGlobal},
wl_surface::{ wl_surface::{
xdg_surface::{ xdg_surface::{
xdg_toplevel::xdg_dialog_v1::XdgDialogV1, XdgSurface, XdgSurfaceError, xdg_toplevel::xdg_dialog_v1::XdgDialogV1, XdgSurface, XdgSurfaceError,
@ -529,6 +529,16 @@ impl Node for XdgToplevel {
seat.pointer_cursor().set_known(KnownCursor::Default); seat.pointer_cursor().set_known(KnownCursor::Default);
} }
fn node_on_tablet_tool_enter(
self: Rc<Self>,
tool: &Rc<TabletTool>,
_time_usec: u64,
_x: Fixed,
_y: Fixed,
) {
tool.cursor().set_known(KnownCursor::Default)
}
fn node_into_toplevel(self: Rc<Self>) -> Option<Rc<dyn ToplevelNode>> { fn node_into_toplevel(self: Rc<Self>) -> Option<Rc<dyn ToplevelNode>> {
Some(self) Some(self)
} }

View file

@ -2,7 +2,7 @@ use {
crate::{ crate::{
client::{Client, ClientError}, client::{Client, ClientError},
cursor::KnownCursor, cursor::KnownCursor,
ifs::wl_seat::WlSeatGlobal, ifs::wl_seat::{tablet::TabletToolOpt, WlSeatGlobal},
leaks::Tracker, leaks::Tracker,
object::{Object, Version}, object::{Object, Version},
wire::{wp_cursor_shape_device_v1::*, WpCursorShapeDeviceV1Id}, wire::{wp_cursor_shape_device_v1::*, WpCursorShapeDeviceV1Id},
@ -46,10 +46,15 @@ const ALL_SCROLL: u32 = 32;
const ZOOM_IN: u32 = 33; const ZOOM_IN: u32 = 33;
const ZOOM_OUT: u32 = 34; const ZOOM_OUT: u32 = 34;
pub enum CursorShapeCursorUser {
Seat(Rc<WlSeatGlobal>),
TabletTool(Rc<TabletToolOpt>),
}
pub struct WpCursorShapeDeviceV1 { pub struct WpCursorShapeDeviceV1 {
pub id: WpCursorShapeDeviceV1Id, pub id: WpCursorShapeDeviceV1Id,
pub client: Rc<Client>, pub client: Rc<Client>,
pub seat: Rc<WlSeatGlobal>, pub cursor_user: CursorShapeCursorUser,
pub tracker: Tracker<Self>, pub tracker: Tracker<Self>,
pub version: Version, pub version: Version,
} }
@ -100,14 +105,24 @@ impl WpCursorShapeDeviceV1RequestHandler for WpCursorShapeDeviceV1 {
ZOOM_OUT => KnownCursor::ZoomOut, ZOOM_OUT => KnownCursor::ZoomOut,
_ => return Err(WpCursorShapeDeviceV1Error::UnknownShape(req.shape)), _ => return Err(WpCursorShapeDeviceV1Error::UnknownShape(req.shape)),
}; };
let pointer_node = match self.seat.pointer_node() { let tablet_tool;
Some(n) => n, let (node_client_id, user) = match &self.cursor_user {
_ => return Ok(()), 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(()); return Ok(());
} }
self.seat.pointer_cursor().set_known(cursor); user.set_known(cursor);
Ok(()) Ok(())
} }
} }

View file

@ -2,10 +2,10 @@ use {
crate::{ crate::{
client::{Client, ClientError}, client::{Client, ClientError},
globals::{Global, GlobalName}, globals::{Global, GlobalName},
ifs::wp_cursor_shape_device_v1::WpCursorShapeDeviceV1, ifs::wp_cursor_shape_device_v1::{CursorShapeCursorUser, WpCursorShapeDeviceV1},
leaks::Tracker, leaks::Tracker,
object::{Object, Version}, object::{Object, Version},
wire::{wp_cursor_shape_manager_v1::*, WpCursorShapeManagerV1Id}, wire::{wp_cursor_shape_manager_v1::*, WpCursorShapeDeviceV1Id, WpCursorShapeManagerV1Id},
}, },
std::rc::Rc, std::rc::Rc,
thiserror::Error, thiserror::Error,
@ -63,6 +63,25 @@ pub struct WpCursorShapeManagerV1 {
pub version: Version, 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 { impl WpCursorShapeManagerV1RequestHandler for WpCursorShapeManagerV1 {
type Error = WpCursorShapeManagerV1Error; type Error = WpCursorShapeManagerV1Error;
@ -73,24 +92,18 @@ impl WpCursorShapeManagerV1RequestHandler for WpCursorShapeManagerV1 {
fn get_pointer(&self, req: GetPointer, _slf: &Rc<Self>) -> Result<(), Self::Error> { fn get_pointer(&self, req: GetPointer, _slf: &Rc<Self>) -> Result<(), Self::Error> {
let pointer = self.client.lookup(req.pointer)?; let pointer = self.client.lookup(req.pointer)?;
let device = Rc::new(WpCursorShapeDeviceV1 { self.get(
id: req.cursor_shape_device, req.cursor_shape_device,
client: self.client.clone(), CursorShapeCursorUser::Seat(pointer.seat.global.clone()),
seat: pointer.seat.global.clone(), )
tracker: Default::default(),
version: self.version,
});
track!(self.client, device);
self.client.add_client_obj(&device)?;
Ok(())
} }
fn get_tablet_tool_v2( fn get_tablet_tool_v2(&self, req: GetTabletToolV2, _slf: &Rc<Self>) -> Result<(), Self::Error> {
&self, let tool = self.client.lookup(req.tablet_tool)?;
_req: GetTabletToolV2, self.get(
_slf: &Rc<Self>, req.cursor_shape_device,
) -> Result<(), Self::Error> { CursorShapeCursorUser::TabletTool(tool.tool.clone()),
Err(WpCursorShapeManagerV1Error::TabletToolNotSupported) )
} }
} }
@ -107,7 +120,5 @@ simple_add_obj!(WpCursorShapeManagerV1);
pub enum WpCursorShapeManagerV1Error { pub enum WpCursorShapeManagerV1Error {
#[error(transparent)] #[error(transparent)]
ClientError(Box<ClientError>), ClientError(Box<ClientError>),
#[error("This compositor does not support tablet tools")]
TabletToolNotSupported,
} }
efrom!(WpCursorShapeManagerV1Error, ClientError); efrom!(WpCursorShapeManagerV1Error, ClientError);

View file

@ -21,9 +21,19 @@ use {
libinput_device_config_tap_get_enabled, libinput_device_config_tap_get_finger_count, 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_enabled,
libinput_device_config_tap_set_drag_lock_enabled, libinput_device_config_tap_set_drag_lock_enabled,
libinput_device_config_tap_set_enabled, libinput_device_get_name, libinput_device_config_tap_set_enabled, libinput_device_get_device_group,
libinput_device_get_user_data, libinput_device_has_capability, libinput_device_get_id_product, libinput_device_get_id_vendor,
libinput_device_set_user_data, libinput_device_unref, libinput_path_remove_device, 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, LibInput,
}, },
@ -36,6 +46,16 @@ pub struct LibInputDevice<'a> {
pub(super) _phantom: PhantomData<&'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 struct RegisteredDevice {
pub(super) _li: Rc<LibInput>, pub(super) _li: Rc<LibInput>,
pub(super) dev: *mut libinput_device, pub(super) dev: *mut libinput_device,
@ -191,6 +211,96 @@ impl<'a> LibInputDevice<'a> {
pub fn has_natural_scrolling(&self) -> bool { pub fn has_natural_scrolling(&self) -> bool {
unsafe { libinput_device_config_scroll_has_natural_scroll(self.dev) != 0 } 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<LibInputTabletPadModeGroup<'_>> {
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 { impl RegisteredDevice {

View file

@ -1,7 +1,11 @@
use { use {
crate::libinput::{ crate::libinput::{
consts::{ButtonState, EventType, KeyState, PointerAxis, Switch, SwitchState}, consts::{
device::LibInputDevice, ButtonState, EventType, KeyState, PointerAxis, Switch, SwitchState,
TabletPadRingAxisSource, TabletPadStripAxisSource, TabletToolProximityState,
TabletToolTipState, TabletToolType,
},
device::{LibInputDevice, LibInputTabletPadModeGroup},
sys::{ sys::{
libinput_event, libinput_event_destroy, libinput_event_gesture, libinput_event, libinput_event_destroy, libinput_event_gesture,
libinput_event_gesture_get_angle_delta, libinput_event_gesture_get_cancelled, 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_gesture_get_time_usec, libinput_event_get_device,
libinput_event_get_gesture_event, libinput_event_get_keyboard_event, libinput_event_get_gesture_event, libinput_event_get_keyboard_event,
libinput_event_get_pointer_event, libinput_event_get_switch_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_get_type, libinput_event_keyboard, libinput_event_keyboard_get_key,
libinput_event_keyboard_get_key_state, libinput_event_keyboard_get_time_usec, libinput_event_keyboard_get_key_state, libinput_event_keyboard_get_time_usec,
libinput_event_pointer, libinput_event_pointer_get_button, 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_get_scroll_value_v120, libinput_event_pointer_get_time_usec,
libinput_event_pointer_has_axis, libinput_event_switch, libinput_event_pointer_has_axis, libinput_event_switch,
libinput_event_switch_get_switch, libinput_event_switch_get_switch_state, 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, std::marker::PhantomData,
@ -51,6 +74,21 @@ pub struct LibInputEventSwitch<'a> {
pub(super) _phantom: PhantomData<&'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> { impl<'a> Drop for LibInputEvent<'a> {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { 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> { impl<'a> LibInputEvent<'a> {
pub fn ty(&self) -> EventType { pub fn ty(&self) -> EventType {
unsafe { EventType(libinput_event_get_type(self.event)) } unsafe { EventType(libinput_event_get_type(self.event)) }
@ -71,53 +125,36 @@ impl<'a> LibInputEvent<'a> {
} }
} }
pub fn keyboard_event(&self) -> Option<LibInputEventKeyboard> { converter!(
let res = unsafe { libinput_event_get_keyboard_event(self.event) }; keyboard_event,
if res.is_null() { LibInputEventKeyboard,
None libinput_event_get_keyboard_event
} else { );
Some(LibInputEventKeyboard { converter!(
event: res, pointer_event,
_phantom: Default::default(), LibInputEventPointer,
}) libinput_event_get_pointer_event
} );
} converter!(
gesture_event,
pub fn pointer_event(&self) -> Option<LibInputEventPointer> { LibInputEventGesture,
let res = unsafe { libinput_event_get_pointer_event(self.event) }; libinput_event_get_gesture_event
if res.is_null() { );
None converter!(
} else { switch_event,
Some(LibInputEventPointer { LibInputEventSwitch,
event: res, libinput_event_get_switch_event
_phantom: Default::default(), );
}) converter!(
} tablet_tool_event,
} LibInputEventTabletTool,
libinput_event_get_tablet_tool_event
pub fn gesture_event(&self) -> Option<LibInputEventGesture> { );
let res = unsafe { libinput_event_get_gesture_event(self.event) }; converter!(
if res.is_null() { tablet_pad_event,
None LibInputEventTabletPad,
} else { libinput_event_get_tablet_pad_event
Some(LibInputEventGesture { );
event: res,
_phantom: Default::default(),
})
}
}
pub fn switch_event(&self) -> Option<LibInputEventSwitch> {
let res = unsafe { libinput_event_get_switch_event(self.event) };
if res.is_null() {
None
} else {
Some(LibInputEventSwitch {
event: res,
_phantom: Default::default(),
})
}
}
} }
impl<'a> LibInputEventKeyboard<'a> { impl<'a> LibInputEventKeyboard<'a> {
@ -228,3 +265,205 @@ impl<'a> LibInputEventSwitch<'a> {
unsafe { SwitchState(libinput_event_switch_get_switch_state(self.event)) } 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(),
}
}
}

View file

@ -9,6 +9,8 @@ pub struct libinput(u8);
#[repr(transparent)] #[repr(transparent)]
pub struct libinput_device(u8); pub struct libinput_device(u8);
#[repr(transparent)] #[repr(transparent)]
pub struct libinput_device_group(u8);
#[repr(transparent)]
pub struct libinput_event(u8); pub struct libinput_event(u8);
#[repr(transparent)] #[repr(transparent)]
pub struct libinput_event_keyboard(u8); pub struct libinput_event_keyboard(u8);
@ -18,6 +20,16 @@ pub struct libinput_event_pointer(u8);
pub struct libinput_event_gesture(u8); pub struct libinput_event_gesture(u8);
#[repr(transparent)] #[repr(transparent)]
pub struct libinput_event_switch(u8); 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")] #[link(name = "input")]
extern "C" { extern "C" {
@ -166,6 +178,185 @@ extern "C" {
event: *mut libinput_event_switch, event: *mut libinput_event_switch,
) -> libinput_switch_state; ) -> libinput_switch_state;
pub fn libinput_event_switch_get_time_usec(event: *mut libinput_event_switch) -> u64; 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)] #[repr(C)]

View file

@ -4,7 +4,8 @@ use {
async_engine::{AsyncEngine, SpawnedFuture}, async_engine::{AsyncEngine, SpawnedFuture},
backend::{ backend::{
Backend, BackendDrmDevice, BackendEvent, Connector, ConnectorId, ConnectorIds, Backend, BackendDrmDevice, BackendEvent, Connector, ConnectorId, ConnectorIds,
DrmDeviceId, DrmDeviceIds, InputDevice, InputDeviceId, InputDeviceIds, MonitorInfo, DrmDeviceId, DrmDeviceIds, InputDevice, InputDeviceGroupIds, InputDeviceId,
InputDeviceIds, MonitorInfo,
}, },
backends::dummy::DummyBackend, backends::dummy::DummyBackend,
cli::RunArgs, cli::RunArgs,
@ -34,7 +35,10 @@ use {
jay_workspace_watcher::JayWorkspaceWatcher, jay_workspace_watcher::JayWorkspaceWatcher,
wl_drm::WlDrmGlobal, wl_drm::WlDrmGlobal,
wl_output::{OutputGlobalOpt, OutputId, PersistentOutputState}, wl_output::{OutputGlobalOpt, OutputId, PersistentOutputState},
wl_seat::{SeatIds, WlSeatGlobal}, wl_seat::{
tablet::{TabletIds, TabletInit, TabletPadIds, TabletPadInit, TabletToolIds},
SeatIds, WlSeatGlobal,
},
wl_surface::{ wl_surface::{
wl_subsurface::SubsurfaceIds, wl_subsurface::SubsurfaceIds,
zwp_idle_inhibitor_v1::{IdleInhibitorId, IdleInhibitorIds, ZwpIdleInhibitorV1}, zwp_idle_inhibitor_v1::{IdleInhibitorId, IdleInhibitorIds, ZwpIdleInhibitorV1},
@ -188,9 +192,12 @@ pub struct State {
pub security_context_acceptors: SecurityContextAcceptors, pub security_context_acceptors: SecurityContextAcceptors,
pub cursor_user_group_ids: CursorUserGroupIds, pub cursor_user_group_ids: CursorUserGroupIds,
pub cursor_user_ids: CursorUserIds, pub cursor_user_ids: CursorUserIds,
pub cursor_users: CopyHashMap<CursorUserGroupId, Rc<CursorUserGroup>>,
pub cursor_user_groups: CopyHashMap<CursorUserGroupId, Rc<CursorUserGroup>>, pub cursor_user_groups: CopyHashMap<CursorUserGroupId, Rc<CursorUserGroup>>,
pub cursor_user_group_hardware_cursor: CloneCell<Option<Rc<CursorUserGroup>>>, pub cursor_user_group_hardware_cursor: CloneCell<Option<Rc<CursorUserGroup>>>,
pub input_device_group_ids: InputDeviceGroupIds,
pub tablet_ids: TabletIds,
pub tablet_tool_ids: TabletToolIds,
pub tablet_pad_ids: TabletPadIds,
} }
// impl Drop for State { // impl Drop for State {
@ -263,6 +270,8 @@ pub struct DeviceHandlerData {
pub keymap: CloneCell<Option<Rc<XkbKeymap>>>, pub keymap: CloneCell<Option<Rc<XkbKeymap>>>,
pub xkb_state: CloneCell<Option<Rc<RefCell<XkbState>>>>, pub xkb_state: CloneCell<Option<Rc<RefCell<XkbState>>>>,
pub output: CloneCell<Option<Rc<OutputGlobalOpt>>>, pub output: CloneCell<Option<Rc<OutputGlobalOpt>>>,
pub tablet_init: Option<Box<TabletInit>>,
pub tablet_pad_init: Option<Box<TabletPadInit>>,
} }
pub struct ConnectorData { pub struct ConnectorData {

View file

@ -24,6 +24,8 @@ pub fn handle(state: &Rc<State>, dev: Rc<dyn InputDevice>) {
keymap: Default::default(), keymap: Default::default(),
xkb_state: Default::default(), xkb_state: Default::default(),
output: Default::default(), output: Default::default(),
tablet_init: dev.tablet_info(),
tablet_pad_init: dev.tablet_pad_info(),
}); });
let ae = Rc::new(AsyncEvent::default()); let ae = Rc::new(AsyncEvent::default());
let oh = DeviceHandler { let oh = DeviceHandler {

View file

@ -122,3 +122,7 @@ impl Add<Duration> for Time {
pub fn now_usec() -> u64 { pub fn now_usec() -> u64 {
Time::now_unchecked().usec() Time::now_unchecked().usec()
} }
pub fn usec_to_msec(usec: u64) -> u32 {
(usec / 1000) as u32
}

View file

@ -4,7 +4,15 @@ use {
client::{Client, ClientId}, client::{Client, ClientId},
fixed::Fixed, fixed::Fixed,
ifs::{ 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, wl_surface::WlSurface,
}, },
rect::Rect, rect::Rect,
@ -344,6 +352,116 @@ pub trait Node: 'static {
let _ = cancelled; let _ = cancelled;
} }
fn node_on_tablet_pad_enter(&self, pad: &Rc<TabletPad>) {
let _ = pad;
}
fn node_on_tablet_pad_leave(&self, pad: &Rc<TabletPad>) {
let _ = pad;
}
fn node_on_tablet_pad_button(
&self,
pad: &Rc<TabletPad>,
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<TabletPad>,
group: &Rc<TabletPadGroup>,
time_usec: u64,
mode: u32,
) {
let _ = pad;
let _ = group;
let _ = time_usec;
let _ = mode;
}
fn node_on_tablet_pad_ring(
&self,
pad: &Rc<TabletPad>,
ring: &Rc<TabletPadRing>,
source: Option<TabletRingEventSource>,
angle: Option<f64>,
time_usec: u64,
) {
let _ = pad;
let _ = time_usec;
let _ = ring;
let _ = source;
let _ = angle;
}
fn node_on_tablet_pad_strip(
&self,
pad: &Rc<TabletPad>,
strip: &Rc<TabletPadStrip>,
source: Option<TabletStripEventSource>,
position: Option<f64>,
time_usec: u64,
) {
let _ = pad;
let _ = time_usec;
let _ = strip;
let _ = source;
let _ = position;
}
fn node_on_tablet_tool_leave(&self, tool: &Rc<TabletTool>, time_usec: u64) {
let _ = tool;
let _ = time_usec;
}
fn node_on_tablet_tool_enter(
self: Rc<Self>,
tool: &Rc<TabletTool>,
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<TabletTool>,
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<Self>,
tool: &Rc<TabletTool>,
time_usec: u64,
changes: Option<&TabletToolChanges>,
x: Fixed,
y: Fixed,
) {
let _ = tool;
let _ = time_usec;
let _ = changes;
let _ = x;
let _ = y;
}
// TYPE CONVERTERS // TYPE CONVERTERS
#[cfg_attr(not(feature = "it"), allow(dead_code))] #[cfg_attr(not(feature = "it"), allow(dead_code))]

View file

@ -2,10 +2,13 @@ use {
crate::{ crate::{
backend::KeyState, backend::KeyState,
cursor::KnownCursor, cursor::KnownCursor,
cursor_user::CursorUser,
fixed::Fixed, fixed::Fixed,
ifs::wl_seat::{ ifs::wl_seat::{
collect_kb_foci, collect_kb_foci2, wl_pointer::PendingScroll, NodeSeatState, SeatId, collect_kb_foci, collect_kb_foci2,
WlSeatGlobal, BTN_LEFT, tablet::{TabletTool, TabletToolChanges, TabletToolId},
wl_pointer::PendingScroll,
NodeSeatState, SeatId, WlSeatGlobal, BTN_LEFT,
}, },
rect::Rect, rect::Rect,
renderer::Renderer, renderer::Renderer,
@ -112,7 +115,7 @@ pub struct ContainerNode {
focus_history: LinkedList<NodeRef<ContainerChild>>, focus_history: LinkedList<NodeRef<ContainerChild>>,
child_nodes: RefCell<AHashMap<NodeId, LinkedNode<ContainerChild>>>, child_nodes: RefCell<AHashMap<NodeId, LinkedNode<ContainerChild>>>,
workspace: CloneCell<Rc<WorkspaceNode>>, workspace: CloneCell<Rc<WorkspaceNode>>,
seats: RefCell<AHashMap<SeatId, SeatState>>, cursors: RefCell<AHashMap<CursorType, CursorState>>,
state: Rc<State>, state: Rc<State>,
pub render_data: RefCell<ContainerRenderData>, pub render_data: RefCell<ContainerRenderData>,
scroller: Scroller, scroller: Scroller,
@ -141,7 +144,13 @@ pub struct ContainerChild {
factor: Cell<f64>, factor: Cell<f64>,
} }
struct SeatState { #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
enum CursorType {
Seat(SeatId),
TabletTool(TabletToolId),
}
struct CursorState {
cursor: KnownCursor, cursor: KnownCursor,
target: bool, target: bool,
x: i32, x: i32,
@ -210,7 +219,7 @@ impl ContainerNode {
focus_history: Default::default(), focus_history: Default::default(),
child_nodes: RefCell::new(child_nodes), child_nodes: RefCell::new(child_nodes),
workspace: CloneCell::new(workspace.clone()), workspace: CloneCell::new(workspace.clone()),
seats: RefCell::new(Default::default()), cursors: RefCell::new(Default::default()),
state: state.clone(), state: state.clone(),
render_data: Default::default(), render_data: Default::default(),
scroller: Default::default(), scroller: Default::default(),
@ -328,7 +337,7 @@ impl ContainerNode {
} }
fn cancel_seat_ops(&self) { 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() { for seat in seats.values_mut() {
seat.op = None; seat.op = None;
} }
@ -514,12 +523,21 @@ impl ContainerNode {
); );
} }
fn pointer_move(self: &Rc<Self>, seat: &Rc<WlSeatGlobal>, mut x: i32, mut y: i32) { fn pointer_move(
self: &Rc<Self>,
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 title_height = self.state.theme.sizes.title_height.get();
let mut seats = self.seats.borrow_mut(); let mut seats = self.cursors.borrow_mut();
let seat_state = seats.entry(seat.id()).or_insert_with(|| SeatState { let seat_state = seats.entry(id).or_insert_with(|| CursorState {
cursor: KnownCursor::Default, cursor: KnownCursor::Default,
target: false, target,
x, x,
y, y,
op: None, op: None,
@ -601,7 +619,7 @@ impl ContainerNode {
}; };
if new_cursor != mem::replace(&mut seat_state.cursor, new_cursor) { if new_cursor != mem::replace(&mut seat_state.cursor, new_cursor) {
if seat_state.target { 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<Self>,
id: CursorType,
seat: &Rc<WlSeatGlobal>,
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 { struct SeatOp {
@ -1194,76 +1286,14 @@ impl Node for ContainerNode {
if button != BTN_LEFT { if button != BTN_LEFT {
return; return;
} }
let mut seat_datas = self.seats.borrow_mut(); let id = CursorType::Seat(seat.id());
let seat_data = match seat_datas.get_mut(&seat.id()) { self.button(id, seat, time_usec, state == KeyState::Pressed);
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
}
}
} }
fn node_on_axis_event(self: Rc<Self>, seat: &Rc<WlSeatGlobal>, event: &PendingScroll) { fn node_on_axis_event(self: Rc<Self>, seat: &Rc<WlSeatGlobal>, event: &PendingScroll) {
let mut seat_datas = self.seats.borrow_mut(); let mut seat_datas = self.cursors.borrow_mut();
let seat_data = match seat_datas.get_mut(&seat.id()) { let id = CursorType::Seat(seat.id());
let seat_data = match seat_datas.get_mut(&id) {
Some(s) => s, Some(s) => s,
_ => return, _ => return,
}; };
@ -1299,21 +1329,29 @@ impl Node for ContainerNode {
fn node_on_pointer_enter(self: Rc<Self>, seat: &Rc<WlSeatGlobal>, x: Fixed, y: Fixed) { fn node_on_pointer_enter(self: Rc<Self>, seat: &Rc<WlSeatGlobal>, x: Fixed, y: Fixed) {
// log::info!("node_on_pointer_enter"); // 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<WlSeatGlobal>) { fn node_on_pointer_unfocus(&self, seat: &Rc<WlSeatGlobal>) {
// log::info!("unfocus"); // log::info!("unfocus");
let mut seats = self.seats.borrow_mut(); let mut seats = self.cursors.borrow_mut();
if let Some(seat_state) = seats.get_mut(&seat.id()) { let id = CursorType::Seat(seat.id());
if let Some(seat_state) = seats.get_mut(&id) {
seat_state.target = false; seat_state.target = false;
} }
} }
fn node_on_pointer_focus(&self, seat: &Rc<WlSeatGlobal>) { fn node_on_pointer_focus(&self, seat: &Rc<WlSeatGlobal>) {
// log::info!("container focus"); // log::info!("container focus");
let mut seats = self.seats.borrow_mut(); let mut seats = self.cursors.borrow_mut();
if let Some(seat_state) = seats.get_mut(&seat.id()) { let id = CursorType::Seat(seat.id());
if let Some(seat_state) = seats.get_mut(&id) {
seat_state.target = true; seat_state.target = true;
seat.pointer_cursor().set_known(seat_state.cursor); seat.pointer_cursor().set_known(seat_state.cursor);
} }
@ -1321,7 +1359,46 @@ impl Node for ContainerNode {
fn node_on_pointer_motion(self: Rc<Self>, seat: &Rc<WlSeatGlobal>, x: Fixed, y: Fixed) { fn node_on_pointer_motion(self: Rc<Self>, seat: &Rc<WlSeatGlobal>, x: Fixed, y: Fixed) {
// log::info!("node_on_pointer_motion"); // 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<TabletTool>, _time_usec: u64) {
let id = CursorType::TabletTool(tool.id);
self.cursors.borrow_mut().remove(&id);
}
fn node_on_tablet_tool_enter(
self: Rc<Self>,
tool: &Rc<TabletTool>,
_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<Self>,
tool: &Rc<TabletTool>,
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<Self>) -> Option<Rc<ContainerNode>> { fn node_into_container(self: Rc<Self>) -> Option<Rc<ContainerNode>> {
@ -1332,13 +1409,13 @@ impl Node for ContainerNode {
Some(self) Some(self)
} }
fn node_is_container(&self) -> bool {
true
}
fn node_into_toplevel(self: Rc<Self>) -> Option<Rc<dyn ToplevelNode>> { fn node_into_toplevel(self: Rc<Self>) -> Option<Rc<dyn ToplevelNode>> {
Some(self) Some(self)
} }
fn node_is_container(&self) -> bool {
true
}
} }
impl ContainingNode for ContainerNode { impl ContainingNode for ContainerNode {
@ -1534,7 +1611,7 @@ impl ToplevelNodeBase for ContainerNode {
} }
fn tl_destroy_impl(&self) { 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(); let mut cn = self.child_nodes.borrow_mut();
for (_, n) in cn.drain() { for (_, n) in cn.drain() {
n.node.tl_destroy(); n.node.tl_destroy();

View file

@ -2,7 +2,8 @@ use {
crate::{ crate::{
backend::ConnectorId, backend::ConnectorId,
cursor::KnownCursor, cursor::KnownCursor,
ifs::wl_seat::{NodeSeatState, WlSeatGlobal}, fixed::Fixed,
ifs::wl_seat::{tablet::TabletTool, NodeSeatState, WlSeatGlobal},
rect::Rect, rect::Rect,
renderer::Renderer, renderer::Renderer,
state::State, state::State,
@ -142,4 +143,14 @@ impl Node for DisplayNode {
// log::info!("display focus"); // log::info!("display focus");
seat.pointer_cursor().set_known(KnownCursor::Default); seat.pointer_cursor().set_known(KnownCursor::Default);
} }
fn node_on_tablet_tool_enter(
self: Rc<Self>,
tool: &Rc<TabletTool>,
_time_usec: u64,
_x: Fixed,
_y: Fixed,
) {
tool.cursor().set_known(KnownCursor::Default)
}
} }

View file

@ -2,8 +2,12 @@ use {
crate::{ crate::{
backend::KeyState, backend::KeyState,
cursor::KnownCursor, cursor::KnownCursor,
cursor_user::CursorUser,
fixed::Fixed, fixed::Fixed,
ifs::wl_seat::{NodeSeatState, SeatId, WlSeatGlobal, BTN_LEFT}, ifs::wl_seat::{
tablet::{TabletTool, TabletToolChanges, TabletToolId},
NodeSeatState, SeatId, WlSeatGlobal, BTN_LEFT,
},
rect::Rect, rect::Rect,
renderer::Renderer, renderer::Renderer,
scale::Scale, scale::Scale,
@ -44,11 +48,17 @@ pub struct FloatNode {
pub render_titles_scheduled: Cell<bool>, pub render_titles_scheduled: Cell<bool>,
pub title: RefCell<String>, pub title: RefCell<String>,
pub title_textures: CopyHashMap<Scale, TextTexture>, pub title_textures: CopyHashMap<Scale, TextTexture>,
seats: RefCell<AHashMap<SeatId, SeatState>>, cursors: RefCell<AHashMap<CursorType, CursorState>>,
pub attention_requested: Cell<bool>, pub attention_requested: Cell<bool>,
} }
struct SeatState { #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
enum CursorType {
Seat(SeatId),
TabletTool(TabletToolId),
}
struct CursorState {
cursor: KnownCursor, cursor: KnownCursor,
target: bool, target: bool,
x: i32, x: i32,
@ -113,7 +123,7 @@ impl FloatNode {
render_titles_scheduled: Cell::new(false), render_titles_scheduled: Cell::new(false),
title: Default::default(), title: Default::default(),
title_textures: Default::default(), title_textures: Default::default(),
seats: Default::default(), cursors: Default::default(),
attention_requested: Cell::new(false), attention_requested: Cell::new(false),
}); });
floater.pull_child_properties(); floater.pull_child_properties();
@ -214,14 +224,23 @@ impl FloatNode {
} }
} }
fn pointer_move(self: &Rc<Self>, seat: &Rc<WlSeatGlobal>, x: i32, y: i32) { fn pointer_move(
self: &Rc<Self>,
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 theme = &self.state.theme;
let bw = theme.sizes.border_width.get(); let bw = theme.sizes.border_width.get();
let th = theme.sizes.title_height.get(); let th = theme.sizes.title_height.get();
let mut seats = self.seats.borrow_mut(); let mut seats = self.cursors.borrow_mut();
let seat_state = seats.entry(seat.id()).or_insert_with(|| SeatState { let seat_state = seats.entry(id).or_insert_with(|| CursorState {
cursor: KnownCursor::Default, cursor: KnownCursor::Default,
target: false, target,
x, x,
y, y,
op_type: OpType::Move, op_type: OpType::Move,
@ -289,6 +308,7 @@ impl FloatNode {
} }
} }
self.position.set(Rect::new(x1, y1, x2, y2).unwrap()); self.position.set(Rect::new(x1, y1, x2, y2).unwrap());
self.state.damage();
self.schedule_layout(); self.schedule_layout();
return; return;
} }
@ -334,7 +354,7 @@ impl FloatNode {
seat_state.op_type = op_type; seat_state.op_type = op_type;
if new_cursor != mem::replace(&mut seat_state.cursor, new_cursor) { if new_cursor != mem::replace(&mut seat_state.cursor, new_cursor) {
if seat_state.target { 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(); self.state.tree_changed();
} }
} }
fn button(
self: Rc<Self>,
id: CursorType,
cursor: &CursorUser,
seat: &Rc<WlSeatGlobal>,
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 { impl Debug for FloatNode {
@ -487,89 +578,89 @@ impl Node for FloatNode {
if button != BTN_LEFT { if button != BTN_LEFT {
return; return;
} }
let mut seat_datas = self.seats.borrow_mut(); self.button(
let seat_data = match seat_datas.get_mut(&seat.id()) { CursorType::Seat(seat.id()),
Some(s) => s, seat.pointer_cursor(),
_ => return, seat,
}; time_usec,
if !seat_data.op_active { state == KeyState::Pressed,
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);
}
} }
fn node_on_pointer_enter(self: Rc<Self>, seat: &Rc<WlSeatGlobal>, x: Fixed, y: Fixed) { fn node_on_pointer_enter(self: Rc<Self>, seat: &Rc<WlSeatGlobal>, 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<WlSeatGlobal>) { fn node_on_pointer_unfocus(&self, seat: &Rc<WlSeatGlobal>) {
let mut seats = self.seats.borrow_mut(); let mut cursors = self.cursors.borrow_mut();
if let Some(seat_state) = seats.get_mut(&seat.id()) { let id = CursorType::Seat(seat.id());
if let Some(seat_state) = cursors.get_mut(&id) {
seat_state.target = false; seat_state.target = false;
} }
} }
fn node_on_pointer_focus(&self, seat: &Rc<WlSeatGlobal>) { fn node_on_pointer_focus(&self, seat: &Rc<WlSeatGlobal>) {
// log::info!("float focus"); // log::info!("float focus");
let mut seats = self.seats.borrow_mut(); let mut cursors = self.cursors.borrow_mut();
if let Some(seat_state) = seats.get_mut(&seat.id()) { let id = CursorType::Seat(seat.id());
if let Some(seat_state) = cursors.get_mut(&id) {
seat_state.target = true; seat_state.target = true;
seat.pointer_cursor().set_known(seat_state.cursor); seat.pointer_cursor().set_known(seat_state.cursor);
} }
} }
fn node_on_pointer_motion(self: Rc<Self>, seat: &Rc<WlSeatGlobal>, x: Fixed, y: Fixed) { fn node_on_pointer_motion(self: Rc<Self>, seat: &Rc<WlSeatGlobal>, 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<TabletTool>, _time_usec: u64) {
let id = CursorType::TabletTool(tool.id);
self.cursors.borrow_mut().remove(&id);
}
fn node_on_tablet_tool_enter(
self: Rc<Self>,
tool: &Rc<TabletTool>,
_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<Self>,
tool: &Rc<TabletTool>,
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<Self>) -> Option<Rc<FloatNode>> { fn node_into_float(self: Rc<Self>) -> Option<Rc<FloatNode>> {

View file

@ -11,8 +11,10 @@ use {
wl_buffer::WlBufferStorage, wl_buffer::WlBufferStorage,
wl_output::WlOutputGlobal, wl_output::WlOutputGlobal,
wl_seat::{ wl_seat::{
collect_kb_foci2, wl_pointer::PendingScroll, NodeSeatState, SeatId, WlSeatGlobal, collect_kb_foci2,
BTN_LEFT, tablet::{TabletTool, TabletToolChanges, TabletToolId},
wl_pointer::PendingScroll,
NodeSeatState, SeatId, WlSeatGlobal, BTN_LEFT,
}, },
wl_surface::{ wl_surface::{
ext_session_lock_surface_v1::ExtSessionLockSurfaceV1, ext_session_lock_surface_v1::ExtSessionLockSurfaceV1,
@ -63,7 +65,7 @@ pub struct OutputNode {
pub is_dummy: bool, pub is_dummy: bool,
pub status: CloneCell<Rc<String>>, pub status: CloneCell<Rc<String>>,
pub scroll: Scroller, pub scroll: Scroller,
pub pointer_positions: CopyHashMap<SeatId, (i32, i32)>, pub pointer_positions: CopyHashMap<PointerType, (i32, i32)>,
pub lock_surface: CloneCell<Option<Rc<ExtSessionLockSurfaceV1>>>, pub lock_surface: CloneCell<Option<Rc<ExtSessionLockSurfaceV1>>>,
pub hardware_cursor: CloneCell<Option<Rc<dyn HardwareCursor>>>, pub hardware_cursor: CloneCell<Option<Rc<dyn HardwareCursor>>>,
pub hardware_cursor_needs_render: Cell<bool>, pub hardware_cursor_needs_render: Cell<bool>,
@ -72,6 +74,12 @@ pub struct OutputNode {
pub screencopies: CopyHashMap<(ClientId, ZwlrScreencopyFrameV1Id), Rc<ZwlrScreencopyFrameV1>>, pub screencopies: CopyHashMap<(ClientId, ZwlrScreencopyFrameV1Id), Rc<ZwlrScreencopyFrameV1>>,
} }
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub enum PointerType {
Seat(SeatId),
TabletTool(TabletToolId),
}
pub async fn output_render_data(state: Rc<State>) { pub async fn output_render_data(state: Rc<State>) {
loop { loop {
let container = state.pending_output_render_data.pop().await; let container = state.pending_output_render_data.pop().await;
@ -593,8 +601,9 @@ impl OutputNode {
self.schedule_update_render_data(); self.schedule_update_render_data();
} }
fn pointer_move(self: &Rc<Self>, seat: &Rc<WlSeatGlobal>, x: i32, y: i32) { fn pointer_move(self: &Rc<Self>, id: PointerType, x: Fixed, y: Fixed) {
self.pointer_positions.set(seat.id(), (x, y)); self.pointer_positions
.set(id, (x.round_down(), y.round_down()));
} }
pub fn has_fullscreen(&self) -> bool { pub fn has_fullscreen(&self) -> bool {
@ -641,6 +650,29 @@ impl OutputNode {
set_layer_visible!(self.layers[2], visible); set_layer_visible!(self.layers[2], visible);
set_layer_visible!(self.layers[3], visible); set_layer_visible!(self.layers[3], visible);
} }
fn button(self: Rc<Self>, 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 { pub struct OutputTitle {
@ -844,26 +876,7 @@ impl Node for OutputNode {
if state != KeyState::Pressed || button != BTN_LEFT { if state != KeyState::Pressed || button != BTN_LEFT {
return; return;
} }
let (x, y) = match self.pointer_positions.get(&seat.id()) { self.button(PointerType::Seat(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();
} }
fn node_on_axis_event(self: Rc<Self>, seat: &Rc<WlSeatGlobal>, event: &PendingScroll) { fn node_on_axis_event(self: Rc<Self>, seat: &Rc<WlSeatGlobal>, event: &PendingScroll) {
@ -905,7 +918,7 @@ impl Node for OutputNode {
} }
fn node_on_pointer_enter(self: Rc<Self>, seat: &Rc<WlSeatGlobal>, x: Fixed, y: Fixed) { fn node_on_pointer_enter(self: Rc<Self>, seat: &Rc<WlSeatGlobal>, 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<WlSeatGlobal>) { fn node_on_pointer_focus(&self, seat: &Rc<WlSeatGlobal>) {
@ -914,7 +927,40 @@ impl Node for OutputNode {
} }
fn node_on_pointer_motion(self: Rc<Self>, seat: &Rc<WlSeatGlobal>, x: Fixed, y: Fixed) { fn node_on_pointer_motion(self: Rc<Self>, seat: &Rc<WlSeatGlobal>, 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<TabletTool>, _time_usec: u64) {
self.pointer_positions
.remove(&PointerType::TabletTool(tool.id));
}
fn node_on_tablet_tool_enter(
self: Rc<Self>,
tool: &Rc<TabletTool>,
_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<Self>,
tool: &Rc<TabletTool>,
_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);
}
}
} }
} }

View file

@ -2,10 +2,11 @@ use {
crate::{ crate::{
client::ClientId, client::ClientId,
cursor::KnownCursor, cursor::KnownCursor,
fixed::Fixed,
ifs::{ ifs::{
jay_workspace::JayWorkspace, jay_workspace::JayWorkspace,
wl_output::OutputId, wl_output::OutputId,
wl_seat::{NodeSeatState, WlSeatGlobal}, wl_seat::{tablet::TabletTool, NodeSeatState, WlSeatGlobal},
wl_surface::WlSurface, wl_surface::WlSurface,
}, },
rect::Rect, rect::Rect,
@ -277,6 +278,16 @@ impl Node for WorkspaceNode {
seat.pointer_cursor().set_known(KnownCursor::Default); seat.pointer_cursor().set_known(KnownCursor::Default);
} }
fn node_on_tablet_tool_enter(
self: Rc<Self>,
tool: &Rc<TabletTool>,
_time_usec: u64,
_x: Fixed,
_y: Fixed,
) {
tool.cursor().set_known(KnownCursor::Default)
}
fn node_into_workspace(self: Rc<Self>) -> Option<Rc<WorkspaceNode>> { fn node_into_workspace(self: Rc<Self>) -> Option<Rc<WorkspaceNode>> {
Some(self.clone()) Some(self.clone())
} }

View file

@ -31,6 +31,7 @@ pub mod opaque_cell;
pub mod option_ext; pub mod option_ext;
pub mod oserror; pub mod oserror;
pub mod page_size; pub mod page_size;
pub mod pending_serial;
pub mod process_name; pub mod process_name;
pub mod ptr_ext; pub mod ptr_ext;
pub mod queue; pub mod queue;

View file

@ -5,7 +5,11 @@ use {
utils::copyhashmap::{CopyHashMap, Locked}, utils::copyhashmap::{CopyHashMap, Locked},
}, },
ahash::AHashMap, ahash::AHashMap,
std::{cell::RefCell, collections::hash_map::Entry, rc::Rc}, std::{
cell::{Ref, RefCell},
collections::hash_map::Entry,
rc::Rc,
},
}; };
pub struct Bindings<P> { pub struct Bindings<P> {
@ -84,4 +88,8 @@ impl<P: Object> PerClientBindings<P> {
} }
} }
} }
pub fn borrow(&self) -> Ref<AHashMap<ClientId, AHashMap<ObjectId, Rc<P>>>> {
self.bindings.borrow()
}
} }

View file

@ -66,7 +66,7 @@ impl<K: Eq + Hash, V> CopyHashMap<K, V> {
unsafe { self.map.get().deref().contains_key(k) } unsafe { self.map.get().deref().contains_key(k) }
} }
pub fn lock(&self) -> Locked<K, V> { pub fn lock(&self) -> Locked<'_, K, V> {
Locked { Locked {
source: self, source: self,
map: self.clear(), map: self.clear(),

View file

@ -0,0 +1,19 @@
use crate::client::Client;
pub struct PendingSerial<'a> {
serial: Option<u32>,
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())
}
}

View file

@ -132,3 +132,110 @@ event switch_event {
input_device: u32, input_device: u32,
event: 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,
}

View file

@ -0,0 +1,7 @@
request get_tablet_seat {
tablet_seat: id(zwp_tablet_seat_v2),
seat: id(wl_seat),
}
request destroy {
}

View file

@ -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,
}

View file

@ -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,
}

View file

@ -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,
}

View file

@ -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 {
}

View file

@ -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),
}

View file

@ -1,5 +1,3 @@
# requests
request set_cursor { request set_cursor {
serial: u32, serial: u32,
surface: id(wl_surface), surface: id(wl_surface),
@ -9,3 +7,84 @@ request set_cursor {
request destroy { 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,
}

21
wire/zwp_tablet_v2.txt Normal file
View file

@ -0,0 +1,21 @@
request destroy {
}
event name {
name: str,
}
event id {
vid: u32,
pid: u32,
}
event path {
path: str,
}
event done {
}
event removed {
}