diff --git a/anvil/src/input_handler.rs b/anvil/src/input_handler.rs index 284f62a..7a21b0f 100644 --- a/anvil/src/input_handler.rs +++ b/anvil/src/input_handler.rs @@ -8,13 +8,15 @@ use crate::AnvilState; use smithay::{ backend::input::{ - self, Event, InputBackend, InputEvent, KeyState, KeyboardKeyEvent, PointerAxisEvent, - PointerButtonEvent, + self, Device, DeviceCapability, Event, InputBackend, InputEvent, KeyState, KeyboardKeyEvent, + PointerAxisEvent, PointerButtonEvent, ProximityState, TabletToolButtonEvent, TabletToolEvent, + TabletToolProximityEvent, TabletToolTipEvent, TabletToolTipState, }, reexports::wayland_server::protocol::wl_pointer, wayland::{ output::Mode, seat::{keysyms as xkb, AxisFrame, Keysym, ModifiersState}, + tablet_manager::TabletSeatTrait, SERIAL_COUNTER as SCOUNTER, }, }; @@ -238,6 +240,27 @@ impl AnvilState { InputEvent::PointerMotion { event, .. } => self.on_pointer_move::(event), InputEvent::PointerButton { event, .. } => self.on_pointer_button::(event), InputEvent::PointerAxis { event, .. } => self.on_pointer_axis::(event), + InputEvent::TabletToolAxis { event, .. } => self.on_tablet_tool_axis::(event), + InputEvent::TabletToolProximity { event, .. } => self.on_tablet_tool_proximity::(event), + InputEvent::TabletToolTip { event, .. } => self.on_tablet_tool_tip::(event), + InputEvent::TabletToolButton { event, .. } => self.on_tablet_button::(event), + InputEvent::DeviceAdded { device } => { + if device.has_capability(DeviceCapability::TabletTool) { + self.seat.tablet_seat().add_tablet(&device.into()); + } + } + InputEvent::DeviceRemoved { device } => { + if device.has_capability(DeviceCapability::TabletTool) { + let tablet_seat = self.seat.tablet_seat(); + + tablet_seat.remove_tablet(&device.into()); + + // If there are no tablets in seat we can remove all tools + if tablet_seat.count_tablets() == 0 { + tablet_seat.clear_tools(); + } + } + } _ => { // other events are not handled in anvil (yet) } @@ -259,6 +282,111 @@ impl AnvilState { .motion(self.pointer_location, under, serial, evt.time()); } + fn on_tablet_tool_axis(&mut self, evt: B::TabletToolAxisEvent) { + if let Some(out) = self.backend_data.output_map.first() { + self.pointer_location = evt.position_transformed(out.size); + + let under = self.window_map.borrow().get_surface_under(self.pointer_location); + let tablet = self.seat.tablet_seat().get_tablet(&evt.device().into()); + let tool = self.seat.tablet_seat().get_tool(&evt.tool()); + + if let (Some(tablet), Some(tool)) = (tablet, tool) { + if evt.pressure_has_changed() { + tool.pressure(evt.pressure()); + } + if evt.distance_has_changed() { + tool.distance(evt.distance()); + } + if evt.tilt_has_changed() { + tool.tilt(evt.tilt()); + } + if evt.slider_has_changed() { + tool.slider_position(evt.slider_position()); + } + if evt.rotation_has_changed() { + tool.rotation(evt.rotation()); + } + if evt.wheel_has_changed() { + tool.wheel(evt.wheel_delta(), evt.wheel_delta_discrete()); + } + + tool.motion( + self.pointer_location, + under, + &tablet, + SCOUNTER.next_serial(), + evt.time(), + ); + } + } + } + + fn on_tablet_tool_proximity(&mut self, evt: B::TabletToolProximityEvent) { + if let Some(out) = self.backend_data.output_map.first() { + let tool = evt.tool(); + self.seat.tablet_seat().add_tool(&tool); + + self.pointer_location = evt.position_transformed(out.size); + + let under = self.window_map.borrow().get_surface_under(self.pointer_location); + let tablet = self.seat.tablet_seat().get_tablet(&evt.device().into()); + let tool = self.seat.tablet_seat().get_tool(&tool); + + if let (Some(under), Some(tablet), Some(tool)) = (under, tablet, tool) { + match evt.state() { + ProximityState::In => tool.proximity_in( + self.pointer_location, + under, + &tablet, + SCOUNTER.next_serial(), + evt.time(), + ), + ProximityState::Out => tool.proximity_out(evt.time()), + } + } + } + } + + fn on_tablet_tool_tip(&mut self, evt: B::TabletToolTipEvent) { + let tool = self.seat.tablet_seat().get_tool(&evt.tool()); + + if let Some(tool) = tool { + match evt.tip_state() { + TabletToolTipState::Down => { + tool.tip_down(SCOUNTER.next_serial(), evt.time()); + + // change the keyboard focus unless the pointer is grabbed + if !self.pointer.is_grabbed() { + let under = self + .window_map + .borrow_mut() + .get_surface_and_bring_to_top(self.pointer_location); + + let serial = SCOUNTER.next_serial(); + self.keyboard + .set_focus(under.as_ref().map(|&(ref s, _)| s), serial); + } + } + TabletToolTipState::Up => { + tool.tip_up(evt.time()); + } + } + } + } + + fn on_tablet_button(&mut self, evt: B::TabletToolButtonEvent) { + let tool = self.seat.tablet_seat().get_tool(&evt.tool()); + + if let Some(tool) = tool { + tool.button( + evt.button(), + evt.button_state(), + SCOUNTER.next_serial(), + evt.time(), + ); + } + } + fn clamp_coords(&self, pos: (f64, f64)) -> (f64, f64) { if self.output_map.borrow().is_empty() { return pos; diff --git a/anvil/src/state.rs b/anvil/src/state.rs index b197677..fc7068d 100644 --- a/anvil/src/state.rs +++ b/anvil/src/state.rs @@ -16,6 +16,7 @@ use smithay::{ data_device::{default_action_chooser, init_data_device, set_data_device_focus, DataDeviceEvent}, seat::{CursorImageStatus, KeyboardHandle, PointerHandle, Seat, XkbConfig}, shm::init_shm_global, + tablet_manager::{init_tablet_manager_global, TabletSeatTrait}, }, }; @@ -41,6 +42,7 @@ pub struct AnvilState { pub pointer_location: (f64, f64), pub cursor_status: Arc>, pub seat_name: String, + pub seat: Seat, pub start_time: std::time::Instant, // things we must keep alive #[cfg(feature = "xwayland")] @@ -121,6 +123,14 @@ impl AnvilState { *cursor_status2.lock().unwrap() = new_status }); + init_tablet_manager_global(&mut display.borrow_mut()); + + let cursor_status3 = cursor_status.clone(); + seat.tablet_seat().on_cursor_surface(move |_tool, new_status| { + // TODO: tablet tools should have their own cursors + *cursor_status3.lock().unwrap() = new_status; + }); + let keyboard = seat .add_keyboard(XkbConfig::default(), 200, 25, |seat, focus| { set_data_device_focus(seat, focus.and_then(|s| s.as_ref().client())) @@ -159,6 +169,7 @@ impl AnvilState { cursor_status, pointer_location: (0.0, 0.0), seat_name, + seat, start_time: std::time::Instant::now(), #[cfg(feature = "xwayland")] xwayland,