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

@ -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);