git.lucas.co / cce-compositor
Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git

src/server/tablet_tool.rs (13.3K)

  1 use crate::ffi;
  2 use crate::tablet::Tablet;
  3 use crate::seat::Seat;
  4 use crate::server::{WlListener, wl_listener_remove, wl_signal_add};
  5 
  6 #[derive(Clone, Copy, Debug, PartialEq)]
  7 pub enum TabletToolMode {
  8     Passthrough,
  9     Down {
 10         lx: f64,
 11         ly: f64,
 12         sx: f64,
 13         sy: f64,
 14     },
 15 }
 16 
 17 pub struct TabletTool {
 18     pub wp_tool: *mut ffi::wlr_tablet_v2_tablet_tool,
 19     pub wlr_cursor: *mut ffi::wlr_cursor,
 20     pub mode: TabletToolMode,
 21     pub tilt_x: f64,
 22     pub tilt_y: f64,
 23 
 24     pub destroy_listener: ffi::wl_listener,
 25     pub set_cursor_listener: ffi::wl_listener,
 26 }
 27 
 28 impl TabletTool {
 29     pub unsafe fn get(
 30         wlr_seat: *mut ffi::wlr_seat,
 31         wlr_tool: *mut ffi::wlr_tablet_tool,
 32     ) -> Result<*mut Self, &'static str> {
 33         let data_ptr = (*wlr_tool).data as *mut Self;
 34         if !data_ptr.is_null() {
 35             Ok(data_ptr)
 36         } else {
 37             Self::create(wlr_seat, wlr_tool)
 38         }
 39     }
 40 
 41     pub unsafe fn create(
 42         wlr_seat: *mut ffi::wlr_seat,
 43         wlr_tool: *mut ffi::wlr_tablet_tool,
 44     ) -> Result<*mut Self, &'static str> {
 45         let wlr_cursor = ffi::wlr_cursor_create();
 46         if wlr_cursor.is_null() {
 47             return Err("Failed to create wlr_cursor");
 48         }
 49 
 50         let seat = ffi::river_wlr_seat_get_data(wlr_seat) as *mut Seat;
 51         if seat.is_null() {
 52             ffi::wlr_cursor_destroy(wlr_cursor);
 53             return Err("Seat data is null");
 54         }
 55         let server = (*seat).server;
 56         let output_layout = (*server).om.output_layout;
 57 
 58         ffi::wlr_cursor_attach_output_layout(wlr_cursor, output_layout);
 59 
 60         let tablet_manager = (*server).input_manager.tablet_manager;
 61         let wp_tool = ffi::wlr_tablet_tool_create(tablet_manager, wlr_seat, wlr_tool);
 62         if wp_tool.is_null() {
 63             ffi::wlr_cursor_destroy(wlr_cursor);
 64             return Err("Failed to create wp_tool");
 65         }
 66 
 67         let tool = Box::into_raw(Box::new(Self {
 68             wp_tool,
 69             wlr_cursor,
 70             mode: TabletToolMode::Passthrough,
 71             tilt_x: 0.0,
 72             tilt_y: 0.0,
 73             destroy_listener: std::mem::zeroed(),
 74             set_cursor_listener: std::mem::zeroed(),
 75         }));
 76 
 77         (*wlr_tool).data = tool as *mut _;
 78 
 79         let destroy_listener_ptr = &mut (*tool).destroy_listener as *mut ffi::wl_listener as *mut WlListener;
 80         (*destroy_listener_ptr).notify = Some(handle_destroy);
 81         wl_signal_add(&mut (*wlr_tool).events.destroy, &mut (*tool).destroy_listener);
 82 
 83         let set_cursor_listener_ptr = &mut (*tool).set_cursor_listener as *mut ffi::wl_listener as *mut WlListener;
 84         (*set_cursor_listener_ptr).notify = Some(handle_set_cursor);
 85         let set_cursor_signal = ffi::river_wlr_tablet_v2_tablet_tool_get_set_cursor_signal(wp_tool);
 86         wl_signal_add(set_cursor_signal, &mut (*tool).set_cursor_listener);
 87 
 88         Ok(tool)
 89     }
 90 
 91     pub unsafe fn cursor_x(&self) -> f64 {
 92         ffi::river_wlr_cursor_get_x(self.wlr_cursor)
 93     }
 94 
 95     pub unsafe fn cursor_y(&self) -> f64 {
 96         ffi::river_wlr_cursor_get_y(self.wlr_cursor)
 97     }
 98 
 99     pub unsafe fn allow_set_cursor(
100         &self,
101         seat_client: *mut ffi::wlr_seat_client,
102         serial: u32,
103     ) -> bool {
104         let focused_surface = ffi::river_wlr_tablet_v2_tablet_tool_get_focused_surface(self.wp_tool);
105         if focused_surface.is_null() {
106             log::debug!("client tried to set cursor without focus");
107             return false;
108         }
109 
110         let surface_resource = ffi::river_wlr_surface_get_resource(focused_surface);
111         if surface_resource.is_null() {
112             return false;
113         }
114 
115         let surface_client = ffi::wl_resource_get_client(surface_resource);
116         let seat_client_ptr = ffi::river_wlr_seat_client_get_client(seat_client);
117 
118         if surface_client != seat_client_ptr {
119             log::debug!("client tried to set cursor without focus");
120             return false;
121         }
122 
123         let proximity_serial = ffi::river_wlr_tablet_v2_tablet_tool_get_proximity_serial(self.wp_tool);
124         if serial != proximity_serial {
125             log::debug!("focused client tried to set cursor with incorrect serial");
126             return false;
127         }
128 
129         true
130     }
131 
132     pub unsafe fn attach(&mut self, tablet: *mut Tablet) {
133         let dev = (*(*tablet).device).wlr_device;
134         ffi::wlr_cursor_attach_input_device(self.wlr_cursor, dev);
135         ffi::wlr_cursor_map_input_to_output(self.wlr_cursor, dev, (*(*tablet).device).config.map_to_output);
136         ffi::wlr_cursor_map_input_to_region(self.wlr_cursor, dev, &mut (*(*tablet).device).config.map_to_rectangle as *mut _);
137     }
138 
139     pub unsafe fn detach(&mut self, tablet: *mut Tablet) {
140         let dev = (*(*tablet).device).wlr_device;
141         ffi::wlr_cursor_detach_input_device(self.wlr_cursor, dev);
142     }
143 
144     pub unsafe fn axis(&mut self, tablet: *mut Tablet, event: *mut ffi::wlr_tablet_tool_axis_event) {
145         self.attach(tablet);
146 
147         let updated = (*event).updated_axes;
148         let has_x = (updated & ffi::wlr_tablet_tool_axes_WLR_TABLET_TOOL_AXIS_X) != 0;
149         let has_y = (updated & ffi::wlr_tablet_tool_axes_WLR_TABLET_TOOL_AXIS_Y) != 0;
150 
151         let wlr_tool = ffi::river_wlr_tablet_v2_tablet_tool_get_wlr_tool(self.wp_tool);
152         let tool_type = (*wlr_tool).type_;
153 
154         if has_x || has_y {
155             match tool_type {
156                 ffi::wlr_tablet_tool_type_WLR_TABLET_TOOL_TYPE_PEN |
157                 ffi::wlr_tablet_tool_type_WLR_TABLET_TOOL_TYPE_ERASER |
158                 ffi::wlr_tablet_tool_type_WLR_TABLET_TOOL_TYPE_BRUSH |
159                 ffi::wlr_tablet_tool_type_WLR_TABLET_TOOL_TYPE_PENCIL |
160                 ffi::wlr_tablet_tool_type_WLR_TABLET_TOOL_TYPE_AIRBRUSH |
161                 ffi::wlr_tablet_tool_type_WLR_TABLET_TOOL_TYPE_TOTEM => {
162                     let warp_x = if has_x { (*event).x } else { f64::NAN };
163                     let warp_y = if has_y { (*event).y } else { f64::NAN };
164                     ffi::wlr_cursor_warp_absolute(self.wlr_cursor, (*(*tablet).device).wlr_device, warp_x, warp_y);
165                 }
166                 ffi::wlr_tablet_tool_type_WLR_TABLET_TOOL_TYPE_LENS |
167                 ffi::wlr_tablet_tool_type_WLR_TABLET_TOOL_TYPE_MOUSE => {
168                     ffi::wlr_cursor_move(self.wlr_cursor, (*(*tablet).device).wlr_device, (*event).dx, (*event).dy);
169                 }
170                 _ => {}
171             }
172 
173             match self.mode {
174                 TabletToolMode::Passthrough => {
175                     self.passthrough(tablet);
176                 }
177                 TabletToolMode::Down { lx, ly, sx, sy } => {
178                     let dx = self.cursor_x() - lx;
179                     let dy = self.cursor_y() - ly;
180                     ffi::wlr_tablet_v2_tablet_tool_notify_motion(self.wp_tool, sx + dx, sy + dy);
181                 }
182             }
183         }
184 
185         let has_dist = (updated & ffi::wlr_tablet_tool_axes_WLR_TABLET_TOOL_AXIS_DISTANCE) != 0;
186         if has_dist {
187             ffi::wlr_tablet_v2_tablet_tool_notify_distance(self.wp_tool, (*event).distance);
188         }
189 
190         let has_press = (updated & ffi::wlr_tablet_tool_axes_WLR_TABLET_TOOL_AXIS_PRESSURE) != 0;
191         if has_press {
192             ffi::wlr_tablet_v2_tablet_tool_notify_pressure(self.wp_tool, (*event).pressure);
193         }
194 
195         let has_tilt_x = (updated & ffi::wlr_tablet_tool_axes_WLR_TABLET_TOOL_AXIS_TILT_X) != 0;
196         let has_tilt_y = (updated & ffi::wlr_tablet_tool_axes_WLR_TABLET_TOOL_AXIS_TILT_Y) != 0;
197         if has_tilt_x || has_tilt_y {
198             if has_tilt_x {
199                 self.tilt_x = (*event).tilt_x;
200             }
201             if has_tilt_y {
202                 self.tilt_y = (*event).tilt_y;
203             }
204             ffi::wlr_tablet_v2_tablet_tool_notify_tilt(self.wp_tool, self.tilt_x, self.tilt_y);
205         }
206 
207         let has_rot = (updated & ffi::wlr_tablet_tool_axes_WLR_TABLET_TOOL_AXIS_ROTATION) != 0;
208         if has_rot {
209             ffi::wlr_tablet_v2_tablet_tool_notify_rotation(self.wp_tool, (*event).rotation);
210         }
211 
212         let has_slider = (updated & ffi::wlr_tablet_tool_axes_WLR_TABLET_TOOL_AXIS_SLIDER) != 0;
213         if has_slider {
214             ffi::wlr_tablet_v2_tablet_tool_notify_slider(self.wp_tool, (*event).slider);
215         }
216 
217         let has_wheel = (updated & ffi::wlr_tablet_tool_axes_WLR_TABLET_TOOL_AXIS_WHEEL) != 0;
218         if has_wheel {
219             ffi::wlr_tablet_v2_tablet_tool_notify_wheel(self.wp_tool, (*event).wheel_delta, 0);
220         }
221 
222         self.detach(tablet);
223     }
224 
225     pub unsafe fn proximity(
226         &mut self,
227         tablet: *mut Tablet,
228         event: *mut ffi::wlr_tablet_tool_proximity_event,
229     ) {
230         let state = (*event).state;
231         if state == ffi::wlr_tablet_tool_proximity_state_WLR_TABLET_TOOL_PROXIMITY_IN {
232             self.attach(tablet);
233 
234             ffi::wlr_cursor_warp_absolute(
235                 self.wlr_cursor,
236                 (*(*tablet).device).wlr_device,
237                 (*event).x,
238                 (*event).y,
239             );
240 
241             let xcursor_manager = (*(*(*tablet).device).seat).cursor.xcursor_manager;
242             ffi::wlr_cursor_set_xcursor(
243                 self.wlr_cursor,
244                 xcursor_manager,
245                 b"pencil\0".as_ptr() as *const _,
246             );
247 
248             self.passthrough(tablet);
249             self.detach(tablet);
250         } else {
251             ffi::wlr_tablet_v2_tablet_tool_notify_proximity_out(self.wp_tool);
252             ffi::wlr_cursor_unset_image(self.wlr_cursor);
253         }
254     }
255 
256     pub unsafe fn tip(&mut self, tablet: *mut Tablet, event: *mut ffi::wlr_tablet_tool_tip_event) {
257         let state = (*event).state;
258         let is_down = ffi::river_wlr_tablet_v2_tablet_tool_get_is_down(self.wp_tool);
259         if state == ffi::wlr_tablet_tool_tip_state_WLR_TABLET_TOOL_TIP_DOWN {
260             if is_down {
261                 return;
262             }
263             ffi::wlr_send_tablet_v2_tablet_tool_down(self.wp_tool);
264 
265             let server = (*(*(*tablet).device).seat).server;
266             let lx = self.cursor_x();
267             let ly = self.cursor_y();
268             if let Some(result) = (*server).scene.at(lx, ly) {
269                 if !result.surface.is_null() {
270                     self.mode = TabletToolMode::Down {
271                         lx,
272                         ly,
273                         sx: result.sx,
274                         sy: result.sy,
275                     };
276                 }
277             }
278         } else {
279             if !is_down {
280                 return;
281             }
282             ffi::wlr_send_tablet_v2_tablet_tool_up(self.wp_tool);
283             self.maybe_exit_down(tablet);
284         }
285     }
286 
287     pub unsafe fn button(
288         &mut self,
289         tablet: *mut Tablet,
290         event: *mut ffi::wlr_tablet_tool_button_event,
291     ) {
292         ffi::wlr_tablet_v2_tablet_tool_notify_button(
293             self.wp_tool,
294             (*event).button,
295             (*event).state,
296         );
297         self.maybe_exit_down(tablet);
298     }
299 
300     pub unsafe fn maybe_exit_down(&mut self, tablet: *mut Tablet) {
301         let is_down = ffi::river_wlr_tablet_v2_tablet_tool_get_is_down(self.wp_tool);
302         let num_buttons = ffi::river_wlr_tablet_v2_tablet_tool_get_num_buttons(self.wp_tool);
303         if !matches!(self.mode, TabletToolMode::Down { .. }) || is_down || num_buttons > 0 {
304             return;
305         }
306         self.mode = TabletToolMode::Passthrough;
307         self.passthrough(tablet);
308     }
309 
310     pub unsafe fn passthrough(&mut self, tablet: *mut Tablet) {
311         let server = (*(*(*tablet).device).seat).server;
312         let lx = self.cursor_x();
313         let ly = self.cursor_y();
314 
315         if let Some(result) = (*server).scene.at(lx, ly) {
316             if matches!(result.data, crate::scene_node_data::SceneNodeDataVal::LockSurface(_)) {
317                 assert!((*server).lock_manager.state != crate::lock_manager::LockState::Unlocked);
318             } else {
319                 assert!((*server).lock_manager.state != crate::lock_manager::LockState::Locked);
320             }
321 
322             if !result.surface.is_null() {
323                 ffi::wlr_send_tablet_v2_tablet_tool_proximity_in(self.wp_tool, (*tablet).wp_tablet, result.surface);
324                 ffi::wlr_tablet_v2_tablet_tool_notify_motion(self.wp_tool, result.sx, result.sy);
325                 return;
326             }
327         } else {
328             let xcursor_manager = (*(*(*tablet).device).seat).cursor.xcursor_manager;
329             ffi::wlr_cursor_set_xcursor(self.wlr_cursor, xcursor_manager, b"pencil\0".as_ptr() as *const _);
330         }
331 
332         ffi::wlr_tablet_v2_tablet_tool_notify_proximity_out(self.wp_tool);
333     }
334 }
335 
336 unsafe extern "C" fn handle_destroy(
337     listener: *mut ffi::wl_listener,
338     _data: *mut std::ffi::c_void,
339 ) {
340     let tool_ptr = crate::container_of!(listener, TabletTool, destroy_listener) as *mut TabletTool;
341     let tool = &mut *tool_ptr;
342 
343     ffi::wlr_cursor_destroy(tool.wlr_cursor);
344 
345     wl_listener_remove(&mut tool.destroy_listener);
346     wl_listener_remove(&mut tool.set_cursor_listener);
347 
348     let _boxed = Box::from_raw(tool_ptr);
349 }
350 
351 unsafe extern "C" fn handle_set_cursor(
352     listener: *mut ffi::wl_listener,
353     data: *mut std::ffi::c_void,
354 ) {
355     let tool = &mut *crate::container_of!(listener, TabletTool, set_cursor_listener);
356     let event = data as *mut ffi::wlr_tablet_v2_event_cursor;
357 
358     if tool.allow_set_cursor((*event).seat_client, (*event).serial) {
359         ffi::wlr_cursor_set_surface(
360             tool.wlr_cursor,
361             (*event).surface,
362             (*event).hotspot_x,
363             (*event).hotspot_y,
364         );
365     }
366 }