1a46c0ec8Sopenharmony_ci/* 2a46c0ec8Sopenharmony_ci * Copyright © 2014 Red Hat, Inc. 3a46c0ec8Sopenharmony_ci * Copyright © 2014 Lyude Paul 4a46c0ec8Sopenharmony_ci * 5a46c0ec8Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 6a46c0ec8Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 7a46c0ec8Sopenharmony_ci * to deal in the Software without restriction, including without limitation 8a46c0ec8Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 9a46c0ec8Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 10a46c0ec8Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 11a46c0ec8Sopenharmony_ci * 12a46c0ec8Sopenharmony_ci * The above copyright notice and this permission notice (including the next 13a46c0ec8Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the 14a46c0ec8Sopenharmony_ci * Software. 15a46c0ec8Sopenharmony_ci * 16a46c0ec8Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17a46c0ec8Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18a46c0ec8Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19a46c0ec8Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20a46c0ec8Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21a46c0ec8Sopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22a46c0ec8Sopenharmony_ci * DEALINGS IN THE SOFTWARE. 23a46c0ec8Sopenharmony_ci */ 24a46c0ec8Sopenharmony_ci#include "config.h" 25a46c0ec8Sopenharmony_ci#include "evdev-tablet.h" 26a46c0ec8Sopenharmony_ci#include "util-input-event.h" 27a46c0ec8Sopenharmony_ci 28a46c0ec8Sopenharmony_ci#include <assert.h> 29a46c0ec8Sopenharmony_ci#include <stdbool.h> 30a46c0ec8Sopenharmony_ci#include <string.h> 31a46c0ec8Sopenharmony_ci 32a46c0ec8Sopenharmony_ci#if HAVE_LIBWACOM 33a46c0ec8Sopenharmony_ci#include <libwacom/libwacom.h> 34a46c0ec8Sopenharmony_ci#endif 35a46c0ec8Sopenharmony_ci 36a46c0ec8Sopenharmony_cienum notify { 37a46c0ec8Sopenharmony_ci DONT_NOTIFY, 38a46c0ec8Sopenharmony_ci DO_NOTIFY, 39a46c0ec8Sopenharmony_ci}; 40a46c0ec8Sopenharmony_ci 41a46c0ec8Sopenharmony_ci/* The tablet sends events every ~2ms , 50ms should be plenty enough to 42a46c0ec8Sopenharmony_ci detect out-of-range. 43a46c0ec8Sopenharmony_ci This value is higher during test suite runs */ 44a46c0ec8Sopenharmony_cistatic int FORCED_PROXOUT_TIMEOUT = 50 * 1000; /* µs */ 45a46c0ec8Sopenharmony_ci 46a46c0ec8Sopenharmony_ci#define tablet_set_status(tablet_,s_) (tablet_)->status |= (s_) 47a46c0ec8Sopenharmony_ci#define tablet_unset_status(tablet_,s_) (tablet_)->status &= ~(s_) 48a46c0ec8Sopenharmony_ci#define tablet_has_status(tablet_,s_) (!!((tablet_)->status & (s_))) 49a46c0ec8Sopenharmony_ci 50a46c0ec8Sopenharmony_cistatic inline void 51a46c0ec8Sopenharmony_citablet_get_pressed_buttons(struct tablet_dispatch *tablet, 52a46c0ec8Sopenharmony_ci struct button_state *buttons) 53a46c0ec8Sopenharmony_ci{ 54a46c0ec8Sopenharmony_ci size_t i; 55a46c0ec8Sopenharmony_ci const struct button_state *state = &tablet->button_state, 56a46c0ec8Sopenharmony_ci *prev_state = &tablet->prev_button_state; 57a46c0ec8Sopenharmony_ci 58a46c0ec8Sopenharmony_ci for (i = 0; i < sizeof(buttons->bits); i++) 59a46c0ec8Sopenharmony_ci buttons->bits[i] = state->bits[i] & ~(prev_state->bits[i]); 60a46c0ec8Sopenharmony_ci} 61a46c0ec8Sopenharmony_ci 62a46c0ec8Sopenharmony_cistatic inline void 63a46c0ec8Sopenharmony_citablet_get_released_buttons(struct tablet_dispatch *tablet, 64a46c0ec8Sopenharmony_ci struct button_state *buttons) 65a46c0ec8Sopenharmony_ci{ 66a46c0ec8Sopenharmony_ci size_t i; 67a46c0ec8Sopenharmony_ci const struct button_state *state = &tablet->button_state, 68a46c0ec8Sopenharmony_ci *prev_state = &tablet->prev_button_state; 69a46c0ec8Sopenharmony_ci 70a46c0ec8Sopenharmony_ci for (i = 0; i < sizeof(buttons->bits); i++) 71a46c0ec8Sopenharmony_ci buttons->bits[i] = prev_state->bits[i] & 72a46c0ec8Sopenharmony_ci ~(state->bits[i]); 73a46c0ec8Sopenharmony_ci} 74a46c0ec8Sopenharmony_ci 75a46c0ec8Sopenharmony_ci/* Merge the previous state with the current one so all buttons look like 76a46c0ec8Sopenharmony_ci * they just got pressed in this frame */ 77a46c0ec8Sopenharmony_cistatic inline void 78a46c0ec8Sopenharmony_citablet_force_button_presses(struct tablet_dispatch *tablet) 79a46c0ec8Sopenharmony_ci{ 80a46c0ec8Sopenharmony_ci struct button_state *state = &tablet->button_state, 81a46c0ec8Sopenharmony_ci *prev_state = &tablet->prev_button_state; 82a46c0ec8Sopenharmony_ci size_t i; 83a46c0ec8Sopenharmony_ci 84a46c0ec8Sopenharmony_ci for (i = 0; i < sizeof(state->bits); i++) { 85a46c0ec8Sopenharmony_ci state->bits[i] = state->bits[i] | prev_state->bits[i]; 86a46c0ec8Sopenharmony_ci prev_state->bits[i] = 0; 87a46c0ec8Sopenharmony_ci } 88a46c0ec8Sopenharmony_ci} 89a46c0ec8Sopenharmony_ci 90a46c0ec8Sopenharmony_cistatic inline size_t 91a46c0ec8Sopenharmony_citablet_history_size(const struct tablet_dispatch *tablet) 92a46c0ec8Sopenharmony_ci{ 93a46c0ec8Sopenharmony_ci return tablet->history.size; 94a46c0ec8Sopenharmony_ci} 95a46c0ec8Sopenharmony_ci 96a46c0ec8Sopenharmony_cistatic inline void 97a46c0ec8Sopenharmony_citablet_history_reset(struct tablet_dispatch *tablet) 98a46c0ec8Sopenharmony_ci{ 99a46c0ec8Sopenharmony_ci tablet->history.count = 0; 100a46c0ec8Sopenharmony_ci} 101a46c0ec8Sopenharmony_ci 102a46c0ec8Sopenharmony_cistatic inline void 103a46c0ec8Sopenharmony_citablet_history_push(struct tablet_dispatch *tablet, 104a46c0ec8Sopenharmony_ci const struct tablet_axes *axes) 105a46c0ec8Sopenharmony_ci{ 106a46c0ec8Sopenharmony_ci unsigned int index = (tablet->history.index + 1) % 107a46c0ec8Sopenharmony_ci tablet_history_size(tablet); 108a46c0ec8Sopenharmony_ci 109a46c0ec8Sopenharmony_ci tablet->history.samples[index] = *axes; 110a46c0ec8Sopenharmony_ci tablet->history.index = index; 111a46c0ec8Sopenharmony_ci tablet->history.count = min(tablet->history.count + 1, 112a46c0ec8Sopenharmony_ci tablet_history_size(tablet)); 113a46c0ec8Sopenharmony_ci 114a46c0ec8Sopenharmony_ci if (tablet->history.count < tablet_history_size(tablet)) 115a46c0ec8Sopenharmony_ci tablet_history_push(tablet, axes); 116a46c0ec8Sopenharmony_ci} 117a46c0ec8Sopenharmony_ci 118a46c0ec8Sopenharmony_ci/** 119a46c0ec8Sopenharmony_ci * Return a previous axis state, where index of 0 means "most recent", 1 is 120a46c0ec8Sopenharmony_ci * "one before most recent", etc. 121a46c0ec8Sopenharmony_ci */ 122a46c0ec8Sopenharmony_cistatic inline const struct tablet_axes* 123a46c0ec8Sopenharmony_citablet_history_get(const struct tablet_dispatch *tablet, unsigned int index) 124a46c0ec8Sopenharmony_ci{ 125a46c0ec8Sopenharmony_ci size_t sz = tablet_history_size(tablet); 126a46c0ec8Sopenharmony_ci 127a46c0ec8Sopenharmony_ci assert(index < sz); 128a46c0ec8Sopenharmony_ci assert(index < tablet->history.count); 129a46c0ec8Sopenharmony_ci 130a46c0ec8Sopenharmony_ci index = (tablet->history.index + sz - index) % sz; 131a46c0ec8Sopenharmony_ci return &tablet->history.samples[index]; 132a46c0ec8Sopenharmony_ci} 133a46c0ec8Sopenharmony_ci 134a46c0ec8Sopenharmony_cistatic inline void 135a46c0ec8Sopenharmony_citablet_reset_changed_axes(struct tablet_dispatch *tablet) 136a46c0ec8Sopenharmony_ci{ 137a46c0ec8Sopenharmony_ci memset(tablet->changed_axes, 0, sizeof(tablet->changed_axes)); 138a46c0ec8Sopenharmony_ci} 139a46c0ec8Sopenharmony_ci 140a46c0ec8Sopenharmony_cistatic bool 141a46c0ec8Sopenharmony_citablet_device_has_axis(struct tablet_dispatch *tablet, 142a46c0ec8Sopenharmony_ci enum libinput_tablet_tool_axis axis) 143a46c0ec8Sopenharmony_ci{ 144a46c0ec8Sopenharmony_ci struct libevdev *evdev = tablet->device->evdev; 145a46c0ec8Sopenharmony_ci bool has_axis = false; 146a46c0ec8Sopenharmony_ci unsigned int code; 147a46c0ec8Sopenharmony_ci 148a46c0ec8Sopenharmony_ci if (axis == LIBINPUT_TABLET_TOOL_AXIS_ROTATION_Z) { 149a46c0ec8Sopenharmony_ci has_axis = (libevdev_has_event_code(evdev, 150a46c0ec8Sopenharmony_ci EV_KEY, 151a46c0ec8Sopenharmony_ci BTN_TOOL_MOUSE) && 152a46c0ec8Sopenharmony_ci libevdev_has_event_code(evdev, 153a46c0ec8Sopenharmony_ci EV_ABS, 154a46c0ec8Sopenharmony_ci ABS_TILT_X) && 155a46c0ec8Sopenharmony_ci libevdev_has_event_code(evdev, 156a46c0ec8Sopenharmony_ci EV_ABS, 157a46c0ec8Sopenharmony_ci ABS_TILT_Y)); 158a46c0ec8Sopenharmony_ci code = axis_to_evcode(axis); 159a46c0ec8Sopenharmony_ci has_axis |= libevdev_has_event_code(evdev, 160a46c0ec8Sopenharmony_ci EV_ABS, 161a46c0ec8Sopenharmony_ci code); 162a46c0ec8Sopenharmony_ci } else if (axis == LIBINPUT_TABLET_TOOL_AXIS_REL_WHEEL) { 163a46c0ec8Sopenharmony_ci has_axis = libevdev_has_event_code(evdev, 164a46c0ec8Sopenharmony_ci EV_REL, 165a46c0ec8Sopenharmony_ci REL_WHEEL); 166a46c0ec8Sopenharmony_ci } else { 167a46c0ec8Sopenharmony_ci code = axis_to_evcode(axis); 168a46c0ec8Sopenharmony_ci has_axis = libevdev_has_event_code(evdev, 169a46c0ec8Sopenharmony_ci EV_ABS, 170a46c0ec8Sopenharmony_ci code); 171a46c0ec8Sopenharmony_ci } 172a46c0ec8Sopenharmony_ci 173a46c0ec8Sopenharmony_ci return has_axis; 174a46c0ec8Sopenharmony_ci} 175a46c0ec8Sopenharmony_ci 176a46c0ec8Sopenharmony_cistatic inline bool 177a46c0ec8Sopenharmony_citablet_filter_axis_fuzz(const struct tablet_dispatch *tablet, 178a46c0ec8Sopenharmony_ci const struct evdev_device *device, 179a46c0ec8Sopenharmony_ci const struct input_event *e, 180a46c0ec8Sopenharmony_ci enum libinput_tablet_tool_axis axis) 181a46c0ec8Sopenharmony_ci{ 182a46c0ec8Sopenharmony_ci int delta, fuzz; 183a46c0ec8Sopenharmony_ci int current, previous; 184a46c0ec8Sopenharmony_ci 185a46c0ec8Sopenharmony_ci previous = tablet->prev_value[axis]; 186a46c0ec8Sopenharmony_ci current = e->value; 187a46c0ec8Sopenharmony_ci delta = previous - current; 188a46c0ec8Sopenharmony_ci 189a46c0ec8Sopenharmony_ci fuzz = libevdev_get_abs_fuzz(device->evdev, e->code); 190a46c0ec8Sopenharmony_ci 191a46c0ec8Sopenharmony_ci /* ABS_DISTANCE doesn't have have fuzz set and causes continuous 192a46c0ec8Sopenharmony_ci * updates for the cursor/lens tools. Add a minimum fuzz of 2, same 193a46c0ec8Sopenharmony_ci * as the xf86-input-wacom driver 194a46c0ec8Sopenharmony_ci */ 195a46c0ec8Sopenharmony_ci switch (e->code) { 196a46c0ec8Sopenharmony_ci case ABS_DISTANCE: 197a46c0ec8Sopenharmony_ci fuzz = max(2, fuzz); 198a46c0ec8Sopenharmony_ci break; 199a46c0ec8Sopenharmony_ci default: 200a46c0ec8Sopenharmony_ci break; 201a46c0ec8Sopenharmony_ci } 202a46c0ec8Sopenharmony_ci 203a46c0ec8Sopenharmony_ci return abs(delta) <= fuzz; 204a46c0ec8Sopenharmony_ci} 205a46c0ec8Sopenharmony_ci 206a46c0ec8Sopenharmony_cistatic void 207a46c0ec8Sopenharmony_citablet_process_absolute(struct tablet_dispatch *tablet, 208a46c0ec8Sopenharmony_ci struct evdev_device *device, 209a46c0ec8Sopenharmony_ci struct input_event *e, 210a46c0ec8Sopenharmony_ci uint64_t time) 211a46c0ec8Sopenharmony_ci{ 212a46c0ec8Sopenharmony_ci enum libinput_tablet_tool_axis axis; 213a46c0ec8Sopenharmony_ci 214a46c0ec8Sopenharmony_ci switch (e->code) { 215a46c0ec8Sopenharmony_ci case ABS_X: 216a46c0ec8Sopenharmony_ci case ABS_Y: 217a46c0ec8Sopenharmony_ci case ABS_Z: 218a46c0ec8Sopenharmony_ci case ABS_PRESSURE: 219a46c0ec8Sopenharmony_ci case ABS_TILT_X: 220a46c0ec8Sopenharmony_ci case ABS_TILT_Y: 221a46c0ec8Sopenharmony_ci case ABS_DISTANCE: 222a46c0ec8Sopenharmony_ci case ABS_WHEEL: 223a46c0ec8Sopenharmony_ci axis = evcode_to_axis(e->code); 224a46c0ec8Sopenharmony_ci if (axis == LIBINPUT_TABLET_TOOL_AXIS_NONE) { 225a46c0ec8Sopenharmony_ci evdev_log_bug_libinput(device, 226a46c0ec8Sopenharmony_ci "Invalid ABS event code %#x\n", 227a46c0ec8Sopenharmony_ci e->code); 228a46c0ec8Sopenharmony_ci break; 229a46c0ec8Sopenharmony_ci } 230a46c0ec8Sopenharmony_ci 231a46c0ec8Sopenharmony_ci tablet->prev_value[axis] = tablet->current_value[axis]; 232a46c0ec8Sopenharmony_ci if (tablet_filter_axis_fuzz(tablet, device, e, axis)) 233a46c0ec8Sopenharmony_ci break; 234a46c0ec8Sopenharmony_ci 235a46c0ec8Sopenharmony_ci tablet->current_value[axis] = e->value; 236a46c0ec8Sopenharmony_ci set_bit(tablet->changed_axes, axis); 237a46c0ec8Sopenharmony_ci tablet_set_status(tablet, TABLET_AXES_UPDATED); 238a46c0ec8Sopenharmony_ci break; 239a46c0ec8Sopenharmony_ci /* tool_id is the identifier for the tool we can use in libwacom 240a46c0ec8Sopenharmony_ci * to identify it (if we have one anyway) */ 241a46c0ec8Sopenharmony_ci case ABS_MISC: 242a46c0ec8Sopenharmony_ci tablet->current_tool.id = e->value; 243a46c0ec8Sopenharmony_ci break; 244a46c0ec8Sopenharmony_ci /* Intuos 3 strip data. Should only happen on the Pad device, not on 245a46c0ec8Sopenharmony_ci the Pen device. */ 246a46c0ec8Sopenharmony_ci case ABS_RX: 247a46c0ec8Sopenharmony_ci case ABS_RY: 248a46c0ec8Sopenharmony_ci /* Only on the 4D mouse (Intuos2), obsolete */ 249a46c0ec8Sopenharmony_ci case ABS_RZ: 250a46c0ec8Sopenharmony_ci /* Only on the 4D mouse (Intuos2), obsolete. 251a46c0ec8Sopenharmony_ci The 24HD sends ABS_THROTTLE on the Pad device for the second 252a46c0ec8Sopenharmony_ci wheel but we shouldn't get here on kernel >= 3.17. 253a46c0ec8Sopenharmony_ci */ 254a46c0ec8Sopenharmony_ci case ABS_THROTTLE: 255a46c0ec8Sopenharmony_ci default: 256a46c0ec8Sopenharmony_ci evdev_log_info(device, 257a46c0ec8Sopenharmony_ci "Unhandled ABS event code %#x\n", 258a46c0ec8Sopenharmony_ci e->code); 259a46c0ec8Sopenharmony_ci break; 260a46c0ec8Sopenharmony_ci } 261a46c0ec8Sopenharmony_ci} 262a46c0ec8Sopenharmony_ci 263a46c0ec8Sopenharmony_cistatic void 264a46c0ec8Sopenharmony_citablet_apply_rotation(struct evdev_device *device) 265a46c0ec8Sopenharmony_ci{ 266a46c0ec8Sopenharmony_ci struct tablet_dispatch *tablet = tablet_dispatch(device->dispatch); 267a46c0ec8Sopenharmony_ci 268a46c0ec8Sopenharmony_ci if (tablet->rotation.rotate == tablet->rotation.want_rotate) 269a46c0ec8Sopenharmony_ci return; 270a46c0ec8Sopenharmony_ci 271a46c0ec8Sopenharmony_ci if (!tablet_has_status(tablet, TABLET_TOOL_OUT_OF_PROXIMITY)) 272a46c0ec8Sopenharmony_ci return; 273a46c0ec8Sopenharmony_ci 274a46c0ec8Sopenharmony_ci tablet->rotation.rotate = tablet->rotation.want_rotate; 275a46c0ec8Sopenharmony_ci 276a46c0ec8Sopenharmony_ci evdev_log_debug(device, 277a46c0ec8Sopenharmony_ci "tablet-rotation: rotation is %s\n", 278a46c0ec8Sopenharmony_ci tablet->rotation.rotate ? "on" : "off"); 279a46c0ec8Sopenharmony_ci} 280a46c0ec8Sopenharmony_ci 281a46c0ec8Sopenharmony_cistatic void 282a46c0ec8Sopenharmony_citablet_change_rotation(struct evdev_device *device, enum notify notify) 283a46c0ec8Sopenharmony_ci{ 284a46c0ec8Sopenharmony_ci struct tablet_dispatch *tablet = tablet_dispatch(device->dispatch); 285a46c0ec8Sopenharmony_ci struct evdev_device *touch_device = tablet->touch_device; 286a46c0ec8Sopenharmony_ci struct evdev_dispatch *dispatch; 287a46c0ec8Sopenharmony_ci bool tablet_is_left, touchpad_is_left; 288a46c0ec8Sopenharmony_ci 289a46c0ec8Sopenharmony_ci tablet_is_left = tablet->device->left_handed.enabled; 290a46c0ec8Sopenharmony_ci touchpad_is_left = tablet->rotation.touch_device_left_handed_state; 291a46c0ec8Sopenharmony_ci 292a46c0ec8Sopenharmony_ci tablet->rotation.want_rotate = tablet_is_left || touchpad_is_left; 293a46c0ec8Sopenharmony_ci tablet_apply_rotation(device); 294a46c0ec8Sopenharmony_ci 295a46c0ec8Sopenharmony_ci if (notify == DO_NOTIFY && touch_device) { 296a46c0ec8Sopenharmony_ci bool enable = device->left_handed.want_enabled; 297a46c0ec8Sopenharmony_ci 298a46c0ec8Sopenharmony_ci dispatch = touch_device->dispatch; 299a46c0ec8Sopenharmony_ci if (dispatch->interface->left_handed_toggle) 300a46c0ec8Sopenharmony_ci dispatch->interface->left_handed_toggle(dispatch, 301a46c0ec8Sopenharmony_ci touch_device, 302a46c0ec8Sopenharmony_ci enable); 303a46c0ec8Sopenharmony_ci } 304a46c0ec8Sopenharmony_ci} 305a46c0ec8Sopenharmony_ci 306a46c0ec8Sopenharmony_cistatic void 307a46c0ec8Sopenharmony_citablet_change_to_left_handed(struct evdev_device *device) 308a46c0ec8Sopenharmony_ci{ 309a46c0ec8Sopenharmony_ci if (device->left_handed.enabled == device->left_handed.want_enabled) 310a46c0ec8Sopenharmony_ci return; 311a46c0ec8Sopenharmony_ci 312a46c0ec8Sopenharmony_ci device->left_handed.enabled = device->left_handed.want_enabled; 313a46c0ec8Sopenharmony_ci 314a46c0ec8Sopenharmony_ci tablet_change_rotation(device, DO_NOTIFY); 315a46c0ec8Sopenharmony_ci} 316a46c0ec8Sopenharmony_ci 317a46c0ec8Sopenharmony_cistatic void 318a46c0ec8Sopenharmony_citablet_update_tool(struct tablet_dispatch *tablet, 319a46c0ec8Sopenharmony_ci struct evdev_device *device, 320a46c0ec8Sopenharmony_ci enum libinput_tablet_tool_type tool, 321a46c0ec8Sopenharmony_ci bool enabled) 322a46c0ec8Sopenharmony_ci{ 323a46c0ec8Sopenharmony_ci assert(tool != LIBINPUT_TOOL_NONE); 324a46c0ec8Sopenharmony_ci 325a46c0ec8Sopenharmony_ci if (enabled) { 326a46c0ec8Sopenharmony_ci tablet->current_tool.type = tool; 327a46c0ec8Sopenharmony_ci tablet_set_status(tablet, TABLET_TOOL_ENTERING_PROXIMITY); 328a46c0ec8Sopenharmony_ci tablet_unset_status(tablet, TABLET_TOOL_OUT_OF_PROXIMITY); 329a46c0ec8Sopenharmony_ci } 330a46c0ec8Sopenharmony_ci else if (!tablet_has_status(tablet, TABLET_TOOL_OUT_OF_PROXIMITY)) { 331a46c0ec8Sopenharmony_ci tablet_set_status(tablet, TABLET_TOOL_LEAVING_PROXIMITY); 332a46c0ec8Sopenharmony_ci } 333a46c0ec8Sopenharmony_ci} 334a46c0ec8Sopenharmony_ci 335a46c0ec8Sopenharmony_cistatic inline double 336a46c0ec8Sopenharmony_cinormalize_slider(const struct input_absinfo *absinfo) 337a46c0ec8Sopenharmony_ci{ 338a46c0ec8Sopenharmony_ci double value = (absinfo->value - absinfo->minimum) / absinfo_range(absinfo); 339a46c0ec8Sopenharmony_ci 340a46c0ec8Sopenharmony_ci return value * 2 - 1; 341a46c0ec8Sopenharmony_ci} 342a46c0ec8Sopenharmony_ci 343a46c0ec8Sopenharmony_cistatic inline double 344a46c0ec8Sopenharmony_cinormalize_distance(const struct input_absinfo *absinfo) 345a46c0ec8Sopenharmony_ci{ 346a46c0ec8Sopenharmony_ci double value = (absinfo->value - absinfo->minimum) / absinfo_range(absinfo); 347a46c0ec8Sopenharmony_ci 348a46c0ec8Sopenharmony_ci return value; 349a46c0ec8Sopenharmony_ci} 350a46c0ec8Sopenharmony_ci 351a46c0ec8Sopenharmony_cistatic inline double 352a46c0ec8Sopenharmony_cinormalize_pressure(const struct input_absinfo *absinfo, 353a46c0ec8Sopenharmony_ci struct libinput_tablet_tool *tool) 354a46c0ec8Sopenharmony_ci{ 355a46c0ec8Sopenharmony_ci /** 356a46c0ec8Sopenharmony_ci * Note: the upper threshold takes the offset into account so that 357a46c0ec8Sopenharmony_ci * |- 4% -| 358a46c0ec8Sopenharmony_ci * min |------X------X-------------------------| max 359a46c0ec8Sopenharmony_ci * | | 360a46c0ec8Sopenharmony_ci * | + upper threshold / tip trigger 361a46c0ec8Sopenharmony_ci * +- offset and lower threshold 362a46c0ec8Sopenharmony_ci * 363a46c0ec8Sopenharmony_ci * The axis is scaled into the range [lower, max] so that the lower 364a46c0ec8Sopenharmony_ci * threshold is 0 pressure. 365a46c0ec8Sopenharmony_ci */ 366a46c0ec8Sopenharmony_ci int base = tool->pressure.threshold.lower; 367a46c0ec8Sopenharmony_ci double range = absinfo->maximum - base + 1; 368a46c0ec8Sopenharmony_ci double value = (absinfo->value - base) / range; 369a46c0ec8Sopenharmony_ci 370a46c0ec8Sopenharmony_ci return max(0.0, value); 371a46c0ec8Sopenharmony_ci} 372a46c0ec8Sopenharmony_ci 373a46c0ec8Sopenharmony_cistatic inline double 374a46c0ec8Sopenharmony_ciadjust_tilt(const struct input_absinfo *absinfo) 375a46c0ec8Sopenharmony_ci{ 376a46c0ec8Sopenharmony_ci double value = (absinfo->value - absinfo->minimum) / absinfo_range(absinfo); 377a46c0ec8Sopenharmony_ci const int WACOM_MAX_DEGREES = 64; 378a46c0ec8Sopenharmony_ci 379a46c0ec8Sopenharmony_ci /* If resolution is nonzero, it's in units/radian. But require 380a46c0ec8Sopenharmony_ci * a min/max less/greater than zero so we can assume 0 is the 381a46c0ec8Sopenharmony_ci * center */ 382a46c0ec8Sopenharmony_ci if (absinfo->resolution != 0 && 383a46c0ec8Sopenharmony_ci absinfo->maximum > 0 && 384a46c0ec8Sopenharmony_ci absinfo->minimum < 0) { 385a46c0ec8Sopenharmony_ci value = 180.0/M_PI * absinfo->value/absinfo->resolution; 386a46c0ec8Sopenharmony_ci } else { 387a46c0ec8Sopenharmony_ci /* Wacom supports physical [-64, 64] degrees, so map to that by 388a46c0ec8Sopenharmony_ci * default. If other tablets have a different physical range or 389a46c0ec8Sopenharmony_ci * nonzero physical offsets, they need extra treatment 390a46c0ec8Sopenharmony_ci * here. 391a46c0ec8Sopenharmony_ci */ 392a46c0ec8Sopenharmony_ci /* Map to the (-1, 1) range */ 393a46c0ec8Sopenharmony_ci value = (value * 2) - 1; 394a46c0ec8Sopenharmony_ci value *= WACOM_MAX_DEGREES; 395a46c0ec8Sopenharmony_ci } 396a46c0ec8Sopenharmony_ci 397a46c0ec8Sopenharmony_ci return value; 398a46c0ec8Sopenharmony_ci} 399a46c0ec8Sopenharmony_ci 400a46c0ec8Sopenharmony_cistatic inline int32_t 401a46c0ec8Sopenharmony_ciinvert_axis(const struct input_absinfo *absinfo) 402a46c0ec8Sopenharmony_ci{ 403a46c0ec8Sopenharmony_ci return absinfo->maximum - (absinfo->value - absinfo->minimum); 404a46c0ec8Sopenharmony_ci} 405a46c0ec8Sopenharmony_ci 406a46c0ec8Sopenharmony_cistatic void 407a46c0ec8Sopenharmony_ciconvert_tilt_to_rotation(struct tablet_dispatch *tablet) 408a46c0ec8Sopenharmony_ci{ 409a46c0ec8Sopenharmony_ci const int offset = 5; 410a46c0ec8Sopenharmony_ci double x, y; 411a46c0ec8Sopenharmony_ci double angle = 0.0; 412a46c0ec8Sopenharmony_ci 413a46c0ec8Sopenharmony_ci /* Wacom Intuos 4, 5, Pro mouse calculates rotation from the x/y tilt 414a46c0ec8Sopenharmony_ci values. The device has a 175 degree CCW hardware offset but since we use 415a46c0ec8Sopenharmony_ci atan2 the effective offset is just 5 degrees. 416a46c0ec8Sopenharmony_ci */ 417a46c0ec8Sopenharmony_ci x = tablet->axes.tilt.x; 418a46c0ec8Sopenharmony_ci y = tablet->axes.tilt.y; 419a46c0ec8Sopenharmony_ci 420a46c0ec8Sopenharmony_ci /* atan2 is CCW, we want CW -> negate x */ 421a46c0ec8Sopenharmony_ci if (x || y) 422a46c0ec8Sopenharmony_ci angle = ((180.0 * atan2(-x, y)) / M_PI); 423a46c0ec8Sopenharmony_ci 424a46c0ec8Sopenharmony_ci angle = fmod(360 + angle - offset, 360); 425a46c0ec8Sopenharmony_ci 426a46c0ec8Sopenharmony_ci tablet->axes.rotation = angle; 427a46c0ec8Sopenharmony_ci set_bit(tablet->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_ROTATION_Z); 428a46c0ec8Sopenharmony_ci} 429a46c0ec8Sopenharmony_ci 430a46c0ec8Sopenharmony_cistatic double 431a46c0ec8Sopenharmony_ciconvert_to_degrees(const struct input_absinfo *absinfo, double offset) 432a46c0ec8Sopenharmony_ci{ 433a46c0ec8Sopenharmony_ci /* range is [0, 360[, i.e. range + 1 */ 434a46c0ec8Sopenharmony_ci double value = (absinfo->value - absinfo->minimum) / absinfo_range(absinfo); 435a46c0ec8Sopenharmony_ci 436a46c0ec8Sopenharmony_ci return fmod(value * 360.0 + offset, 360.0); 437a46c0ec8Sopenharmony_ci} 438a46c0ec8Sopenharmony_ci 439a46c0ec8Sopenharmony_cistatic inline double 440a46c0ec8Sopenharmony_cinormalize_wheel(struct tablet_dispatch *tablet, 441a46c0ec8Sopenharmony_ci int value) 442a46c0ec8Sopenharmony_ci{ 443a46c0ec8Sopenharmony_ci struct evdev_device *device = tablet->device; 444a46c0ec8Sopenharmony_ci 445a46c0ec8Sopenharmony_ci return value * device->scroll.wheel_click_angle.x; 446a46c0ec8Sopenharmony_ci} 447a46c0ec8Sopenharmony_ci 448a46c0ec8Sopenharmony_cistatic inline void 449a46c0ec8Sopenharmony_citablet_update_xy(struct tablet_dispatch *tablet, 450a46c0ec8Sopenharmony_ci struct evdev_device *device) 451a46c0ec8Sopenharmony_ci{ 452a46c0ec8Sopenharmony_ci const struct input_absinfo *absinfo; 453a46c0ec8Sopenharmony_ci int value; 454a46c0ec8Sopenharmony_ci 455a46c0ec8Sopenharmony_ci if (!libevdev_has_event_code(device->evdev, EV_ABS, ABS_X) || 456a46c0ec8Sopenharmony_ci !libevdev_has_event_code(device->evdev, EV_ABS, ABS_Y)) 457a46c0ec8Sopenharmony_ci return; 458a46c0ec8Sopenharmony_ci 459a46c0ec8Sopenharmony_ci if (bit_is_set(tablet->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_X) || 460a46c0ec8Sopenharmony_ci bit_is_set(tablet->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_Y)) { 461a46c0ec8Sopenharmony_ci absinfo = libevdev_get_abs_info(device->evdev, ABS_X); 462a46c0ec8Sopenharmony_ci 463a46c0ec8Sopenharmony_ci if (tablet->rotation.rotate) 464a46c0ec8Sopenharmony_ci value = invert_axis(absinfo); 465a46c0ec8Sopenharmony_ci else 466a46c0ec8Sopenharmony_ci value = absinfo->value; 467a46c0ec8Sopenharmony_ci 468a46c0ec8Sopenharmony_ci tablet->axes.point.x = value; 469a46c0ec8Sopenharmony_ci 470a46c0ec8Sopenharmony_ci absinfo = libevdev_get_abs_info(device->evdev, ABS_Y); 471a46c0ec8Sopenharmony_ci 472a46c0ec8Sopenharmony_ci if (tablet->rotation.rotate) 473a46c0ec8Sopenharmony_ci value = invert_axis(absinfo); 474a46c0ec8Sopenharmony_ci else 475a46c0ec8Sopenharmony_ci value = absinfo->value; 476a46c0ec8Sopenharmony_ci 477a46c0ec8Sopenharmony_ci tablet->axes.point.y = value; 478a46c0ec8Sopenharmony_ci 479a46c0ec8Sopenharmony_ci evdev_transform_absolute(device, &tablet->axes.point); 480a46c0ec8Sopenharmony_ci } 481a46c0ec8Sopenharmony_ci} 482a46c0ec8Sopenharmony_ci 483a46c0ec8Sopenharmony_cistatic inline struct normalized_coords 484a46c0ec8Sopenharmony_citablet_tool_process_delta(struct tablet_dispatch *tablet, 485a46c0ec8Sopenharmony_ci struct libinput_tablet_tool *tool, 486a46c0ec8Sopenharmony_ci const struct evdev_device *device, 487a46c0ec8Sopenharmony_ci struct tablet_axes *axes, 488a46c0ec8Sopenharmony_ci uint64_t time) 489a46c0ec8Sopenharmony_ci{ 490a46c0ec8Sopenharmony_ci const struct normalized_coords zero = { 0.0, 0.0 }; 491a46c0ec8Sopenharmony_ci struct device_coords delta = { 0, 0 }; 492a46c0ec8Sopenharmony_ci struct device_float_coords accel; 493a46c0ec8Sopenharmony_ci 494a46c0ec8Sopenharmony_ci /* When tool contact changes, we probably got a cursor jump. Don't 495a46c0ec8Sopenharmony_ci try to calculate a delta for that event */ 496a46c0ec8Sopenharmony_ci if (!tablet_has_status(tablet, 497a46c0ec8Sopenharmony_ci TABLET_TOOL_ENTERING_PROXIMITY) && 498a46c0ec8Sopenharmony_ci !tablet_has_status(tablet, TABLET_TOOL_ENTERING_CONTACT) && 499a46c0ec8Sopenharmony_ci !tablet_has_status(tablet, TABLET_TOOL_LEAVING_CONTACT) && 500a46c0ec8Sopenharmony_ci (bit_is_set(tablet->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_X) || 501a46c0ec8Sopenharmony_ci bit_is_set(tablet->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_Y))) { 502a46c0ec8Sopenharmony_ci delta.x = axes->point.x - tablet->last_smooth_point.x; 503a46c0ec8Sopenharmony_ci delta.y = axes->point.y - tablet->last_smooth_point.y; 504a46c0ec8Sopenharmony_ci } 505a46c0ec8Sopenharmony_ci 506a46c0ec8Sopenharmony_ci if (axes->point.x != tablet->last_smooth_point.x) 507a46c0ec8Sopenharmony_ci set_bit(tablet->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_X); 508a46c0ec8Sopenharmony_ci if (axes->point.y != tablet->last_smooth_point.y) 509a46c0ec8Sopenharmony_ci set_bit(tablet->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_Y); 510a46c0ec8Sopenharmony_ci 511a46c0ec8Sopenharmony_ci tablet->last_smooth_point = axes->point; 512a46c0ec8Sopenharmony_ci 513a46c0ec8Sopenharmony_ci accel.x = 1.0 * delta.x; 514a46c0ec8Sopenharmony_ci accel.y = 1.0 * delta.y; 515a46c0ec8Sopenharmony_ci 516a46c0ec8Sopenharmony_ci if (device_float_is_zero(accel)) 517a46c0ec8Sopenharmony_ci return zero; 518a46c0ec8Sopenharmony_ci 519a46c0ec8Sopenharmony_ci return filter_dispatch(device->pointer.filter, 520a46c0ec8Sopenharmony_ci &accel, 521a46c0ec8Sopenharmony_ci tool, 522a46c0ec8Sopenharmony_ci time); 523a46c0ec8Sopenharmony_ci} 524a46c0ec8Sopenharmony_ci 525a46c0ec8Sopenharmony_cistatic inline void 526a46c0ec8Sopenharmony_citablet_update_pressure(struct tablet_dispatch *tablet, 527a46c0ec8Sopenharmony_ci struct evdev_device *device, 528a46c0ec8Sopenharmony_ci struct libinput_tablet_tool *tool) 529a46c0ec8Sopenharmony_ci{ 530a46c0ec8Sopenharmony_ci const struct input_absinfo *absinfo; 531a46c0ec8Sopenharmony_ci 532a46c0ec8Sopenharmony_ci if (!libevdev_has_event_code(device->evdev, EV_ABS, ABS_PRESSURE)) 533a46c0ec8Sopenharmony_ci return; 534a46c0ec8Sopenharmony_ci 535a46c0ec8Sopenharmony_ci if (bit_is_set(tablet->changed_axes, 536a46c0ec8Sopenharmony_ci LIBINPUT_TABLET_TOOL_AXIS_PRESSURE)) { 537a46c0ec8Sopenharmony_ci absinfo = libevdev_get_abs_info(device->evdev, ABS_PRESSURE); 538a46c0ec8Sopenharmony_ci tablet->axes.pressure = normalize_pressure(absinfo, tool); 539a46c0ec8Sopenharmony_ci } 540a46c0ec8Sopenharmony_ci} 541a46c0ec8Sopenharmony_ci 542a46c0ec8Sopenharmony_cistatic inline void 543a46c0ec8Sopenharmony_citablet_update_distance(struct tablet_dispatch *tablet, 544a46c0ec8Sopenharmony_ci struct evdev_device *device) 545a46c0ec8Sopenharmony_ci{ 546a46c0ec8Sopenharmony_ci const struct input_absinfo *absinfo; 547a46c0ec8Sopenharmony_ci 548a46c0ec8Sopenharmony_ci if (!libevdev_has_event_code(device->evdev, EV_ABS, ABS_DISTANCE)) 549a46c0ec8Sopenharmony_ci return; 550a46c0ec8Sopenharmony_ci 551a46c0ec8Sopenharmony_ci if (bit_is_set(tablet->changed_axes, 552a46c0ec8Sopenharmony_ci LIBINPUT_TABLET_TOOL_AXIS_DISTANCE)) { 553a46c0ec8Sopenharmony_ci absinfo = libevdev_get_abs_info(device->evdev, ABS_DISTANCE); 554a46c0ec8Sopenharmony_ci tablet->axes.distance = normalize_distance(absinfo); 555a46c0ec8Sopenharmony_ci } 556a46c0ec8Sopenharmony_ci} 557a46c0ec8Sopenharmony_ci 558a46c0ec8Sopenharmony_cistatic inline void 559a46c0ec8Sopenharmony_citablet_update_slider(struct tablet_dispatch *tablet, 560a46c0ec8Sopenharmony_ci struct evdev_device *device) 561a46c0ec8Sopenharmony_ci{ 562a46c0ec8Sopenharmony_ci const struct input_absinfo *absinfo; 563a46c0ec8Sopenharmony_ci 564a46c0ec8Sopenharmony_ci if (!libevdev_has_event_code(device->evdev, EV_ABS, ABS_WHEEL)) 565a46c0ec8Sopenharmony_ci return; 566a46c0ec8Sopenharmony_ci 567a46c0ec8Sopenharmony_ci if (bit_is_set(tablet->changed_axes, 568a46c0ec8Sopenharmony_ci LIBINPUT_TABLET_TOOL_AXIS_SLIDER)) { 569a46c0ec8Sopenharmony_ci absinfo = libevdev_get_abs_info(device->evdev, ABS_WHEEL); 570a46c0ec8Sopenharmony_ci tablet->axes.slider = normalize_slider(absinfo); 571a46c0ec8Sopenharmony_ci } 572a46c0ec8Sopenharmony_ci} 573a46c0ec8Sopenharmony_ci 574a46c0ec8Sopenharmony_cistatic inline void 575a46c0ec8Sopenharmony_citablet_update_tilt(struct tablet_dispatch *tablet, 576a46c0ec8Sopenharmony_ci struct evdev_device *device) 577a46c0ec8Sopenharmony_ci{ 578a46c0ec8Sopenharmony_ci const struct input_absinfo *absinfo; 579a46c0ec8Sopenharmony_ci 580a46c0ec8Sopenharmony_ci if (!libevdev_has_event_code(device->evdev, EV_ABS, ABS_TILT_X) || 581a46c0ec8Sopenharmony_ci !libevdev_has_event_code(device->evdev, EV_ABS, ABS_TILT_Y)) 582a46c0ec8Sopenharmony_ci return; 583a46c0ec8Sopenharmony_ci 584a46c0ec8Sopenharmony_ci /* mouse rotation resets tilt to 0 so always fetch both axes if 585a46c0ec8Sopenharmony_ci * either has changed */ 586a46c0ec8Sopenharmony_ci if (bit_is_set(tablet->changed_axes, 587a46c0ec8Sopenharmony_ci LIBINPUT_TABLET_TOOL_AXIS_TILT_X) || 588a46c0ec8Sopenharmony_ci bit_is_set(tablet->changed_axes, 589a46c0ec8Sopenharmony_ci LIBINPUT_TABLET_TOOL_AXIS_TILT_Y)) { 590a46c0ec8Sopenharmony_ci 591a46c0ec8Sopenharmony_ci absinfo = libevdev_get_abs_info(device->evdev, ABS_TILT_X); 592a46c0ec8Sopenharmony_ci tablet->axes.tilt.x = adjust_tilt(absinfo); 593a46c0ec8Sopenharmony_ci 594a46c0ec8Sopenharmony_ci absinfo = libevdev_get_abs_info(device->evdev, ABS_TILT_Y); 595a46c0ec8Sopenharmony_ci tablet->axes.tilt.y = adjust_tilt(absinfo); 596a46c0ec8Sopenharmony_ci 597a46c0ec8Sopenharmony_ci if (device->left_handed.enabled) { 598a46c0ec8Sopenharmony_ci tablet->axes.tilt.x *= -1; 599a46c0ec8Sopenharmony_ci tablet->axes.tilt.y *= -1; 600a46c0ec8Sopenharmony_ci } 601a46c0ec8Sopenharmony_ci } 602a46c0ec8Sopenharmony_ci} 603a46c0ec8Sopenharmony_ci 604a46c0ec8Sopenharmony_cistatic inline void 605a46c0ec8Sopenharmony_citablet_update_artpen_rotation(struct tablet_dispatch *tablet, 606a46c0ec8Sopenharmony_ci struct evdev_device *device) 607a46c0ec8Sopenharmony_ci{ 608a46c0ec8Sopenharmony_ci const struct input_absinfo *absinfo; 609a46c0ec8Sopenharmony_ci 610a46c0ec8Sopenharmony_ci if (!libevdev_has_event_code(device->evdev, EV_ABS, ABS_Z)) 611a46c0ec8Sopenharmony_ci return; 612a46c0ec8Sopenharmony_ci 613a46c0ec8Sopenharmony_ci if (bit_is_set(tablet->changed_axes, 614a46c0ec8Sopenharmony_ci LIBINPUT_TABLET_TOOL_AXIS_ROTATION_Z)) { 615a46c0ec8Sopenharmony_ci absinfo = libevdev_get_abs_info(device->evdev, 616a46c0ec8Sopenharmony_ci ABS_Z); 617a46c0ec8Sopenharmony_ci /* artpen has 0 with buttons pointing east */ 618a46c0ec8Sopenharmony_ci tablet->axes.rotation = convert_to_degrees(absinfo, 90); 619a46c0ec8Sopenharmony_ci } 620a46c0ec8Sopenharmony_ci} 621a46c0ec8Sopenharmony_ci 622a46c0ec8Sopenharmony_cistatic inline void 623a46c0ec8Sopenharmony_citablet_update_mouse_rotation(struct tablet_dispatch *tablet, 624a46c0ec8Sopenharmony_ci struct evdev_device *device) 625a46c0ec8Sopenharmony_ci{ 626a46c0ec8Sopenharmony_ci if (bit_is_set(tablet->changed_axes, 627a46c0ec8Sopenharmony_ci LIBINPUT_TABLET_TOOL_AXIS_TILT_X) || 628a46c0ec8Sopenharmony_ci bit_is_set(tablet->changed_axes, 629a46c0ec8Sopenharmony_ci LIBINPUT_TABLET_TOOL_AXIS_TILT_Y)) { 630a46c0ec8Sopenharmony_ci convert_tilt_to_rotation(tablet); 631a46c0ec8Sopenharmony_ci } 632a46c0ec8Sopenharmony_ci} 633a46c0ec8Sopenharmony_ci 634a46c0ec8Sopenharmony_cistatic inline void 635a46c0ec8Sopenharmony_citablet_update_rotation(struct tablet_dispatch *tablet, 636a46c0ec8Sopenharmony_ci struct evdev_device *device) 637a46c0ec8Sopenharmony_ci{ 638a46c0ec8Sopenharmony_ci /* We must check ROTATION_Z after TILT_X/Y so that the tilt axes are 639a46c0ec8Sopenharmony_ci * already normalized and set if we have the mouse/lens tool */ 640a46c0ec8Sopenharmony_ci if (tablet->current_tool.type == LIBINPUT_TABLET_TOOL_TYPE_MOUSE || 641a46c0ec8Sopenharmony_ci tablet->current_tool.type == LIBINPUT_TABLET_TOOL_TYPE_LENS) { 642a46c0ec8Sopenharmony_ci tablet_update_mouse_rotation(tablet, device); 643a46c0ec8Sopenharmony_ci clear_bit(tablet->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_TILT_X); 644a46c0ec8Sopenharmony_ci clear_bit(tablet->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_TILT_Y); 645a46c0ec8Sopenharmony_ci tablet->axes.tilt.x = 0; 646a46c0ec8Sopenharmony_ci tablet->axes.tilt.y = 0; 647a46c0ec8Sopenharmony_ci 648a46c0ec8Sopenharmony_ci /* tilt is already converted to left-handed, so mouse 649a46c0ec8Sopenharmony_ci * rotation is converted to left-handed automatically */ 650a46c0ec8Sopenharmony_ci } else { 651a46c0ec8Sopenharmony_ci 652a46c0ec8Sopenharmony_ci tablet_update_artpen_rotation(tablet, device); 653a46c0ec8Sopenharmony_ci 654a46c0ec8Sopenharmony_ci if (device->left_handed.enabled) { 655a46c0ec8Sopenharmony_ci double r = tablet->axes.rotation; 656a46c0ec8Sopenharmony_ci tablet->axes.rotation = fmod(180 + r, 360); 657a46c0ec8Sopenharmony_ci } 658a46c0ec8Sopenharmony_ci } 659a46c0ec8Sopenharmony_ci} 660a46c0ec8Sopenharmony_ci 661a46c0ec8Sopenharmony_cistatic inline void 662a46c0ec8Sopenharmony_citablet_update_wheel(struct tablet_dispatch *tablet, 663a46c0ec8Sopenharmony_ci struct evdev_device *device) 664a46c0ec8Sopenharmony_ci{ 665a46c0ec8Sopenharmony_ci int a; 666a46c0ec8Sopenharmony_ci 667a46c0ec8Sopenharmony_ci a = LIBINPUT_TABLET_TOOL_AXIS_REL_WHEEL; 668a46c0ec8Sopenharmony_ci if (bit_is_set(tablet->changed_axes, a)) { 669a46c0ec8Sopenharmony_ci /* tablet->axes.wheel_discrete is already set */ 670a46c0ec8Sopenharmony_ci tablet->axes.wheel = normalize_wheel(tablet, 671a46c0ec8Sopenharmony_ci tablet->axes.wheel_discrete); 672a46c0ec8Sopenharmony_ci } else { 673a46c0ec8Sopenharmony_ci tablet->axes.wheel = 0; 674a46c0ec8Sopenharmony_ci tablet->axes.wheel_discrete = 0; 675a46c0ec8Sopenharmony_ci } 676a46c0ec8Sopenharmony_ci} 677a46c0ec8Sopenharmony_ci 678a46c0ec8Sopenharmony_cistatic void 679a46c0ec8Sopenharmony_citablet_smoothen_axes(const struct tablet_dispatch *tablet, 680a46c0ec8Sopenharmony_ci struct tablet_axes *axes) 681a46c0ec8Sopenharmony_ci{ 682a46c0ec8Sopenharmony_ci size_t i; 683a46c0ec8Sopenharmony_ci size_t count = tablet_history_size(tablet); 684a46c0ec8Sopenharmony_ci struct tablet_axes smooth = { 0 }; 685a46c0ec8Sopenharmony_ci 686a46c0ec8Sopenharmony_ci for (i = 0; i < count; i++) { 687a46c0ec8Sopenharmony_ci const struct tablet_axes *a = tablet_history_get(tablet, i); 688a46c0ec8Sopenharmony_ci 689a46c0ec8Sopenharmony_ci smooth.point.x += a->point.x; 690a46c0ec8Sopenharmony_ci smooth.point.y += a->point.y; 691a46c0ec8Sopenharmony_ci 692a46c0ec8Sopenharmony_ci smooth.tilt.x += a->tilt.x; 693a46c0ec8Sopenharmony_ci smooth.tilt.y += a->tilt.y; 694a46c0ec8Sopenharmony_ci } 695a46c0ec8Sopenharmony_ci 696a46c0ec8Sopenharmony_ci axes->point.x = smooth.point.x/count; 697a46c0ec8Sopenharmony_ci axes->point.y = smooth.point.y/count; 698a46c0ec8Sopenharmony_ci 699a46c0ec8Sopenharmony_ci axes->tilt.x = smooth.tilt.x/count; 700a46c0ec8Sopenharmony_ci axes->tilt.y = smooth.tilt.y/count; 701a46c0ec8Sopenharmony_ci} 702a46c0ec8Sopenharmony_ci 703a46c0ec8Sopenharmony_cistatic bool 704a46c0ec8Sopenharmony_citablet_check_notify_axes(struct tablet_dispatch *tablet, 705a46c0ec8Sopenharmony_ci struct evdev_device *device, 706a46c0ec8Sopenharmony_ci struct libinput_tablet_tool *tool, 707a46c0ec8Sopenharmony_ci struct tablet_axes *axes_out, 708a46c0ec8Sopenharmony_ci uint64_t time) 709a46c0ec8Sopenharmony_ci{ 710a46c0ec8Sopenharmony_ci struct tablet_axes axes = {0}; 711a46c0ec8Sopenharmony_ci const char tmp[sizeof(tablet->changed_axes)] = {0}; 712a46c0ec8Sopenharmony_ci bool rc = false; 713a46c0ec8Sopenharmony_ci 714a46c0ec8Sopenharmony_ci if (memcmp(tmp, tablet->changed_axes, sizeof(tmp)) == 0) { 715a46c0ec8Sopenharmony_ci axes = tablet->axes; 716a46c0ec8Sopenharmony_ci goto out; 717a46c0ec8Sopenharmony_ci } 718a46c0ec8Sopenharmony_ci 719a46c0ec8Sopenharmony_ci tablet_update_xy(tablet, device); 720a46c0ec8Sopenharmony_ci tablet_update_pressure(tablet, device, tool); 721a46c0ec8Sopenharmony_ci tablet_update_distance(tablet, device); 722a46c0ec8Sopenharmony_ci tablet_update_slider(tablet, device); 723a46c0ec8Sopenharmony_ci tablet_update_tilt(tablet, device); 724a46c0ec8Sopenharmony_ci tablet_update_wheel(tablet, device); 725a46c0ec8Sopenharmony_ci /* We must check ROTATION_Z after TILT_X/Y so that the tilt axes are 726a46c0ec8Sopenharmony_ci * already normalized and set if we have the mouse/lens tool */ 727a46c0ec8Sopenharmony_ci tablet_update_rotation(tablet, device); 728a46c0ec8Sopenharmony_ci 729a46c0ec8Sopenharmony_ci axes.point = tablet->axes.point; 730a46c0ec8Sopenharmony_ci axes.pressure = tablet->axes.pressure; 731a46c0ec8Sopenharmony_ci axes.distance = tablet->axes.distance; 732a46c0ec8Sopenharmony_ci axes.slider = tablet->axes.slider; 733a46c0ec8Sopenharmony_ci axes.tilt = tablet->axes.tilt; 734a46c0ec8Sopenharmony_ci axes.wheel = tablet->axes.wheel; 735a46c0ec8Sopenharmony_ci axes.wheel_discrete = tablet->axes.wheel_discrete; 736a46c0ec8Sopenharmony_ci axes.rotation = tablet->axes.rotation; 737a46c0ec8Sopenharmony_ci 738a46c0ec8Sopenharmony_ci rc = true; 739a46c0ec8Sopenharmony_ci 740a46c0ec8Sopenharmony_ciout: 741a46c0ec8Sopenharmony_ci /* The tool position often jumps to a different spot when contact changes. 742a46c0ec8Sopenharmony_ci * If tool contact changes, clear the history to prevent axis smoothing 743a46c0ec8Sopenharmony_ci * from trying to average over the spatial discontinuity. */ 744a46c0ec8Sopenharmony_ci if (tablet_has_status(tablet, TABLET_TOOL_ENTERING_CONTACT) || 745a46c0ec8Sopenharmony_ci tablet_has_status(tablet, TABLET_TOOL_LEAVING_CONTACT)) { 746a46c0ec8Sopenharmony_ci tablet_history_reset(tablet); 747a46c0ec8Sopenharmony_ci } 748a46c0ec8Sopenharmony_ci 749a46c0ec8Sopenharmony_ci tablet_history_push(tablet, &tablet->axes); 750a46c0ec8Sopenharmony_ci tablet_smoothen_axes(tablet, &axes); 751a46c0ec8Sopenharmony_ci 752a46c0ec8Sopenharmony_ci /* The delta relies on the last *smooth* point, so we do it last */ 753a46c0ec8Sopenharmony_ci axes.delta = tablet_tool_process_delta(tablet, tool, device, &axes, time); 754a46c0ec8Sopenharmony_ci 755a46c0ec8Sopenharmony_ci *axes_out = axes; 756a46c0ec8Sopenharmony_ci 757a46c0ec8Sopenharmony_ci return rc; 758a46c0ec8Sopenharmony_ci} 759a46c0ec8Sopenharmony_ci 760a46c0ec8Sopenharmony_cistatic void 761a46c0ec8Sopenharmony_citablet_update_button(struct tablet_dispatch *tablet, 762a46c0ec8Sopenharmony_ci uint32_t evcode, 763a46c0ec8Sopenharmony_ci uint32_t enable) 764a46c0ec8Sopenharmony_ci{ 765a46c0ec8Sopenharmony_ci switch (evcode) { 766a46c0ec8Sopenharmony_ci case BTN_LEFT: 767a46c0ec8Sopenharmony_ci case BTN_RIGHT: 768a46c0ec8Sopenharmony_ci case BTN_MIDDLE: 769a46c0ec8Sopenharmony_ci case BTN_SIDE: 770a46c0ec8Sopenharmony_ci case BTN_EXTRA: 771a46c0ec8Sopenharmony_ci case BTN_FORWARD: 772a46c0ec8Sopenharmony_ci case BTN_BACK: 773a46c0ec8Sopenharmony_ci case BTN_TASK: 774a46c0ec8Sopenharmony_ci case BTN_STYLUS: 775a46c0ec8Sopenharmony_ci case BTN_STYLUS2: 776a46c0ec8Sopenharmony_ci break; 777a46c0ec8Sopenharmony_ci default: 778a46c0ec8Sopenharmony_ci evdev_log_info(tablet->device, 779a46c0ec8Sopenharmony_ci "Unhandled button %s (%#x)\n", 780a46c0ec8Sopenharmony_ci libevdev_event_code_get_name(EV_KEY, evcode), 781a46c0ec8Sopenharmony_ci evcode); 782a46c0ec8Sopenharmony_ci return; 783a46c0ec8Sopenharmony_ci } 784a46c0ec8Sopenharmony_ci 785a46c0ec8Sopenharmony_ci if (enable) { 786a46c0ec8Sopenharmony_ci set_bit(tablet->button_state.bits, evcode); 787a46c0ec8Sopenharmony_ci tablet_set_status(tablet, TABLET_BUTTONS_PRESSED); 788a46c0ec8Sopenharmony_ci } else { 789a46c0ec8Sopenharmony_ci clear_bit(tablet->button_state.bits, evcode); 790a46c0ec8Sopenharmony_ci tablet_set_status(tablet, TABLET_BUTTONS_RELEASED); 791a46c0ec8Sopenharmony_ci } 792a46c0ec8Sopenharmony_ci} 793a46c0ec8Sopenharmony_ci 794a46c0ec8Sopenharmony_cistatic inline enum libinput_tablet_tool_type 795a46c0ec8Sopenharmony_citablet_evcode_to_tool(int code) 796a46c0ec8Sopenharmony_ci{ 797a46c0ec8Sopenharmony_ci enum libinput_tablet_tool_type type; 798a46c0ec8Sopenharmony_ci 799a46c0ec8Sopenharmony_ci switch (code) { 800a46c0ec8Sopenharmony_ci case BTN_TOOL_PEN: type = LIBINPUT_TABLET_TOOL_TYPE_PEN; break; 801a46c0ec8Sopenharmony_ci case BTN_TOOL_RUBBER: type = LIBINPUT_TABLET_TOOL_TYPE_ERASER; break; 802a46c0ec8Sopenharmony_ci case BTN_TOOL_BRUSH: type = LIBINPUT_TABLET_TOOL_TYPE_BRUSH; break; 803a46c0ec8Sopenharmony_ci case BTN_TOOL_PENCIL: type = LIBINPUT_TABLET_TOOL_TYPE_PENCIL; break; 804a46c0ec8Sopenharmony_ci case BTN_TOOL_AIRBRUSH: type = LIBINPUT_TABLET_TOOL_TYPE_AIRBRUSH; break; 805a46c0ec8Sopenharmony_ci case BTN_TOOL_MOUSE: type = LIBINPUT_TABLET_TOOL_TYPE_MOUSE; break; 806a46c0ec8Sopenharmony_ci case BTN_TOOL_LENS: type = LIBINPUT_TABLET_TOOL_TYPE_LENS; break; 807a46c0ec8Sopenharmony_ci default: 808a46c0ec8Sopenharmony_ci abort(); 809a46c0ec8Sopenharmony_ci } 810a46c0ec8Sopenharmony_ci 811a46c0ec8Sopenharmony_ci return type; 812a46c0ec8Sopenharmony_ci} 813a46c0ec8Sopenharmony_ci 814a46c0ec8Sopenharmony_cistatic void 815a46c0ec8Sopenharmony_citablet_process_key(struct tablet_dispatch *tablet, 816a46c0ec8Sopenharmony_ci struct evdev_device *device, 817a46c0ec8Sopenharmony_ci struct input_event *e, 818a46c0ec8Sopenharmony_ci uint64_t time) 819a46c0ec8Sopenharmony_ci{ 820a46c0ec8Sopenharmony_ci enum libinput_tablet_tool_type type; 821a46c0ec8Sopenharmony_ci 822a46c0ec8Sopenharmony_ci /* ignore kernel key repeat */ 823a46c0ec8Sopenharmony_ci if (e->value == 2) 824a46c0ec8Sopenharmony_ci return; 825a46c0ec8Sopenharmony_ci 826a46c0ec8Sopenharmony_ci switch (e->code) { 827a46c0ec8Sopenharmony_ci case BTN_TOOL_FINGER: 828a46c0ec8Sopenharmony_ci evdev_log_bug_libinput(device, 829a46c0ec8Sopenharmony_ci "Invalid tool 'finger' on tablet interface\n"); 830a46c0ec8Sopenharmony_ci break; 831a46c0ec8Sopenharmony_ci case BTN_TOOL_PEN: 832a46c0ec8Sopenharmony_ci case BTN_TOOL_RUBBER: 833a46c0ec8Sopenharmony_ci case BTN_TOOL_BRUSH: 834a46c0ec8Sopenharmony_ci case BTN_TOOL_PENCIL: 835a46c0ec8Sopenharmony_ci case BTN_TOOL_AIRBRUSH: 836a46c0ec8Sopenharmony_ci case BTN_TOOL_MOUSE: 837a46c0ec8Sopenharmony_ci case BTN_TOOL_LENS: 838a46c0ec8Sopenharmony_ci type = tablet_evcode_to_tool(e->code); 839a46c0ec8Sopenharmony_ci tablet_set_status(tablet, TABLET_TOOL_UPDATED); 840a46c0ec8Sopenharmony_ci if (e->value) 841a46c0ec8Sopenharmony_ci tablet->tool_state |= bit(type); 842a46c0ec8Sopenharmony_ci else 843a46c0ec8Sopenharmony_ci tablet->tool_state &= ~bit(type); 844a46c0ec8Sopenharmony_ci break; 845a46c0ec8Sopenharmony_ci case BTN_TOUCH: 846a46c0ec8Sopenharmony_ci if (!bit_is_set(tablet->axis_caps, 847a46c0ec8Sopenharmony_ci LIBINPUT_TABLET_TOOL_AXIS_PRESSURE)) { 848a46c0ec8Sopenharmony_ci if (e->value) 849a46c0ec8Sopenharmony_ci tablet_set_status(tablet, 850a46c0ec8Sopenharmony_ci TABLET_TOOL_ENTERING_CONTACT); 851a46c0ec8Sopenharmony_ci else 852a46c0ec8Sopenharmony_ci tablet_set_status(tablet, 853a46c0ec8Sopenharmony_ci TABLET_TOOL_LEAVING_CONTACT); 854a46c0ec8Sopenharmony_ci } 855a46c0ec8Sopenharmony_ci break; 856a46c0ec8Sopenharmony_ci default: 857a46c0ec8Sopenharmony_ci tablet_update_button(tablet, e->code, e->value); 858a46c0ec8Sopenharmony_ci break; 859a46c0ec8Sopenharmony_ci } 860a46c0ec8Sopenharmony_ci} 861a46c0ec8Sopenharmony_ci 862a46c0ec8Sopenharmony_cistatic void 863a46c0ec8Sopenharmony_citablet_process_relative(struct tablet_dispatch *tablet, 864a46c0ec8Sopenharmony_ci struct evdev_device *device, 865a46c0ec8Sopenharmony_ci struct input_event *e, 866a46c0ec8Sopenharmony_ci uint64_t time) 867a46c0ec8Sopenharmony_ci{ 868a46c0ec8Sopenharmony_ci enum libinput_tablet_tool_axis axis; 869a46c0ec8Sopenharmony_ci 870a46c0ec8Sopenharmony_ci switch (e->code) { 871a46c0ec8Sopenharmony_ci case REL_WHEEL: 872a46c0ec8Sopenharmony_ci axis = rel_evcode_to_axis(e->code); 873a46c0ec8Sopenharmony_ci if (axis == LIBINPUT_TABLET_TOOL_AXIS_NONE) { 874a46c0ec8Sopenharmony_ci evdev_log_bug_libinput(device, 875a46c0ec8Sopenharmony_ci "Invalid ABS event code %#x\n", 876a46c0ec8Sopenharmony_ci e->code); 877a46c0ec8Sopenharmony_ci break; 878a46c0ec8Sopenharmony_ci } 879a46c0ec8Sopenharmony_ci set_bit(tablet->changed_axes, axis); 880a46c0ec8Sopenharmony_ci tablet->axes.wheel_discrete = -1 * e->value; 881a46c0ec8Sopenharmony_ci tablet_set_status(tablet, TABLET_AXES_UPDATED); 882a46c0ec8Sopenharmony_ci break; 883a46c0ec8Sopenharmony_ci default: 884a46c0ec8Sopenharmony_ci evdev_log_info(device, 885a46c0ec8Sopenharmony_ci "Unhandled relative axis %s (%#x)\n", 886a46c0ec8Sopenharmony_ci libevdev_event_code_get_name(EV_REL, e->code), 887a46c0ec8Sopenharmony_ci e->code); 888a46c0ec8Sopenharmony_ci return; 889a46c0ec8Sopenharmony_ci } 890a46c0ec8Sopenharmony_ci} 891a46c0ec8Sopenharmony_ci 892a46c0ec8Sopenharmony_cistatic void 893a46c0ec8Sopenharmony_citablet_process_misc(struct tablet_dispatch *tablet, 894a46c0ec8Sopenharmony_ci struct evdev_device *device, 895a46c0ec8Sopenharmony_ci struct input_event *e, 896a46c0ec8Sopenharmony_ci uint64_t time) 897a46c0ec8Sopenharmony_ci{ 898a46c0ec8Sopenharmony_ci switch (e->code) { 899a46c0ec8Sopenharmony_ci case MSC_SERIAL: 900a46c0ec8Sopenharmony_ci if (e->value != -1) 901a46c0ec8Sopenharmony_ci tablet->current_tool.serial = e->value; 902a46c0ec8Sopenharmony_ci 903a46c0ec8Sopenharmony_ci break; 904a46c0ec8Sopenharmony_ci case MSC_SCAN: 905a46c0ec8Sopenharmony_ci break; 906a46c0ec8Sopenharmony_ci default: 907a46c0ec8Sopenharmony_ci evdev_log_info(device, 908a46c0ec8Sopenharmony_ci "Unhandled MSC event code %s (%#x)\n", 909a46c0ec8Sopenharmony_ci libevdev_event_code_get_name(EV_MSC, e->code), 910a46c0ec8Sopenharmony_ci e->code); 911a46c0ec8Sopenharmony_ci break; 912a46c0ec8Sopenharmony_ci } 913a46c0ec8Sopenharmony_ci} 914a46c0ec8Sopenharmony_ci 915a46c0ec8Sopenharmony_cistatic inline void 916a46c0ec8Sopenharmony_cicopy_axis_cap(const struct tablet_dispatch *tablet, 917a46c0ec8Sopenharmony_ci struct libinput_tablet_tool *tool, 918a46c0ec8Sopenharmony_ci enum libinput_tablet_tool_axis axis) 919a46c0ec8Sopenharmony_ci{ 920a46c0ec8Sopenharmony_ci if (bit_is_set(tablet->axis_caps, axis)) 921a46c0ec8Sopenharmony_ci set_bit(tool->axis_caps, axis); 922a46c0ec8Sopenharmony_ci} 923a46c0ec8Sopenharmony_ci 924a46c0ec8Sopenharmony_cistatic inline void 925a46c0ec8Sopenharmony_cicopy_button_cap(const struct tablet_dispatch *tablet, 926a46c0ec8Sopenharmony_ci struct libinput_tablet_tool *tool, 927a46c0ec8Sopenharmony_ci uint32_t button) 928a46c0ec8Sopenharmony_ci{ 929a46c0ec8Sopenharmony_ci struct libevdev *evdev = tablet->device->evdev; 930a46c0ec8Sopenharmony_ci if (libevdev_has_event_code(evdev, EV_KEY, button)) 931a46c0ec8Sopenharmony_ci set_bit(tool->buttons, button); 932a46c0ec8Sopenharmony_ci} 933a46c0ec8Sopenharmony_ci 934a46c0ec8Sopenharmony_ci#if HAVE_LIBWACOM 935a46c0ec8Sopenharmony_cistatic inline int 936a46c0ec8Sopenharmony_citool_set_bits_from_libwacom(const struct tablet_dispatch *tablet, 937a46c0ec8Sopenharmony_ci struct libinput_tablet_tool *tool) 938a46c0ec8Sopenharmony_ci{ 939a46c0ec8Sopenharmony_ci int rc = 1; 940a46c0ec8Sopenharmony_ci WacomDeviceDatabase *db; 941a46c0ec8Sopenharmony_ci const WacomStylus *s = NULL; 942a46c0ec8Sopenharmony_ci int code; 943a46c0ec8Sopenharmony_ci WacomStylusType type; 944a46c0ec8Sopenharmony_ci WacomAxisTypeFlags axes; 945a46c0ec8Sopenharmony_ci 946a46c0ec8Sopenharmony_ci db = tablet_libinput_context(tablet)->libwacom.db; 947a46c0ec8Sopenharmony_ci if (!db) 948a46c0ec8Sopenharmony_ci return rc; 949a46c0ec8Sopenharmony_ci 950a46c0ec8Sopenharmony_ci s = libwacom_stylus_get_for_id(db, tool->tool_id); 951a46c0ec8Sopenharmony_ci if (!s) 952a46c0ec8Sopenharmony_ci return rc; 953a46c0ec8Sopenharmony_ci 954a46c0ec8Sopenharmony_ci type = libwacom_stylus_get_type(s); 955a46c0ec8Sopenharmony_ci if (type == WSTYLUS_PUCK) { 956a46c0ec8Sopenharmony_ci for (code = BTN_LEFT; 957a46c0ec8Sopenharmony_ci code < BTN_LEFT + libwacom_stylus_get_num_buttons(s); 958a46c0ec8Sopenharmony_ci code++) 959a46c0ec8Sopenharmony_ci copy_button_cap(tablet, tool, code); 960a46c0ec8Sopenharmony_ci } else { 961a46c0ec8Sopenharmony_ci if (libwacom_stylus_get_num_buttons(s) >= 2) 962a46c0ec8Sopenharmony_ci copy_button_cap(tablet, tool, BTN_STYLUS2); 963a46c0ec8Sopenharmony_ci if (libwacom_stylus_get_num_buttons(s) >= 1) 964a46c0ec8Sopenharmony_ci copy_button_cap(tablet, tool, BTN_STYLUS); 965a46c0ec8Sopenharmony_ci } 966a46c0ec8Sopenharmony_ci 967a46c0ec8Sopenharmony_ci if (libwacom_stylus_has_wheel(s)) 968a46c0ec8Sopenharmony_ci copy_axis_cap(tablet, tool, LIBINPUT_TABLET_TOOL_AXIS_REL_WHEEL); 969a46c0ec8Sopenharmony_ci 970a46c0ec8Sopenharmony_ci axes = libwacom_stylus_get_axes(s); 971a46c0ec8Sopenharmony_ci 972a46c0ec8Sopenharmony_ci if (axes & WACOM_AXIS_TYPE_TILT) { 973a46c0ec8Sopenharmony_ci /* tilt on the puck is converted to rotation */ 974a46c0ec8Sopenharmony_ci if (type == WSTYLUS_PUCK) { 975a46c0ec8Sopenharmony_ci set_bit(tool->axis_caps, 976a46c0ec8Sopenharmony_ci LIBINPUT_TABLET_TOOL_AXIS_ROTATION_Z); 977a46c0ec8Sopenharmony_ci } else { 978a46c0ec8Sopenharmony_ci copy_axis_cap(tablet, 979a46c0ec8Sopenharmony_ci tool, 980a46c0ec8Sopenharmony_ci LIBINPUT_TABLET_TOOL_AXIS_TILT_X); 981a46c0ec8Sopenharmony_ci copy_axis_cap(tablet, 982a46c0ec8Sopenharmony_ci tool, 983a46c0ec8Sopenharmony_ci LIBINPUT_TABLET_TOOL_AXIS_TILT_Y); 984a46c0ec8Sopenharmony_ci } 985a46c0ec8Sopenharmony_ci } 986a46c0ec8Sopenharmony_ci if (axes & WACOM_AXIS_TYPE_ROTATION_Z) 987a46c0ec8Sopenharmony_ci copy_axis_cap(tablet, tool, LIBINPUT_TABLET_TOOL_AXIS_ROTATION_Z); 988a46c0ec8Sopenharmony_ci if (axes & WACOM_AXIS_TYPE_DISTANCE) 989a46c0ec8Sopenharmony_ci copy_axis_cap(tablet, tool, LIBINPUT_TABLET_TOOL_AXIS_DISTANCE); 990a46c0ec8Sopenharmony_ci if (axes & WACOM_AXIS_TYPE_SLIDER) 991a46c0ec8Sopenharmony_ci copy_axis_cap(tablet, tool, LIBINPUT_TABLET_TOOL_AXIS_SLIDER); 992a46c0ec8Sopenharmony_ci if (axes & WACOM_AXIS_TYPE_PRESSURE) 993a46c0ec8Sopenharmony_ci copy_axis_cap(tablet, tool, LIBINPUT_TABLET_TOOL_AXIS_PRESSURE); 994a46c0ec8Sopenharmony_ci 995a46c0ec8Sopenharmony_ci rc = 0; 996a46c0ec8Sopenharmony_ci 997a46c0ec8Sopenharmony_ci return rc; 998a46c0ec8Sopenharmony_ci} 999a46c0ec8Sopenharmony_ci#endif 1000a46c0ec8Sopenharmony_ci 1001a46c0ec8Sopenharmony_cistatic void 1002a46c0ec8Sopenharmony_citool_set_bits(const struct tablet_dispatch *tablet, 1003a46c0ec8Sopenharmony_ci struct libinput_tablet_tool *tool) 1004a46c0ec8Sopenharmony_ci{ 1005a46c0ec8Sopenharmony_ci enum libinput_tablet_tool_type type = tool->type; 1006a46c0ec8Sopenharmony_ci 1007a46c0ec8Sopenharmony_ci copy_axis_cap(tablet, tool, LIBINPUT_TABLET_TOOL_AXIS_X); 1008a46c0ec8Sopenharmony_ci copy_axis_cap(tablet, tool, LIBINPUT_TABLET_TOOL_AXIS_Y); 1009a46c0ec8Sopenharmony_ci 1010a46c0ec8Sopenharmony_ci#if HAVE_LIBWACOM 1011a46c0ec8Sopenharmony_ci if (tool_set_bits_from_libwacom(tablet, tool) == 0) 1012a46c0ec8Sopenharmony_ci return; 1013a46c0ec8Sopenharmony_ci#endif 1014a46c0ec8Sopenharmony_ci /* If we don't have libwacom, we simply copy any axis we have on the 1015a46c0ec8Sopenharmony_ci tablet onto the tool. Except we know that mice only have rotation 1016a46c0ec8Sopenharmony_ci anyway. 1017a46c0ec8Sopenharmony_ci */ 1018a46c0ec8Sopenharmony_ci switch (type) { 1019a46c0ec8Sopenharmony_ci case LIBINPUT_TABLET_TOOL_TYPE_PEN: 1020a46c0ec8Sopenharmony_ci case LIBINPUT_TABLET_TOOL_TYPE_ERASER: 1021a46c0ec8Sopenharmony_ci case LIBINPUT_TABLET_TOOL_TYPE_PENCIL: 1022a46c0ec8Sopenharmony_ci case LIBINPUT_TABLET_TOOL_TYPE_BRUSH: 1023a46c0ec8Sopenharmony_ci case LIBINPUT_TABLET_TOOL_TYPE_AIRBRUSH: 1024a46c0ec8Sopenharmony_ci copy_axis_cap(tablet, tool, LIBINPUT_TABLET_TOOL_AXIS_PRESSURE); 1025a46c0ec8Sopenharmony_ci copy_axis_cap(tablet, tool, LIBINPUT_TABLET_TOOL_AXIS_DISTANCE); 1026a46c0ec8Sopenharmony_ci copy_axis_cap(tablet, tool, LIBINPUT_TABLET_TOOL_AXIS_TILT_X); 1027a46c0ec8Sopenharmony_ci copy_axis_cap(tablet, tool, LIBINPUT_TABLET_TOOL_AXIS_TILT_Y); 1028a46c0ec8Sopenharmony_ci copy_axis_cap(tablet, tool, LIBINPUT_TABLET_TOOL_AXIS_SLIDER); 1029a46c0ec8Sopenharmony_ci 1030a46c0ec8Sopenharmony_ci /* Rotation is special, it can be either ABS_Z or 1031a46c0ec8Sopenharmony_ci * BTN_TOOL_MOUSE+ABS_TILT_X/Y. Aiptek tablets have 1032a46c0ec8Sopenharmony_ci * mouse+tilt (and thus rotation), but they do not have 1033a46c0ec8Sopenharmony_ci * ABS_Z. So let's not copy the axis bit if we don't have 1034a46c0ec8Sopenharmony_ci * ABS_Z, otherwise we try to get the value from it later on 1035a46c0ec8Sopenharmony_ci * proximity in and go boom because the absinfo isn't there. 1036a46c0ec8Sopenharmony_ci */ 1037a46c0ec8Sopenharmony_ci if (libevdev_has_event_code(tablet->device->evdev, EV_ABS, 1038a46c0ec8Sopenharmony_ci ABS_Z)) 1039a46c0ec8Sopenharmony_ci copy_axis_cap(tablet, tool, LIBINPUT_TABLET_TOOL_AXIS_ROTATION_Z); 1040a46c0ec8Sopenharmony_ci break; 1041a46c0ec8Sopenharmony_ci case LIBINPUT_TABLET_TOOL_TYPE_MOUSE: 1042a46c0ec8Sopenharmony_ci case LIBINPUT_TABLET_TOOL_TYPE_LENS: 1043a46c0ec8Sopenharmony_ci copy_axis_cap(tablet, tool, LIBINPUT_TABLET_TOOL_AXIS_ROTATION_Z); 1044a46c0ec8Sopenharmony_ci copy_axis_cap(tablet, tool, LIBINPUT_TABLET_TOOL_AXIS_REL_WHEEL); 1045a46c0ec8Sopenharmony_ci break; 1046a46c0ec8Sopenharmony_ci default: 1047a46c0ec8Sopenharmony_ci break; 1048a46c0ec8Sopenharmony_ci } 1049a46c0ec8Sopenharmony_ci 1050a46c0ec8Sopenharmony_ci /* If we don't have libwacom, copy all pen-related buttons from the 1051a46c0ec8Sopenharmony_ci tablet vs all mouse-related buttons */ 1052a46c0ec8Sopenharmony_ci switch (type) { 1053a46c0ec8Sopenharmony_ci case LIBINPUT_TABLET_TOOL_TYPE_PEN: 1054a46c0ec8Sopenharmony_ci case LIBINPUT_TABLET_TOOL_TYPE_BRUSH: 1055a46c0ec8Sopenharmony_ci case LIBINPUT_TABLET_TOOL_TYPE_AIRBRUSH: 1056a46c0ec8Sopenharmony_ci case LIBINPUT_TABLET_TOOL_TYPE_PENCIL: 1057a46c0ec8Sopenharmony_ci case LIBINPUT_TABLET_TOOL_TYPE_ERASER: 1058a46c0ec8Sopenharmony_ci copy_button_cap(tablet, tool, BTN_STYLUS); 1059a46c0ec8Sopenharmony_ci copy_button_cap(tablet, tool, BTN_STYLUS2); 1060a46c0ec8Sopenharmony_ci break; 1061a46c0ec8Sopenharmony_ci case LIBINPUT_TABLET_TOOL_TYPE_MOUSE: 1062a46c0ec8Sopenharmony_ci case LIBINPUT_TABLET_TOOL_TYPE_LENS: 1063a46c0ec8Sopenharmony_ci copy_button_cap(tablet, tool, BTN_LEFT); 1064a46c0ec8Sopenharmony_ci copy_button_cap(tablet, tool, BTN_MIDDLE); 1065a46c0ec8Sopenharmony_ci copy_button_cap(tablet, tool, BTN_RIGHT); 1066a46c0ec8Sopenharmony_ci copy_button_cap(tablet, tool, BTN_SIDE); 1067a46c0ec8Sopenharmony_ci copy_button_cap(tablet, tool, BTN_EXTRA); 1068a46c0ec8Sopenharmony_ci break; 1069a46c0ec8Sopenharmony_ci default: 1070a46c0ec8Sopenharmony_ci break; 1071a46c0ec8Sopenharmony_ci } 1072a46c0ec8Sopenharmony_ci} 1073a46c0ec8Sopenharmony_ci 1074a46c0ec8Sopenharmony_cistatic inline int 1075a46c0ec8Sopenharmony_ciaxis_range_percentage(const struct input_absinfo *a, double percent) 1076a46c0ec8Sopenharmony_ci{ 1077a46c0ec8Sopenharmony_ci return absinfo_range(a) * percent/100.0 + a->minimum; 1078a46c0ec8Sopenharmony_ci} 1079a46c0ec8Sopenharmony_ci 1080a46c0ec8Sopenharmony_cistatic inline void 1081a46c0ec8Sopenharmony_citool_set_pressure_thresholds(struct tablet_dispatch *tablet, 1082a46c0ec8Sopenharmony_ci struct libinput_tablet_tool *tool) 1083a46c0ec8Sopenharmony_ci{ 1084a46c0ec8Sopenharmony_ci struct evdev_device *device = tablet->device; 1085a46c0ec8Sopenharmony_ci const struct input_absinfo *pressure, *distance; 1086a46c0ec8Sopenharmony_ci struct quirks_context *quirks = NULL; 1087a46c0ec8Sopenharmony_ci struct quirks *q = NULL; 1088a46c0ec8Sopenharmony_ci struct quirk_range r; 1089a46c0ec8Sopenharmony_ci int lo = 0, hi = 1; 1090a46c0ec8Sopenharmony_ci 1091a46c0ec8Sopenharmony_ci tool->pressure.offset = 0; 1092a46c0ec8Sopenharmony_ci tool->pressure.has_offset = false; 1093a46c0ec8Sopenharmony_ci 1094a46c0ec8Sopenharmony_ci pressure = libevdev_get_abs_info(device->evdev, ABS_PRESSURE); 1095a46c0ec8Sopenharmony_ci if (!pressure) 1096a46c0ec8Sopenharmony_ci goto out; 1097a46c0ec8Sopenharmony_ci 1098a46c0ec8Sopenharmony_ci quirks = evdev_libinput_context(device)->quirks; 1099a46c0ec8Sopenharmony_ci q = quirks_fetch_for_device(quirks, device->udev_device); 1100a46c0ec8Sopenharmony_ci 1101a46c0ec8Sopenharmony_ci distance = libevdev_get_abs_info(device->evdev, ABS_DISTANCE); 1102a46c0ec8Sopenharmony_ci if (distance) { 1103a46c0ec8Sopenharmony_ci tool->pressure.offset = pressure->minimum; 1104a46c0ec8Sopenharmony_ci tool->pressure.heuristic_state = PRESSURE_HEURISTIC_STATE_DONE; 1105a46c0ec8Sopenharmony_ci } else { 1106a46c0ec8Sopenharmony_ci tool->pressure.offset = pressure->maximum; 1107a46c0ec8Sopenharmony_ci tool->pressure.heuristic_state = PRESSURE_HEURISTIC_STATE_PROXIN1; 1108a46c0ec8Sopenharmony_ci } 1109a46c0ec8Sopenharmony_ci 1110a46c0ec8Sopenharmony_ci /* 5 and 1% of the pressure range */ 1111a46c0ec8Sopenharmony_ci hi = axis_range_percentage(pressure, 5); 1112a46c0ec8Sopenharmony_ci lo = axis_range_percentage(pressure, 1); 1113a46c0ec8Sopenharmony_ci 1114a46c0ec8Sopenharmony_ci if (q && quirks_get_range(q, QUIRK_ATTR_PRESSURE_RANGE, &r)) { 1115a46c0ec8Sopenharmony_ci if (r.lower >= r.upper) { 1116a46c0ec8Sopenharmony_ci evdev_log_info(device, 1117a46c0ec8Sopenharmony_ci "Invalid pressure range, using defaults\n"); 1118a46c0ec8Sopenharmony_ci } else { 1119a46c0ec8Sopenharmony_ci hi = r.upper; 1120a46c0ec8Sopenharmony_ci lo = r.lower; 1121a46c0ec8Sopenharmony_ci } 1122a46c0ec8Sopenharmony_ci } 1123a46c0ec8Sopenharmony_ciout: 1124a46c0ec8Sopenharmony_ci tool->pressure.threshold.upper = hi; 1125a46c0ec8Sopenharmony_ci tool->pressure.threshold.lower = lo; 1126a46c0ec8Sopenharmony_ci 1127a46c0ec8Sopenharmony_ci quirks_unref(q); 1128a46c0ec8Sopenharmony_ci} 1129a46c0ec8Sopenharmony_ci 1130a46c0ec8Sopenharmony_cistatic struct libinput_tablet_tool * 1131a46c0ec8Sopenharmony_citablet_get_tool(struct tablet_dispatch *tablet, 1132a46c0ec8Sopenharmony_ci enum libinput_tablet_tool_type type, 1133a46c0ec8Sopenharmony_ci uint32_t tool_id, 1134a46c0ec8Sopenharmony_ci uint32_t serial) 1135a46c0ec8Sopenharmony_ci{ 1136a46c0ec8Sopenharmony_ci struct libinput *libinput = tablet_libinput_context(tablet); 1137a46c0ec8Sopenharmony_ci struct libinput_tablet_tool *tool = NULL, *t; 1138a46c0ec8Sopenharmony_ci struct list *tool_list; 1139a46c0ec8Sopenharmony_ci 1140a46c0ec8Sopenharmony_ci if (serial) { 1141a46c0ec8Sopenharmony_ci tool_list = &libinput->tool_list; 1142a46c0ec8Sopenharmony_ci /* Check if we already have the tool in our list of tools */ 1143a46c0ec8Sopenharmony_ci list_for_each(t, tool_list, link) { 1144a46c0ec8Sopenharmony_ci if (type == t->type && serial == t->serial) { 1145a46c0ec8Sopenharmony_ci tool = t; 1146a46c0ec8Sopenharmony_ci break; 1147a46c0ec8Sopenharmony_ci } 1148a46c0ec8Sopenharmony_ci } 1149a46c0ec8Sopenharmony_ci } 1150a46c0ec8Sopenharmony_ci 1151a46c0ec8Sopenharmony_ci /* If we get a tool with a delayed serial number, we already created 1152a46c0ec8Sopenharmony_ci * a 0-serial number tool for it earlier. Re-use that, even though 1153a46c0ec8Sopenharmony_ci * it means we can't distinguish this tool from others. 1154a46c0ec8Sopenharmony_ci * https://bugs.freedesktop.org/show_bug.cgi?id=97526 1155a46c0ec8Sopenharmony_ci */ 1156a46c0ec8Sopenharmony_ci if (!tool) { 1157a46c0ec8Sopenharmony_ci tool_list = &tablet->tool_list; 1158a46c0ec8Sopenharmony_ci /* We can't guarantee that tools without serial numbers are 1159a46c0ec8Sopenharmony_ci * unique, so we keep them local to the tablet that they come 1160a46c0ec8Sopenharmony_ci * into proximity of instead of storing them in the global tool 1161a46c0ec8Sopenharmony_ci * list 1162a46c0ec8Sopenharmony_ci * Same as above, but don't bother checking the serial number 1163a46c0ec8Sopenharmony_ci */ 1164a46c0ec8Sopenharmony_ci list_for_each(t, tool_list, link) { 1165a46c0ec8Sopenharmony_ci if (type == t->type) { 1166a46c0ec8Sopenharmony_ci tool = t; 1167a46c0ec8Sopenharmony_ci break; 1168a46c0ec8Sopenharmony_ci } 1169a46c0ec8Sopenharmony_ci } 1170a46c0ec8Sopenharmony_ci 1171a46c0ec8Sopenharmony_ci /* Didn't find the tool but we have a serial. Switch 1172a46c0ec8Sopenharmony_ci * tool_list back so we create in the correct list */ 1173a46c0ec8Sopenharmony_ci if (!tool && serial) 1174a46c0ec8Sopenharmony_ci tool_list = &libinput->tool_list; 1175a46c0ec8Sopenharmony_ci } 1176a46c0ec8Sopenharmony_ci 1177a46c0ec8Sopenharmony_ci /* If we didn't already have the new_tool in our list of tools, 1178a46c0ec8Sopenharmony_ci * add it */ 1179a46c0ec8Sopenharmony_ci if (!tool) { 1180a46c0ec8Sopenharmony_ci tool = zalloc(sizeof *tool); 1181a46c0ec8Sopenharmony_ci 1182a46c0ec8Sopenharmony_ci *tool = (struct libinput_tablet_tool) { 1183a46c0ec8Sopenharmony_ci .type = type, 1184a46c0ec8Sopenharmony_ci .serial = serial, 1185a46c0ec8Sopenharmony_ci .tool_id = tool_id, 1186a46c0ec8Sopenharmony_ci .refcount = 1, 1187a46c0ec8Sopenharmony_ci }; 1188a46c0ec8Sopenharmony_ci 1189a46c0ec8Sopenharmony_ci tool_set_pressure_thresholds(tablet, tool); 1190a46c0ec8Sopenharmony_ci tool_set_bits(tablet, tool); 1191a46c0ec8Sopenharmony_ci 1192a46c0ec8Sopenharmony_ci list_insert(tool_list, &tool->link); 1193a46c0ec8Sopenharmony_ci } 1194a46c0ec8Sopenharmony_ci 1195a46c0ec8Sopenharmony_ci return tool; 1196a46c0ec8Sopenharmony_ci} 1197a46c0ec8Sopenharmony_ci 1198a46c0ec8Sopenharmony_cistatic void 1199a46c0ec8Sopenharmony_citablet_notify_button_mask(struct tablet_dispatch *tablet, 1200a46c0ec8Sopenharmony_ci struct evdev_device *device, 1201a46c0ec8Sopenharmony_ci uint64_t time, 1202a46c0ec8Sopenharmony_ci struct libinput_tablet_tool *tool, 1203a46c0ec8Sopenharmony_ci const struct button_state *buttons, 1204a46c0ec8Sopenharmony_ci enum libinput_button_state state) 1205a46c0ec8Sopenharmony_ci{ 1206a46c0ec8Sopenharmony_ci struct libinput_device *base = &device->base; 1207a46c0ec8Sopenharmony_ci size_t i; 1208a46c0ec8Sopenharmony_ci size_t nbits = 8 * sizeof(buttons->bits); 1209a46c0ec8Sopenharmony_ci enum libinput_tablet_tool_tip_state tip_state; 1210a46c0ec8Sopenharmony_ci 1211a46c0ec8Sopenharmony_ci if (tablet_has_status(tablet, TABLET_TOOL_IN_CONTACT)) 1212a46c0ec8Sopenharmony_ci tip_state = LIBINPUT_TABLET_TOOL_TIP_DOWN; 1213a46c0ec8Sopenharmony_ci else 1214a46c0ec8Sopenharmony_ci tip_state = LIBINPUT_TABLET_TOOL_TIP_UP; 1215a46c0ec8Sopenharmony_ci 1216a46c0ec8Sopenharmony_ci for (i = 0; i < nbits; i++) { 1217a46c0ec8Sopenharmony_ci if (!bit_is_set(buttons->bits, i)) 1218a46c0ec8Sopenharmony_ci continue; 1219a46c0ec8Sopenharmony_ci 1220a46c0ec8Sopenharmony_ci tablet_notify_button(base, 1221a46c0ec8Sopenharmony_ci time, 1222a46c0ec8Sopenharmony_ci tool, 1223a46c0ec8Sopenharmony_ci tip_state, 1224a46c0ec8Sopenharmony_ci &tablet->axes, 1225a46c0ec8Sopenharmony_ci i, 1226a46c0ec8Sopenharmony_ci state); 1227a46c0ec8Sopenharmony_ci } 1228a46c0ec8Sopenharmony_ci} 1229a46c0ec8Sopenharmony_ci 1230a46c0ec8Sopenharmony_cistatic void 1231a46c0ec8Sopenharmony_citablet_notify_buttons(struct tablet_dispatch *tablet, 1232a46c0ec8Sopenharmony_ci struct evdev_device *device, 1233a46c0ec8Sopenharmony_ci uint64_t time, 1234a46c0ec8Sopenharmony_ci struct libinput_tablet_tool *tool, 1235a46c0ec8Sopenharmony_ci enum libinput_button_state state) 1236a46c0ec8Sopenharmony_ci{ 1237a46c0ec8Sopenharmony_ci struct button_state buttons; 1238a46c0ec8Sopenharmony_ci 1239a46c0ec8Sopenharmony_ci if (state == LIBINPUT_BUTTON_STATE_PRESSED) 1240a46c0ec8Sopenharmony_ci tablet_get_pressed_buttons(tablet, &buttons); 1241a46c0ec8Sopenharmony_ci else 1242a46c0ec8Sopenharmony_ci tablet_get_released_buttons(tablet, &buttons); 1243a46c0ec8Sopenharmony_ci 1244a46c0ec8Sopenharmony_ci tablet_notify_button_mask(tablet, 1245a46c0ec8Sopenharmony_ci device, 1246a46c0ec8Sopenharmony_ci time, 1247a46c0ec8Sopenharmony_ci tool, 1248a46c0ec8Sopenharmony_ci &buttons, 1249a46c0ec8Sopenharmony_ci state); 1250a46c0ec8Sopenharmony_ci} 1251a46c0ec8Sopenharmony_ci 1252a46c0ec8Sopenharmony_cistatic void 1253a46c0ec8Sopenharmony_cisanitize_pressure_distance(struct tablet_dispatch *tablet, 1254a46c0ec8Sopenharmony_ci struct libinput_tablet_tool *tool) 1255a46c0ec8Sopenharmony_ci{ 1256a46c0ec8Sopenharmony_ci bool tool_in_contact; 1257a46c0ec8Sopenharmony_ci const struct input_absinfo *distance, 1258a46c0ec8Sopenharmony_ci *pressure; 1259a46c0ec8Sopenharmony_ci 1260a46c0ec8Sopenharmony_ci distance = libevdev_get_abs_info(tablet->device->evdev, ABS_DISTANCE); 1261a46c0ec8Sopenharmony_ci pressure = libevdev_get_abs_info(tablet->device->evdev, ABS_PRESSURE); 1262a46c0ec8Sopenharmony_ci 1263a46c0ec8Sopenharmony_ci if (!pressure || !distance) 1264a46c0ec8Sopenharmony_ci return; 1265a46c0ec8Sopenharmony_ci 1266a46c0ec8Sopenharmony_ci bool pressure_changed = bit_is_set(tablet->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_PRESSURE); 1267a46c0ec8Sopenharmony_ci bool distance_changed = bit_is_set(tablet->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_DISTANCE); 1268a46c0ec8Sopenharmony_ci 1269a46c0ec8Sopenharmony_ci if (!pressure_changed && !distance_changed) 1270a46c0ec8Sopenharmony_ci return; 1271a46c0ec8Sopenharmony_ci 1272a46c0ec8Sopenharmony_ci /* Note: this is an arbitrary "in contact" decision rather than "tip 1273a46c0ec8Sopenharmony_ci * down". We use the lower threshold as minimum pressure value, 1274a46c0ec8Sopenharmony_ci * anything less than that gets filtered away */ 1275a46c0ec8Sopenharmony_ci tool_in_contact = (pressure->value > tool->pressure.threshold.lower); 1276a46c0ec8Sopenharmony_ci 1277a46c0ec8Sopenharmony_ci /* Keep distance and pressure mutually exclusive */ 1278a46c0ec8Sopenharmony_ci if (distance && 1279a46c0ec8Sopenharmony_ci distance->value > distance->minimum && 1280a46c0ec8Sopenharmony_ci pressure->value > pressure->minimum) { 1281a46c0ec8Sopenharmony_ci if (tool_in_contact) { 1282a46c0ec8Sopenharmony_ci clear_bit(tablet->changed_axes, 1283a46c0ec8Sopenharmony_ci LIBINPUT_TABLET_TOOL_AXIS_DISTANCE); 1284a46c0ec8Sopenharmony_ci tablet->axes.distance = 0; 1285a46c0ec8Sopenharmony_ci } else { 1286a46c0ec8Sopenharmony_ci clear_bit(tablet->changed_axes, 1287a46c0ec8Sopenharmony_ci LIBINPUT_TABLET_TOOL_AXIS_PRESSURE); 1288a46c0ec8Sopenharmony_ci tablet->axes.pressure = 0; 1289a46c0ec8Sopenharmony_ci } 1290a46c0ec8Sopenharmony_ci } else if (pressure_changed && !tool_in_contact) { 1291a46c0ec8Sopenharmony_ci /* Make sure that the last axis value sent to the caller is a 0 */ 1292a46c0ec8Sopenharmony_ci if (tablet->axes.pressure == 0) 1293a46c0ec8Sopenharmony_ci clear_bit(tablet->changed_axes, 1294a46c0ec8Sopenharmony_ci LIBINPUT_TABLET_TOOL_AXIS_PRESSURE); 1295a46c0ec8Sopenharmony_ci else 1296a46c0ec8Sopenharmony_ci tablet->axes.pressure = 0; 1297a46c0ec8Sopenharmony_ci } 1298a46c0ec8Sopenharmony_ci} 1299a46c0ec8Sopenharmony_ci 1300a46c0ec8Sopenharmony_cistatic inline void 1301a46c0ec8Sopenharmony_cisanitize_mouse_lens_rotation(struct tablet_dispatch *tablet) 1302a46c0ec8Sopenharmony_ci{ 1303a46c0ec8Sopenharmony_ci /* If we have a mouse/lens cursor and the tilt changed, the rotation 1304a46c0ec8Sopenharmony_ci changed. Mark this, calculate the angle later */ 1305a46c0ec8Sopenharmony_ci if ((tablet->current_tool.type == LIBINPUT_TABLET_TOOL_TYPE_MOUSE || 1306a46c0ec8Sopenharmony_ci tablet->current_tool.type == LIBINPUT_TABLET_TOOL_TYPE_LENS) && 1307a46c0ec8Sopenharmony_ci (bit_is_set(tablet->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_TILT_X) || 1308a46c0ec8Sopenharmony_ci bit_is_set(tablet->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_TILT_Y))) 1309a46c0ec8Sopenharmony_ci set_bit(tablet->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_ROTATION_Z); 1310a46c0ec8Sopenharmony_ci} 1311a46c0ec8Sopenharmony_ci 1312a46c0ec8Sopenharmony_cistatic void 1313a46c0ec8Sopenharmony_cisanitize_tablet_axes(struct tablet_dispatch *tablet, 1314a46c0ec8Sopenharmony_ci struct libinput_tablet_tool *tool) 1315a46c0ec8Sopenharmony_ci{ 1316a46c0ec8Sopenharmony_ci sanitize_pressure_distance(tablet, tool); 1317a46c0ec8Sopenharmony_ci sanitize_mouse_lens_rotation(tablet); 1318a46c0ec8Sopenharmony_ci} 1319a46c0ec8Sopenharmony_ci 1320a46c0ec8Sopenharmony_cistatic void 1321a46c0ec8Sopenharmony_ciset_pressure_offset(struct libinput_tablet_tool *tool, int offset) 1322a46c0ec8Sopenharmony_ci{ 1323a46c0ec8Sopenharmony_ci tool->pressure.offset = offset; 1324a46c0ec8Sopenharmony_ci tool->pressure.has_offset = true; 1325a46c0ec8Sopenharmony_ci 1326a46c0ec8Sopenharmony_ci /* Adjust the tresholds accordingly - we use the same gap (4% in 1327a46c0ec8Sopenharmony_ci * device coordinates) between upper and lower as before which isn't 1328a46c0ec8Sopenharmony_ci * technically correct (our range shrunk) but it's easy to calculate. 1329a46c0ec8Sopenharmony_ci */ 1330a46c0ec8Sopenharmony_ci int gap = tool->pressure.threshold.upper - tool->pressure.threshold.lower; 1331a46c0ec8Sopenharmony_ci tool->pressure.threshold.lower = offset; 1332a46c0ec8Sopenharmony_ci tool->pressure.threshold.upper = offset + gap; 1333a46c0ec8Sopenharmony_ci} 1334a46c0ec8Sopenharmony_ci 1335a46c0ec8Sopenharmony_cistatic void 1336a46c0ec8Sopenharmony_ciupdate_pressure_offset(struct tablet_dispatch *tablet, 1337a46c0ec8Sopenharmony_ci struct evdev_device *device, 1338a46c0ec8Sopenharmony_ci struct libinput_tablet_tool *tool) 1339a46c0ec8Sopenharmony_ci{ 1340a46c0ec8Sopenharmony_ci const struct input_absinfo *pressure = 1341a46c0ec8Sopenharmony_ci libevdev_get_abs_info(device->evdev, ABS_PRESSURE); 1342a46c0ec8Sopenharmony_ci 1343a46c0ec8Sopenharmony_ci if (!pressure || 1344a46c0ec8Sopenharmony_ci !bit_is_set(tablet->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_PRESSURE)) 1345a46c0ec8Sopenharmony_ci return; 1346a46c0ec8Sopenharmony_ci 1347a46c0ec8Sopenharmony_ci /* If we have an event that falls below the current offset, adjust 1348a46c0ec8Sopenharmony_ci * the offset downwards. A fast contact can start with a 1349a46c0ec8Sopenharmony_ci * higher-than-needed pressure offset and then we'd be tied into a 1350a46c0ec8Sopenharmony_ci * high pressure offset for the rest of the session. 1351a46c0ec8Sopenharmony_ci * 1352a46c0ec8Sopenharmony_ci * If we are still pending the offset decision, only update the observed 1353a46c0ec8Sopenharmony_ci * offset value, don't actually set it to have an offset. 1354a46c0ec8Sopenharmony_ci */ 1355a46c0ec8Sopenharmony_ci int offset = pressure->value; 1356a46c0ec8Sopenharmony_ci if (tool->pressure.has_offset) { 1357a46c0ec8Sopenharmony_ci if (offset < tool->pressure.offset) 1358a46c0ec8Sopenharmony_ci set_pressure_offset(tool, offset); 1359a46c0ec8Sopenharmony_ci } else if (tool->pressure.heuristic_state != PRESSURE_HEURISTIC_STATE_DONE) { 1360a46c0ec8Sopenharmony_ci tool->pressure.offset = min(offset, tool->pressure.offset); 1361a46c0ec8Sopenharmony_ci } 1362a46c0ec8Sopenharmony_ci} 1363a46c0ec8Sopenharmony_ci 1364a46c0ec8Sopenharmony_cistatic void 1365a46c0ec8Sopenharmony_cidetect_pressure_offset(struct tablet_dispatch *tablet, 1366a46c0ec8Sopenharmony_ci struct evdev_device *device, 1367a46c0ec8Sopenharmony_ci struct libinput_tablet_tool *tool) 1368a46c0ec8Sopenharmony_ci{ 1369a46c0ec8Sopenharmony_ci const struct input_absinfo *pressure, *distance; 1370a46c0ec8Sopenharmony_ci int offset; 1371a46c0ec8Sopenharmony_ci 1372a46c0ec8Sopenharmony_ci if (tool->pressure.has_offset || 1373a46c0ec8Sopenharmony_ci !bit_is_set(tablet->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_PRESSURE)) 1374a46c0ec8Sopenharmony_ci return; 1375a46c0ec8Sopenharmony_ci 1376a46c0ec8Sopenharmony_ci pressure = libevdev_get_abs_info(device->evdev, ABS_PRESSURE); 1377a46c0ec8Sopenharmony_ci distance = libevdev_get_abs_info(device->evdev, ABS_DISTANCE); 1378a46c0ec8Sopenharmony_ci 1379a46c0ec8Sopenharmony_ci if (!pressure) 1380a46c0ec8Sopenharmony_ci return; 1381a46c0ec8Sopenharmony_ci 1382a46c0ec8Sopenharmony_ci offset = pressure->value; 1383a46c0ec8Sopenharmony_ci if (offset <= pressure->minimum) 1384a46c0ec8Sopenharmony_ci return; 1385a46c0ec8Sopenharmony_ci 1386a46c0ec8Sopenharmony_ci if (distance) { 1387a46c0ec8Sopenharmony_ci /* If we're closer than 50% of the distance axis, skip pressure 1388a46c0ec8Sopenharmony_ci * offset detection, too likely to be wrong */ 1389a46c0ec8Sopenharmony_ci if (distance->value < axis_range_percentage(distance, 50)) 1390a46c0ec8Sopenharmony_ci return; 1391a46c0ec8Sopenharmony_ci } else { 1392a46c0ec8Sopenharmony_ci /* A device without distance will always have some pressure on 1393a46c0ec8Sopenharmony_ci * contact. Offset detection is delayed for a few proximity ins 1394a46c0ec8Sopenharmony_ci * in the hope we'll find the minimum value until then. That 1395a46c0ec8Sopenharmony_ci * offset is updated during motion events so by the time the 1396a46c0ec8Sopenharmony_ci * deciding prox-in arrives we should know the minimum offset. 1397a46c0ec8Sopenharmony_ci */ 1398a46c0ec8Sopenharmony_ci if (offset > pressure->minimum) 1399a46c0ec8Sopenharmony_ci tool->pressure.offset = min(offset, tool->pressure.offset); 1400a46c0ec8Sopenharmony_ci 1401a46c0ec8Sopenharmony_ci switch (tool->pressure.heuristic_state) { 1402a46c0ec8Sopenharmony_ci case PRESSURE_HEURISTIC_STATE_PROXIN1: 1403a46c0ec8Sopenharmony_ci case PRESSURE_HEURISTIC_STATE_PROXIN2: 1404a46c0ec8Sopenharmony_ci tool->pressure.heuristic_state++; 1405a46c0ec8Sopenharmony_ci return; 1406a46c0ec8Sopenharmony_ci case PRESSURE_HEURISTIC_STATE_DECIDE: 1407a46c0ec8Sopenharmony_ci tool->pressure.heuristic_state++; 1408a46c0ec8Sopenharmony_ci offset = tool->pressure.offset; 1409a46c0ec8Sopenharmony_ci break; 1410a46c0ec8Sopenharmony_ci case PRESSURE_HEURISTIC_STATE_DONE: 1411a46c0ec8Sopenharmony_ci return; 1412a46c0ec8Sopenharmony_ci } 1413a46c0ec8Sopenharmony_ci } 1414a46c0ec8Sopenharmony_ci 1415a46c0ec8Sopenharmony_ci if (offset <= pressure->minimum) 1416a46c0ec8Sopenharmony_ci return; 1417a46c0ec8Sopenharmony_ci 1418a46c0ec8Sopenharmony_ci if (offset > axis_range_percentage(pressure, 50)) { 1419a46c0ec8Sopenharmony_ci evdev_log_error(device, 1420a46c0ec8Sopenharmony_ci "Ignoring pressure offset greater than 50%% detected on tool %s (serial %#x). " 1421a46c0ec8Sopenharmony_ci "See %s/tablet-support.html\n", 1422a46c0ec8Sopenharmony_ci tablet_tool_type_to_string(tool->type), 1423a46c0ec8Sopenharmony_ci tool->serial, 1424a46c0ec8Sopenharmony_ci HTTP_DOC_LINK); 1425a46c0ec8Sopenharmony_ci return; 1426a46c0ec8Sopenharmony_ci } 1427a46c0ec8Sopenharmony_ci 1428a46c0ec8Sopenharmony_ci evdev_log_info(device, 1429a46c0ec8Sopenharmony_ci "Pressure offset detected on tool %s (serial %#x). " 1430a46c0ec8Sopenharmony_ci "See %s/tablet-support.html\n", 1431a46c0ec8Sopenharmony_ci tablet_tool_type_to_string(tool->type), 1432a46c0ec8Sopenharmony_ci tool->serial, 1433a46c0ec8Sopenharmony_ci HTTP_DOC_LINK); 1434a46c0ec8Sopenharmony_ci 1435a46c0ec8Sopenharmony_ci set_pressure_offset(tool, offset); 1436a46c0ec8Sopenharmony_ci} 1437a46c0ec8Sopenharmony_ci 1438a46c0ec8Sopenharmony_cistatic void 1439a46c0ec8Sopenharmony_cidetect_tool_contact(struct tablet_dispatch *tablet, 1440a46c0ec8Sopenharmony_ci struct evdev_device *device, 1441a46c0ec8Sopenharmony_ci struct libinput_tablet_tool *tool) 1442a46c0ec8Sopenharmony_ci{ 1443a46c0ec8Sopenharmony_ci const struct input_absinfo *p; 1444a46c0ec8Sopenharmony_ci int pressure; 1445a46c0ec8Sopenharmony_ci 1446a46c0ec8Sopenharmony_ci if (!bit_is_set(tool->axis_caps, LIBINPUT_TABLET_TOOL_AXIS_PRESSURE)) 1447a46c0ec8Sopenharmony_ci return; 1448a46c0ec8Sopenharmony_ci 1449a46c0ec8Sopenharmony_ci /* if we have pressure, always use that for contact, not BTN_TOUCH */ 1450a46c0ec8Sopenharmony_ci if (tablet_has_status(tablet, TABLET_TOOL_ENTERING_CONTACT)) 1451a46c0ec8Sopenharmony_ci evdev_log_bug_libinput(device, 1452a46c0ec8Sopenharmony_ci "Invalid status: entering contact\n"); 1453a46c0ec8Sopenharmony_ci if (tablet_has_status(tablet, TABLET_TOOL_LEAVING_CONTACT) && 1454a46c0ec8Sopenharmony_ci !tablet_has_status(tablet, TABLET_TOOL_LEAVING_PROXIMITY)) 1455a46c0ec8Sopenharmony_ci evdev_log_bug_libinput(device, 1456a46c0ec8Sopenharmony_ci "Invalid status: leaving contact\n"); 1457a46c0ec8Sopenharmony_ci 1458a46c0ec8Sopenharmony_ci p = libevdev_get_abs_info(tablet->device->evdev, ABS_PRESSURE); 1459a46c0ec8Sopenharmony_ci if (!p) { 1460a46c0ec8Sopenharmony_ci evdev_log_bug_libinput(device, 1461a46c0ec8Sopenharmony_ci "Missing pressure axis\n"); 1462a46c0ec8Sopenharmony_ci return; 1463a46c0ec8Sopenharmony_ci } 1464a46c0ec8Sopenharmony_ci pressure = p->value; 1465a46c0ec8Sopenharmony_ci 1466a46c0ec8Sopenharmony_ci if (pressure <= tool->pressure.threshold.lower && 1467a46c0ec8Sopenharmony_ci tablet_has_status(tablet, TABLET_TOOL_IN_CONTACT)) { 1468a46c0ec8Sopenharmony_ci tablet_set_status(tablet, TABLET_TOOL_LEAVING_CONTACT); 1469a46c0ec8Sopenharmony_ci } else if (pressure >= tool->pressure.threshold.upper && 1470a46c0ec8Sopenharmony_ci !tablet_has_status(tablet, TABLET_TOOL_IN_CONTACT)) { 1471a46c0ec8Sopenharmony_ci tablet_set_status(tablet, TABLET_TOOL_ENTERING_CONTACT); 1472a46c0ec8Sopenharmony_ci } 1473a46c0ec8Sopenharmony_ci} 1474a46c0ec8Sopenharmony_ci 1475a46c0ec8Sopenharmony_cistatic void 1476a46c0ec8Sopenharmony_citablet_mark_all_axes_changed(struct tablet_dispatch *tablet, 1477a46c0ec8Sopenharmony_ci struct libinput_tablet_tool *tool) 1478a46c0ec8Sopenharmony_ci{ 1479a46c0ec8Sopenharmony_ci static_assert(sizeof(tablet->changed_axes) == 1480a46c0ec8Sopenharmony_ci sizeof(tool->axis_caps), 1481a46c0ec8Sopenharmony_ci "Mismatching array sizes"); 1482a46c0ec8Sopenharmony_ci 1483a46c0ec8Sopenharmony_ci memcpy(tablet->changed_axes, 1484a46c0ec8Sopenharmony_ci tool->axis_caps, 1485a46c0ec8Sopenharmony_ci sizeof(tablet->changed_axes)); 1486a46c0ec8Sopenharmony_ci} 1487a46c0ec8Sopenharmony_ci 1488a46c0ec8Sopenharmony_cistatic void 1489a46c0ec8Sopenharmony_citablet_update_proximity_state(struct tablet_dispatch *tablet, 1490a46c0ec8Sopenharmony_ci struct evdev_device *device, 1491a46c0ec8Sopenharmony_ci struct libinput_tablet_tool *tool) 1492a46c0ec8Sopenharmony_ci{ 1493a46c0ec8Sopenharmony_ci const struct input_absinfo *distance; 1494a46c0ec8Sopenharmony_ci int dist_max = tablet->cursor_proximity_threshold; 1495a46c0ec8Sopenharmony_ci int dist; 1496a46c0ec8Sopenharmony_ci 1497a46c0ec8Sopenharmony_ci distance = libevdev_get_abs_info(tablet->device->evdev, ABS_DISTANCE); 1498a46c0ec8Sopenharmony_ci if (!distance) 1499a46c0ec8Sopenharmony_ci return; 1500a46c0ec8Sopenharmony_ci 1501a46c0ec8Sopenharmony_ci dist = distance->value; 1502a46c0ec8Sopenharmony_ci if (dist == 0) 1503a46c0ec8Sopenharmony_ci return; 1504a46c0ec8Sopenharmony_ci 1505a46c0ec8Sopenharmony_ci /* Tool got into permitted range */ 1506a46c0ec8Sopenharmony_ci if (dist < dist_max && 1507a46c0ec8Sopenharmony_ci (tablet_has_status(tablet, TABLET_TOOL_OUT_OF_RANGE) || 1508a46c0ec8Sopenharmony_ci tablet_has_status(tablet, TABLET_TOOL_OUT_OF_PROXIMITY))) { 1509a46c0ec8Sopenharmony_ci tablet_unset_status(tablet, 1510a46c0ec8Sopenharmony_ci TABLET_TOOL_OUT_OF_RANGE); 1511a46c0ec8Sopenharmony_ci tablet_unset_status(tablet, 1512a46c0ec8Sopenharmony_ci TABLET_TOOL_OUT_OF_PROXIMITY); 1513a46c0ec8Sopenharmony_ci tablet_set_status(tablet, TABLET_TOOL_ENTERING_PROXIMITY); 1514a46c0ec8Sopenharmony_ci tablet_mark_all_axes_changed(tablet, tool); 1515a46c0ec8Sopenharmony_ci 1516a46c0ec8Sopenharmony_ci tablet_set_status(tablet, TABLET_BUTTONS_PRESSED); 1517a46c0ec8Sopenharmony_ci tablet_force_button_presses(tablet); 1518a46c0ec8Sopenharmony_ci return; 1519a46c0ec8Sopenharmony_ci } 1520a46c0ec8Sopenharmony_ci 1521a46c0ec8Sopenharmony_ci if (dist < dist_max) 1522a46c0ec8Sopenharmony_ci return; 1523a46c0ec8Sopenharmony_ci 1524a46c0ec8Sopenharmony_ci /* Still out of range/proximity */ 1525a46c0ec8Sopenharmony_ci if (tablet_has_status(tablet, TABLET_TOOL_OUT_OF_RANGE) || 1526a46c0ec8Sopenharmony_ci tablet_has_status(tablet, TABLET_TOOL_OUT_OF_PROXIMITY)) 1527a46c0ec8Sopenharmony_ci return; 1528a46c0ec8Sopenharmony_ci 1529a46c0ec8Sopenharmony_ci /* Tool entered prox but is outside of permitted range */ 1530a46c0ec8Sopenharmony_ci if (tablet_has_status(tablet, 1531a46c0ec8Sopenharmony_ci TABLET_TOOL_ENTERING_PROXIMITY)) { 1532a46c0ec8Sopenharmony_ci tablet_set_status(tablet, 1533a46c0ec8Sopenharmony_ci TABLET_TOOL_OUT_OF_RANGE); 1534a46c0ec8Sopenharmony_ci tablet_unset_status(tablet, 1535a46c0ec8Sopenharmony_ci TABLET_TOOL_ENTERING_PROXIMITY); 1536a46c0ec8Sopenharmony_ci return; 1537a46c0ec8Sopenharmony_ci } 1538a46c0ec8Sopenharmony_ci 1539a46c0ec8Sopenharmony_ci /* Tool was in prox and is now outside of range. Set leaving 1540a46c0ec8Sopenharmony_ci * proximity, on the next event it will be OUT_OF_PROXIMITY and thus 1541a46c0ec8Sopenharmony_ci * caught by the above conditions */ 1542a46c0ec8Sopenharmony_ci tablet_set_status(tablet, TABLET_TOOL_LEAVING_PROXIMITY); 1543a46c0ec8Sopenharmony_ci} 1544a46c0ec8Sopenharmony_ci 1545a46c0ec8Sopenharmony_cistatic struct phys_rect 1546a46c0ec8Sopenharmony_citablet_calculate_arbitration_rect(struct tablet_dispatch *tablet) 1547a46c0ec8Sopenharmony_ci{ 1548a46c0ec8Sopenharmony_ci struct evdev_device *device = tablet->device; 1549a46c0ec8Sopenharmony_ci struct phys_rect r = {0}; 1550a46c0ec8Sopenharmony_ci struct phys_coords mm; 1551a46c0ec8Sopenharmony_ci 1552a46c0ec8Sopenharmony_ci mm = evdev_device_units_to_mm(device, &tablet->axes.point); 1553a46c0ec8Sopenharmony_ci 1554a46c0ec8Sopenharmony_ci /* The rect we disable is 20mm left of the tip, 100mm north of the 1555a46c0ec8Sopenharmony_ci * tip, and 200x250mm large. 1556a46c0ec8Sopenharmony_ci * If the stylus is tilted left (tip further right than the eraser 1557a46c0ec8Sopenharmony_ci * end) assume left-handed mode. 1558a46c0ec8Sopenharmony_ci * 1559a46c0ec8Sopenharmony_ci * Obviously if we'd run out of the boundaries, we clip the rect 1560a46c0ec8Sopenharmony_ci * accordingly. 1561a46c0ec8Sopenharmony_ci */ 1562a46c0ec8Sopenharmony_ci if (tablet->axes.tilt.x > 0) { 1563a46c0ec8Sopenharmony_ci r.x = mm.x - 20; 1564a46c0ec8Sopenharmony_ci r.w = 200; 1565a46c0ec8Sopenharmony_ci } else { 1566a46c0ec8Sopenharmony_ci r.x = mm.x + 20; 1567a46c0ec8Sopenharmony_ci r.w = 200; 1568a46c0ec8Sopenharmony_ci r.x -= r.w; 1569a46c0ec8Sopenharmony_ci } 1570a46c0ec8Sopenharmony_ci 1571a46c0ec8Sopenharmony_ci if (r.x < 0) { 1572a46c0ec8Sopenharmony_ci r.w += r.x; 1573a46c0ec8Sopenharmony_ci r.x = 0; 1574a46c0ec8Sopenharmony_ci } 1575a46c0ec8Sopenharmony_ci 1576a46c0ec8Sopenharmony_ci r.y = mm.y - 100; 1577a46c0ec8Sopenharmony_ci r.h = 250; 1578a46c0ec8Sopenharmony_ci if (r.y < 0) { 1579a46c0ec8Sopenharmony_ci r.h += r.y; 1580a46c0ec8Sopenharmony_ci r.y = 0; 1581a46c0ec8Sopenharmony_ci } 1582a46c0ec8Sopenharmony_ci 1583a46c0ec8Sopenharmony_ci return r; 1584a46c0ec8Sopenharmony_ci} 1585a46c0ec8Sopenharmony_ci 1586a46c0ec8Sopenharmony_cistatic inline void 1587a46c0ec8Sopenharmony_citablet_update_touch_device_rect(struct tablet_dispatch *tablet, 1588a46c0ec8Sopenharmony_ci const struct tablet_axes *axes, 1589a46c0ec8Sopenharmony_ci uint64_t time) 1590a46c0ec8Sopenharmony_ci{ 1591a46c0ec8Sopenharmony_ci struct evdev_dispatch *dispatch; 1592a46c0ec8Sopenharmony_ci struct phys_rect rect = {0}; 1593a46c0ec8Sopenharmony_ci 1594a46c0ec8Sopenharmony_ci if (tablet->touch_device == NULL || 1595a46c0ec8Sopenharmony_ci tablet->arbitration != ARBITRATION_IGNORE_RECT) 1596a46c0ec8Sopenharmony_ci return; 1597a46c0ec8Sopenharmony_ci 1598a46c0ec8Sopenharmony_ci rect = tablet_calculate_arbitration_rect(tablet); 1599a46c0ec8Sopenharmony_ci 1600a46c0ec8Sopenharmony_ci dispatch = tablet->touch_device->dispatch; 1601a46c0ec8Sopenharmony_ci if (dispatch->interface->touch_arbitration_update_rect) 1602a46c0ec8Sopenharmony_ci dispatch->interface->touch_arbitration_update_rect(dispatch, 1603a46c0ec8Sopenharmony_ci tablet->touch_device, 1604a46c0ec8Sopenharmony_ci &rect, 1605a46c0ec8Sopenharmony_ci time); 1606a46c0ec8Sopenharmony_ci} 1607a46c0ec8Sopenharmony_ci 1608a46c0ec8Sopenharmony_cistatic inline bool 1609a46c0ec8Sopenharmony_citablet_send_proximity_in(struct tablet_dispatch *tablet, 1610a46c0ec8Sopenharmony_ci struct libinput_tablet_tool *tool, 1611a46c0ec8Sopenharmony_ci struct evdev_device *device, 1612a46c0ec8Sopenharmony_ci struct tablet_axes *axes, 1613a46c0ec8Sopenharmony_ci uint64_t time) 1614a46c0ec8Sopenharmony_ci{ 1615a46c0ec8Sopenharmony_ci if (!tablet_has_status(tablet, TABLET_TOOL_ENTERING_PROXIMITY)) 1616a46c0ec8Sopenharmony_ci return false; 1617a46c0ec8Sopenharmony_ci 1618a46c0ec8Sopenharmony_ci tablet_notify_proximity(&device->base, 1619a46c0ec8Sopenharmony_ci time, 1620a46c0ec8Sopenharmony_ci tool, 1621a46c0ec8Sopenharmony_ci LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN, 1622a46c0ec8Sopenharmony_ci tablet->changed_axes, 1623a46c0ec8Sopenharmony_ci axes); 1624a46c0ec8Sopenharmony_ci tablet_unset_status(tablet, TABLET_TOOL_ENTERING_PROXIMITY); 1625a46c0ec8Sopenharmony_ci tablet_unset_status(tablet, TABLET_AXES_UPDATED); 1626a46c0ec8Sopenharmony_ci 1627a46c0ec8Sopenharmony_ci tablet_reset_changed_axes(tablet); 1628a46c0ec8Sopenharmony_ci axes->delta.x = 0; 1629a46c0ec8Sopenharmony_ci axes->delta.y = 0; 1630a46c0ec8Sopenharmony_ci 1631a46c0ec8Sopenharmony_ci return true; 1632a46c0ec8Sopenharmony_ci} 1633a46c0ec8Sopenharmony_ci 1634a46c0ec8Sopenharmony_cistatic inline bool 1635a46c0ec8Sopenharmony_citablet_send_proximity_out(struct tablet_dispatch *tablet, 1636a46c0ec8Sopenharmony_ci struct libinput_tablet_tool *tool, 1637a46c0ec8Sopenharmony_ci struct evdev_device *device, 1638a46c0ec8Sopenharmony_ci struct tablet_axes *axes, 1639a46c0ec8Sopenharmony_ci uint64_t time) 1640a46c0ec8Sopenharmony_ci{ 1641a46c0ec8Sopenharmony_ci if (!tablet_has_status(tablet, TABLET_TOOL_LEAVING_PROXIMITY)) 1642a46c0ec8Sopenharmony_ci return false; 1643a46c0ec8Sopenharmony_ci 1644a46c0ec8Sopenharmony_ci tablet_notify_proximity(&device->base, 1645a46c0ec8Sopenharmony_ci time, 1646a46c0ec8Sopenharmony_ci tool, 1647a46c0ec8Sopenharmony_ci LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT, 1648a46c0ec8Sopenharmony_ci tablet->changed_axes, 1649a46c0ec8Sopenharmony_ci axes); 1650a46c0ec8Sopenharmony_ci 1651a46c0ec8Sopenharmony_ci tablet_set_status(tablet, TABLET_TOOL_OUT_OF_PROXIMITY); 1652a46c0ec8Sopenharmony_ci tablet_unset_status(tablet, TABLET_TOOL_LEAVING_PROXIMITY); 1653a46c0ec8Sopenharmony_ci 1654a46c0ec8Sopenharmony_ci tablet_reset_changed_axes(tablet); 1655a46c0ec8Sopenharmony_ci axes->delta.x = 0; 1656a46c0ec8Sopenharmony_ci axes->delta.y = 0; 1657a46c0ec8Sopenharmony_ci 1658a46c0ec8Sopenharmony_ci return true; 1659a46c0ec8Sopenharmony_ci} 1660a46c0ec8Sopenharmony_ci 1661a46c0ec8Sopenharmony_cistatic inline bool 1662a46c0ec8Sopenharmony_citablet_send_tip(struct tablet_dispatch *tablet, 1663a46c0ec8Sopenharmony_ci struct libinput_tablet_tool *tool, 1664a46c0ec8Sopenharmony_ci struct evdev_device *device, 1665a46c0ec8Sopenharmony_ci struct tablet_axes *axes, 1666a46c0ec8Sopenharmony_ci uint64_t time) 1667a46c0ec8Sopenharmony_ci{ 1668a46c0ec8Sopenharmony_ci if (tablet_has_status(tablet, TABLET_TOOL_ENTERING_CONTACT)) { 1669a46c0ec8Sopenharmony_ci tablet_notify_tip(&device->base, 1670a46c0ec8Sopenharmony_ci time, 1671a46c0ec8Sopenharmony_ci tool, 1672a46c0ec8Sopenharmony_ci LIBINPUT_TABLET_TOOL_TIP_DOWN, 1673a46c0ec8Sopenharmony_ci tablet->changed_axes, 1674a46c0ec8Sopenharmony_ci axes); 1675a46c0ec8Sopenharmony_ci tablet_unset_status(tablet, TABLET_AXES_UPDATED); 1676a46c0ec8Sopenharmony_ci tablet_unset_status(tablet, TABLET_TOOL_ENTERING_CONTACT); 1677a46c0ec8Sopenharmony_ci tablet_set_status(tablet, TABLET_TOOL_IN_CONTACT); 1678a46c0ec8Sopenharmony_ci 1679a46c0ec8Sopenharmony_ci tablet_reset_changed_axes(tablet); 1680a46c0ec8Sopenharmony_ci axes->delta.x = 0; 1681a46c0ec8Sopenharmony_ci axes->delta.y = 0; 1682a46c0ec8Sopenharmony_ci 1683a46c0ec8Sopenharmony_ci return true; 1684a46c0ec8Sopenharmony_ci } 1685a46c0ec8Sopenharmony_ci 1686a46c0ec8Sopenharmony_ci if (tablet_has_status(tablet, TABLET_TOOL_LEAVING_CONTACT)) { 1687a46c0ec8Sopenharmony_ci tablet_notify_tip(&device->base, 1688a46c0ec8Sopenharmony_ci time, 1689a46c0ec8Sopenharmony_ci tool, 1690a46c0ec8Sopenharmony_ci LIBINPUT_TABLET_TOOL_TIP_UP, 1691a46c0ec8Sopenharmony_ci tablet->changed_axes, 1692a46c0ec8Sopenharmony_ci axes); 1693a46c0ec8Sopenharmony_ci tablet_unset_status(tablet, TABLET_AXES_UPDATED); 1694a46c0ec8Sopenharmony_ci tablet_unset_status(tablet, TABLET_TOOL_LEAVING_CONTACT); 1695a46c0ec8Sopenharmony_ci tablet_unset_status(tablet, TABLET_TOOL_IN_CONTACT); 1696a46c0ec8Sopenharmony_ci 1697a46c0ec8Sopenharmony_ci tablet_reset_changed_axes(tablet); 1698a46c0ec8Sopenharmony_ci axes->delta.x = 0; 1699a46c0ec8Sopenharmony_ci axes->delta.y = 0; 1700a46c0ec8Sopenharmony_ci 1701a46c0ec8Sopenharmony_ci return true; 1702a46c0ec8Sopenharmony_ci } 1703a46c0ec8Sopenharmony_ci 1704a46c0ec8Sopenharmony_ci return false; 1705a46c0ec8Sopenharmony_ci} 1706a46c0ec8Sopenharmony_ci 1707a46c0ec8Sopenharmony_cistatic inline void 1708a46c0ec8Sopenharmony_citablet_send_axes(struct tablet_dispatch *tablet, 1709a46c0ec8Sopenharmony_ci struct libinput_tablet_tool *tool, 1710a46c0ec8Sopenharmony_ci struct evdev_device *device, 1711a46c0ec8Sopenharmony_ci struct tablet_axes *axes, 1712a46c0ec8Sopenharmony_ci uint64_t time) 1713a46c0ec8Sopenharmony_ci{ 1714a46c0ec8Sopenharmony_ci enum libinput_tablet_tool_tip_state tip_state; 1715a46c0ec8Sopenharmony_ci 1716a46c0ec8Sopenharmony_ci if (!tablet_has_status(tablet, TABLET_AXES_UPDATED)) 1717a46c0ec8Sopenharmony_ci return; 1718a46c0ec8Sopenharmony_ci 1719a46c0ec8Sopenharmony_ci if (tablet_has_status(tablet, 1720a46c0ec8Sopenharmony_ci TABLET_TOOL_IN_CONTACT)) 1721a46c0ec8Sopenharmony_ci tip_state = LIBINPUT_TABLET_TOOL_TIP_DOWN; 1722a46c0ec8Sopenharmony_ci else 1723a46c0ec8Sopenharmony_ci tip_state = LIBINPUT_TABLET_TOOL_TIP_UP; 1724a46c0ec8Sopenharmony_ci 1725a46c0ec8Sopenharmony_ci tablet_notify_axis(&device->base, 1726a46c0ec8Sopenharmony_ci time, 1727a46c0ec8Sopenharmony_ci tool, 1728a46c0ec8Sopenharmony_ci tip_state, 1729a46c0ec8Sopenharmony_ci tablet->changed_axes, 1730a46c0ec8Sopenharmony_ci axes); 1731a46c0ec8Sopenharmony_ci tablet_unset_status(tablet, TABLET_AXES_UPDATED); 1732a46c0ec8Sopenharmony_ci tablet_reset_changed_axes(tablet); 1733a46c0ec8Sopenharmony_ci axes->delta.x = 0; 1734a46c0ec8Sopenharmony_ci axes->delta.y = 0; 1735a46c0ec8Sopenharmony_ci} 1736a46c0ec8Sopenharmony_ci 1737a46c0ec8Sopenharmony_cistatic inline void 1738a46c0ec8Sopenharmony_citablet_send_buttons(struct tablet_dispatch *tablet, 1739a46c0ec8Sopenharmony_ci struct libinput_tablet_tool *tool, 1740a46c0ec8Sopenharmony_ci struct evdev_device *device, 1741a46c0ec8Sopenharmony_ci uint64_t time) 1742a46c0ec8Sopenharmony_ci{ 1743a46c0ec8Sopenharmony_ci if (tablet_has_status(tablet, TABLET_BUTTONS_RELEASED)) { 1744a46c0ec8Sopenharmony_ci tablet_notify_buttons(tablet, 1745a46c0ec8Sopenharmony_ci device, 1746a46c0ec8Sopenharmony_ci time, 1747a46c0ec8Sopenharmony_ci tool, 1748a46c0ec8Sopenharmony_ci LIBINPUT_BUTTON_STATE_RELEASED); 1749a46c0ec8Sopenharmony_ci tablet_unset_status(tablet, TABLET_BUTTONS_RELEASED); 1750a46c0ec8Sopenharmony_ci } 1751a46c0ec8Sopenharmony_ci 1752a46c0ec8Sopenharmony_ci if (tablet_has_status(tablet, TABLET_BUTTONS_PRESSED)) { 1753a46c0ec8Sopenharmony_ci tablet_notify_buttons(tablet, 1754a46c0ec8Sopenharmony_ci device, 1755a46c0ec8Sopenharmony_ci time, 1756a46c0ec8Sopenharmony_ci tool, 1757a46c0ec8Sopenharmony_ci LIBINPUT_BUTTON_STATE_PRESSED); 1758a46c0ec8Sopenharmony_ci tablet_unset_status(tablet, TABLET_BUTTONS_PRESSED); 1759a46c0ec8Sopenharmony_ci } 1760a46c0ec8Sopenharmony_ci} 1761a46c0ec8Sopenharmony_ci 1762a46c0ec8Sopenharmony_cistatic void 1763a46c0ec8Sopenharmony_citablet_send_events(struct tablet_dispatch *tablet, 1764a46c0ec8Sopenharmony_ci struct libinput_tablet_tool *tool, 1765a46c0ec8Sopenharmony_ci struct evdev_device *device, 1766a46c0ec8Sopenharmony_ci uint64_t time) 1767a46c0ec8Sopenharmony_ci{ 1768a46c0ec8Sopenharmony_ci struct tablet_axes axes = {0}; 1769a46c0ec8Sopenharmony_ci 1770a46c0ec8Sopenharmony_ci if (tablet_has_status(tablet, TABLET_TOOL_LEAVING_PROXIMITY)) { 1771a46c0ec8Sopenharmony_ci /* Tool is leaving proximity, we can't rely on the last axis 1772a46c0ec8Sopenharmony_ci * information (it'll be mostly 0), so we just get the 1773a46c0ec8Sopenharmony_ci * current state and skip over updating the axes. 1774a46c0ec8Sopenharmony_ci */ 1775a46c0ec8Sopenharmony_ci axes = tablet->axes; 1776a46c0ec8Sopenharmony_ci 1777a46c0ec8Sopenharmony_ci /* Don't send an axis event, but we may have a tip event 1778a46c0ec8Sopenharmony_ci * update */ 1779a46c0ec8Sopenharmony_ci tablet_unset_status(tablet, TABLET_AXES_UPDATED); 1780a46c0ec8Sopenharmony_ci } else { 1781a46c0ec8Sopenharmony_ci if (tablet_check_notify_axes(tablet, device, tool, &axes, time)) 1782a46c0ec8Sopenharmony_ci tablet_update_touch_device_rect(tablet, &axes, time); 1783a46c0ec8Sopenharmony_ci } 1784a46c0ec8Sopenharmony_ci 1785a46c0ec8Sopenharmony_ci assert(tablet->axes.delta.x == 0); 1786a46c0ec8Sopenharmony_ci assert(tablet->axes.delta.y == 0); 1787a46c0ec8Sopenharmony_ci 1788a46c0ec8Sopenharmony_ci tablet_send_proximity_in(tablet, tool, device, &axes, time); 1789a46c0ec8Sopenharmony_ci if (!tablet_send_tip(tablet, tool, device, &axes, time)) 1790a46c0ec8Sopenharmony_ci tablet_send_axes(tablet, tool, device, &axes, time); 1791a46c0ec8Sopenharmony_ci 1792a46c0ec8Sopenharmony_ci tablet_unset_status(tablet, TABLET_TOOL_ENTERING_CONTACT); 1793a46c0ec8Sopenharmony_ci tablet_reset_changed_axes(tablet); 1794a46c0ec8Sopenharmony_ci 1795a46c0ec8Sopenharmony_ci tablet_send_buttons(tablet, tool, device, time); 1796a46c0ec8Sopenharmony_ci 1797a46c0ec8Sopenharmony_ci if (tablet_send_proximity_out(tablet, tool, device, &axes, time)) { 1798a46c0ec8Sopenharmony_ci tablet_change_to_left_handed(device); 1799a46c0ec8Sopenharmony_ci tablet_apply_rotation(device); 1800a46c0ec8Sopenharmony_ci tablet_history_reset(tablet); 1801a46c0ec8Sopenharmony_ci } 1802a46c0ec8Sopenharmony_ci} 1803a46c0ec8Sopenharmony_ci 1804a46c0ec8Sopenharmony_ci/** 1805a46c0ec8Sopenharmony_ci * Handling for the proximity out workaround. Some tablets only send 1806a46c0ec8Sopenharmony_ci * BTN_TOOL_PEN on the very first event, then leave it set even when the pen 1807a46c0ec8Sopenharmony_ci * leaves the detectable range. To libinput this looks like we always have 1808a46c0ec8Sopenharmony_ci * the pen in proximity. 1809a46c0ec8Sopenharmony_ci * 1810a46c0ec8Sopenharmony_ci * To avoid this, we set a timer on BTN_TOOL_PEN in. We expect the tablet to 1811a46c0ec8Sopenharmony_ci * continuously send events, and while it's doing so we keep updating the 1812a46c0ec8Sopenharmony_ci * timer. Once we go Xms without an event we assume proximity out and inject 1813a46c0ec8Sopenharmony_ci * a BTN_TOOL_PEN event into the sequence through the timer func. 1814a46c0ec8Sopenharmony_ci * 1815a46c0ec8Sopenharmony_ci * We need to remember that we did that, on the first event after the 1816a46c0ec8Sopenharmony_ci * timeout we need to emulate a BTN_TOOL_PEN event again to force proximity 1817a46c0ec8Sopenharmony_ci * in. 1818a46c0ec8Sopenharmony_ci * 1819a46c0ec8Sopenharmony_ci * Other tools never send the BTN_TOOL_PEN event. For those tools, we 1820a46c0ec8Sopenharmony_ci * piggyback along with the proximity out quirks by injecting 1821a46c0ec8Sopenharmony_ci * the event during the first event frame. 1822a46c0ec8Sopenharmony_ci */ 1823a46c0ec8Sopenharmony_cistatic inline void 1824a46c0ec8Sopenharmony_citablet_proximity_out_quirk_set_timer(struct tablet_dispatch *tablet, 1825a46c0ec8Sopenharmony_ci uint64_t time) 1826a46c0ec8Sopenharmony_ci{ 1827a46c0ec8Sopenharmony_ci if (tablet->quirks.need_to_force_prox_out) 1828a46c0ec8Sopenharmony_ci libinput_timer_set(&tablet->quirks.prox_out_timer, 1829a46c0ec8Sopenharmony_ci time + FORCED_PROXOUT_TIMEOUT); 1830a46c0ec8Sopenharmony_ci} 1831a46c0ec8Sopenharmony_ci 1832a46c0ec8Sopenharmony_cistatic bool 1833a46c0ec8Sopenharmony_citablet_update_tool_state(struct tablet_dispatch *tablet, 1834a46c0ec8Sopenharmony_ci struct evdev_device *device, 1835a46c0ec8Sopenharmony_ci uint64_t time) 1836a46c0ec8Sopenharmony_ci{ 1837a46c0ec8Sopenharmony_ci enum libinput_tablet_tool_type type; 1838a46c0ec8Sopenharmony_ci uint32_t changed; 1839a46c0ec8Sopenharmony_ci int state; 1840a46c0ec8Sopenharmony_ci uint32_t doubled_up_new_tool_bit = 0; 1841a46c0ec8Sopenharmony_ci 1842a46c0ec8Sopenharmony_ci /* we were already out of proximity but now got a tool update but 1843a46c0ec8Sopenharmony_ci * our tool state is zero - i.e. we got a valid prox out from the 1844a46c0ec8Sopenharmony_ci * device. 1845a46c0ec8Sopenharmony_ci */ 1846a46c0ec8Sopenharmony_ci if (tablet->quirks.proximity_out_forced && 1847a46c0ec8Sopenharmony_ci tablet_has_status(tablet, TABLET_TOOL_UPDATED) && 1848a46c0ec8Sopenharmony_ci !tablet->tool_state) { 1849a46c0ec8Sopenharmony_ci tablet->quirks.need_to_force_prox_out = false; 1850a46c0ec8Sopenharmony_ci tablet->quirks.proximity_out_forced = false; 1851a46c0ec8Sopenharmony_ci } 1852a46c0ec8Sopenharmony_ci /* We need to emulate a BTN_TOOL_PEN if we get an axis event (i.e. 1853a46c0ec8Sopenharmony_ci * stylus is def. in proximity) and: 1854a46c0ec8Sopenharmony_ci * - we forced a proximity out before, or 1855a46c0ec8Sopenharmony_ci * - on the very first event after init, because if we didn't get a 1856a46c0ec8Sopenharmony_ci * BTN_TOOL_PEN and the state for the tool was 0, this device will 1857a46c0ec8Sopenharmony_ci * never send the event. 1858a46c0ec8Sopenharmony_ci * We don't do this for pure button events because we discard those. 1859a46c0ec8Sopenharmony_ci * 1860a46c0ec8Sopenharmony_ci * But: on some devices the proximity out is delayed by the kernel, 1861a46c0ec8Sopenharmony_ci * so we get it after our forced prox-out has triggered. In that 1862a46c0ec8Sopenharmony_ci * case we need to just ignore the change. 1863a46c0ec8Sopenharmony_ci */ 1864a46c0ec8Sopenharmony_ci if (tablet_has_status(tablet, TABLET_AXES_UPDATED)) { 1865a46c0ec8Sopenharmony_ci if (tablet->quirks.proximity_out_forced) { 1866a46c0ec8Sopenharmony_ci if (!tablet_has_status(tablet, TABLET_TOOL_UPDATED) && 1867a46c0ec8Sopenharmony_ci !tablet->tool_state) 1868a46c0ec8Sopenharmony_ci tablet->tool_state = bit(LIBINPUT_TABLET_TOOL_TYPE_PEN); 1869a46c0ec8Sopenharmony_ci tablet->quirks.proximity_out_forced = false; 1870a46c0ec8Sopenharmony_ci } else if (tablet->tool_state == 0 && 1871a46c0ec8Sopenharmony_ci tablet->current_tool.type == LIBINPUT_TOOL_NONE) { 1872a46c0ec8Sopenharmony_ci tablet->tool_state = bit(LIBINPUT_TABLET_TOOL_TYPE_PEN); 1873a46c0ec8Sopenharmony_ci tablet->quirks.proximity_out_forced = false; 1874a46c0ec8Sopenharmony_ci } 1875a46c0ec8Sopenharmony_ci } 1876a46c0ec8Sopenharmony_ci 1877a46c0ec8Sopenharmony_ci if (tablet->tool_state == tablet->prev_tool_state) 1878a46c0ec8Sopenharmony_ci return false; 1879a46c0ec8Sopenharmony_ci 1880a46c0ec8Sopenharmony_ci /* Kernel tools are supposed to be mutually exclusive, but we may have 1881a46c0ec8Sopenharmony_ci * two bits set due to firmware/kernel bugs. 1882a46c0ec8Sopenharmony_ci * Two cases that have been seen in the wild: 1883a46c0ec8Sopenharmony_ci * - BTN_TOOL_PEN on proximity in, followed by 1884a46c0ec8Sopenharmony_ci * BTN_TOOL_RUBBER later, see #259 1885a46c0ec8Sopenharmony_ci * -> We force a prox-out of the pen, trigger prox-in for eraser 1886a46c0ec8Sopenharmony_ci * - BTN_TOOL_RUBBER on proximity in, but BTN_TOOL_PEN when 1887a46c0ec8Sopenharmony_ci * the tip is down, see #702. 1888a46c0ec8Sopenharmony_ci * -> We ignore BTN_TOOL_PEN 1889a46c0ec8Sopenharmony_ci * In both cases the eraser is what we want, so we bias 1890a46c0ec8Sopenharmony_ci * towards that. 1891a46c0ec8Sopenharmony_ci */ 1892a46c0ec8Sopenharmony_ci if (tablet->tool_state & (tablet->tool_state - 1)) { 1893a46c0ec8Sopenharmony_ci doubled_up_new_tool_bit = tablet->tool_state ^ tablet->prev_tool_state; 1894a46c0ec8Sopenharmony_ci 1895a46c0ec8Sopenharmony_ci /* The new tool is the pen. Ignore it */ 1896a46c0ec8Sopenharmony_ci if (doubled_up_new_tool_bit == bit(LIBINPUT_TABLET_TOOL_TYPE_PEN)) { 1897a46c0ec8Sopenharmony_ci tablet->tool_state &= ~bit(LIBINPUT_TABLET_TOOL_TYPE_PEN); 1898a46c0ec8Sopenharmony_ci return false; 1899a46c0ec8Sopenharmony_ci } 1900a46c0ec8Sopenharmony_ci 1901a46c0ec8Sopenharmony_ci /* The new tool is some tool other than pen (usually eraser). 1902a46c0ec8Sopenharmony_ci * We set the current tool state to zero, thus setting 1903a46c0ec8Sopenharmony_ci * everything up for a prox out on the tool. Once that is set 1904a46c0ec8Sopenharmony_ci * up, we change the tool state to be the new one we just got. 1905a46c0ec8Sopenharmony_ci * When we re-process this function we now get the new tool 1906a46c0ec8Sopenharmony_ci * as prox in. Importantly, we basically rely on nothing else 1907a46c0ec8Sopenharmony_ci * happening in the meantime. 1908a46c0ec8Sopenharmony_ci */ 1909a46c0ec8Sopenharmony_ci tablet->tool_state = 0; 1910a46c0ec8Sopenharmony_ci } 1911a46c0ec8Sopenharmony_ci 1912a46c0ec8Sopenharmony_ci changed = tablet->tool_state ^ tablet->prev_tool_state; 1913a46c0ec8Sopenharmony_ci type = ffs(changed) - 1; 1914a46c0ec8Sopenharmony_ci state = !!(tablet->tool_state & bit(type)); 1915a46c0ec8Sopenharmony_ci 1916a46c0ec8Sopenharmony_ci tablet_update_tool(tablet, device, type, state); 1917a46c0ec8Sopenharmony_ci 1918a46c0ec8Sopenharmony_ci /* The proximity timeout is only needed for BTN_TOOL_PEN, devices 1919a46c0ec8Sopenharmony_ci * that require it don't do erasers */ 1920a46c0ec8Sopenharmony_ci if (type == LIBINPUT_TABLET_TOOL_TYPE_PEN) { 1921a46c0ec8Sopenharmony_ci if (state) { 1922a46c0ec8Sopenharmony_ci tablet_proximity_out_quirk_set_timer(tablet, time); 1923a46c0ec8Sopenharmony_ci } else { 1924a46c0ec8Sopenharmony_ci /* If we get a BTN_TOOL_PEN 0 when *not* injecting 1925a46c0ec8Sopenharmony_ci * events it means the tablet will give us the right 1926a46c0ec8Sopenharmony_ci * events after all and we can disable our 1927a46c0ec8Sopenharmony_ci * timer-based proximity out. 1928a46c0ec8Sopenharmony_ci */ 1929a46c0ec8Sopenharmony_ci if (!tablet->quirks.proximity_out_in_progress) 1930a46c0ec8Sopenharmony_ci tablet->quirks.need_to_force_prox_out = false; 1931a46c0ec8Sopenharmony_ci 1932a46c0ec8Sopenharmony_ci libinput_timer_cancel(&tablet->quirks.prox_out_timer); 1933a46c0ec8Sopenharmony_ci } 1934a46c0ec8Sopenharmony_ci } 1935a46c0ec8Sopenharmony_ci 1936a46c0ec8Sopenharmony_ci tablet->prev_tool_state = tablet->tool_state; 1937a46c0ec8Sopenharmony_ci 1938a46c0ec8Sopenharmony_ci if (doubled_up_new_tool_bit) { 1939a46c0ec8Sopenharmony_ci tablet->tool_state = doubled_up_new_tool_bit; 1940a46c0ec8Sopenharmony_ci return true; /* need to re-process */ 1941a46c0ec8Sopenharmony_ci } 1942a46c0ec8Sopenharmony_ci return false; 1943a46c0ec8Sopenharmony_ci} 1944a46c0ec8Sopenharmony_ci 1945a46c0ec8Sopenharmony_cistatic struct libinput_tablet_tool * 1946a46c0ec8Sopenharmony_citablet_get_current_tool(struct tablet_dispatch *tablet) 1947a46c0ec8Sopenharmony_ci{ 1948a46c0ec8Sopenharmony_ci if (tablet->current_tool.type == LIBINPUT_TOOL_NONE) 1949a46c0ec8Sopenharmony_ci return NULL; 1950a46c0ec8Sopenharmony_ci 1951a46c0ec8Sopenharmony_ci return tablet_get_tool(tablet, 1952a46c0ec8Sopenharmony_ci tablet->current_tool.type, 1953a46c0ec8Sopenharmony_ci tablet->current_tool.id, 1954a46c0ec8Sopenharmony_ci tablet->current_tool.serial); 1955a46c0ec8Sopenharmony_ci} 1956a46c0ec8Sopenharmony_ci 1957a46c0ec8Sopenharmony_cistatic void 1958a46c0ec8Sopenharmony_citablet_flush(struct tablet_dispatch *tablet, 1959a46c0ec8Sopenharmony_ci struct evdev_device *device, 1960a46c0ec8Sopenharmony_ci uint64_t time) 1961a46c0ec8Sopenharmony_ci{ 1962a46c0ec8Sopenharmony_ci struct libinput_tablet_tool *tool; 1963a46c0ec8Sopenharmony_ci bool process_tool_twice; 1964a46c0ec8Sopenharmony_ci 1965a46c0ec8Sopenharmony_cireprocess: 1966a46c0ec8Sopenharmony_ci process_tool_twice = tablet_update_tool_state(tablet, device, time); 1967a46c0ec8Sopenharmony_ci 1968a46c0ec8Sopenharmony_ci tool = tablet_get_current_tool(tablet); 1969a46c0ec8Sopenharmony_ci if (!tool) 1970a46c0ec8Sopenharmony_ci return; /* OOM */ 1971a46c0ec8Sopenharmony_ci 1972a46c0ec8Sopenharmony_ci if (tool->type == LIBINPUT_TABLET_TOOL_TYPE_MOUSE || 1973a46c0ec8Sopenharmony_ci tool->type == LIBINPUT_TABLET_TOOL_TYPE_LENS) 1974a46c0ec8Sopenharmony_ci tablet_update_proximity_state(tablet, device, tool); 1975a46c0ec8Sopenharmony_ci 1976a46c0ec8Sopenharmony_ci if (tablet_has_status(tablet, TABLET_TOOL_OUT_OF_PROXIMITY) || 1977a46c0ec8Sopenharmony_ci tablet_has_status(tablet, TABLET_TOOL_OUT_OF_RANGE)) 1978a46c0ec8Sopenharmony_ci return; 1979a46c0ec8Sopenharmony_ci 1980a46c0ec8Sopenharmony_ci if (tablet_has_status(tablet, TABLET_TOOL_LEAVING_PROXIMITY)) { 1981a46c0ec8Sopenharmony_ci /* Release all stylus buttons */ 1982a46c0ec8Sopenharmony_ci memset(tablet->button_state.bits, 1983a46c0ec8Sopenharmony_ci 0, 1984a46c0ec8Sopenharmony_ci sizeof(tablet->button_state.bits)); 1985a46c0ec8Sopenharmony_ci tablet_set_status(tablet, TABLET_BUTTONS_RELEASED); 1986a46c0ec8Sopenharmony_ci if (tablet_has_status(tablet, TABLET_TOOL_IN_CONTACT)) 1987a46c0ec8Sopenharmony_ci tablet_set_status(tablet, TABLET_TOOL_LEAVING_CONTACT); 1988a46c0ec8Sopenharmony_ci } else if (tablet_has_status(tablet, TABLET_TOOL_ENTERING_PROXIMITY)) { 1989a46c0ec8Sopenharmony_ci tablet_mark_all_axes_changed(tablet, tool); 1990a46c0ec8Sopenharmony_ci update_pressure_offset(tablet, device, tool); 1991a46c0ec8Sopenharmony_ci detect_pressure_offset(tablet, device, tool); 1992a46c0ec8Sopenharmony_ci detect_tool_contact(tablet, device, tool); 1993a46c0ec8Sopenharmony_ci sanitize_tablet_axes(tablet, tool); 1994a46c0ec8Sopenharmony_ci } else if (tablet_has_status(tablet, TABLET_AXES_UPDATED)) { 1995a46c0ec8Sopenharmony_ci update_pressure_offset(tablet, device, tool); 1996a46c0ec8Sopenharmony_ci detect_tool_contact(tablet, device, tool); 1997a46c0ec8Sopenharmony_ci sanitize_tablet_axes(tablet, tool); 1998a46c0ec8Sopenharmony_ci } 1999a46c0ec8Sopenharmony_ci 2000a46c0ec8Sopenharmony_ci tablet_send_events(tablet, tool, device, time); 2001a46c0ec8Sopenharmony_ci 2002a46c0ec8Sopenharmony_ci if (process_tool_twice) 2003a46c0ec8Sopenharmony_ci goto reprocess; 2004a46c0ec8Sopenharmony_ci} 2005a46c0ec8Sopenharmony_ci 2006a46c0ec8Sopenharmony_cistatic inline void 2007a46c0ec8Sopenharmony_citablet_set_touch_device_enabled(struct tablet_dispatch *tablet, 2008a46c0ec8Sopenharmony_ci enum evdev_arbitration_state which, 2009a46c0ec8Sopenharmony_ci const struct phys_rect *rect, 2010a46c0ec8Sopenharmony_ci uint64_t time) 2011a46c0ec8Sopenharmony_ci{ 2012a46c0ec8Sopenharmony_ci struct evdev_device *touch_device = tablet->touch_device; 2013a46c0ec8Sopenharmony_ci struct evdev_dispatch *dispatch; 2014a46c0ec8Sopenharmony_ci 2015a46c0ec8Sopenharmony_ci if (touch_device == NULL) 2016a46c0ec8Sopenharmony_ci return; 2017a46c0ec8Sopenharmony_ci 2018a46c0ec8Sopenharmony_ci tablet->arbitration = which; 2019a46c0ec8Sopenharmony_ci 2020a46c0ec8Sopenharmony_ci dispatch = touch_device->dispatch; 2021a46c0ec8Sopenharmony_ci if (dispatch->interface->touch_arbitration_toggle) 2022a46c0ec8Sopenharmony_ci dispatch->interface->touch_arbitration_toggle(dispatch, 2023a46c0ec8Sopenharmony_ci touch_device, 2024a46c0ec8Sopenharmony_ci which, 2025a46c0ec8Sopenharmony_ci rect, 2026a46c0ec8Sopenharmony_ci time); 2027a46c0ec8Sopenharmony_ci} 2028a46c0ec8Sopenharmony_ci 2029a46c0ec8Sopenharmony_cistatic inline void 2030a46c0ec8Sopenharmony_citablet_toggle_touch_device(struct tablet_dispatch *tablet, 2031a46c0ec8Sopenharmony_ci struct evdev_device *tablet_device, 2032a46c0ec8Sopenharmony_ci uint64_t time) 2033a46c0ec8Sopenharmony_ci{ 2034a46c0ec8Sopenharmony_ci enum evdev_arbitration_state which; 2035a46c0ec8Sopenharmony_ci struct phys_rect r = {0}; 2036a46c0ec8Sopenharmony_ci struct phys_rect *rect = NULL; 2037a46c0ec8Sopenharmony_ci 2038a46c0ec8Sopenharmony_ci if (tablet_has_status(tablet, 2039a46c0ec8Sopenharmony_ci TABLET_TOOL_OUT_OF_RANGE) || 2040a46c0ec8Sopenharmony_ci tablet_has_status(tablet, TABLET_NONE) || 2041a46c0ec8Sopenharmony_ci tablet_has_status(tablet, 2042a46c0ec8Sopenharmony_ci TABLET_TOOL_LEAVING_PROXIMITY) || 2043a46c0ec8Sopenharmony_ci tablet_has_status(tablet, 2044a46c0ec8Sopenharmony_ci TABLET_TOOL_OUT_OF_PROXIMITY)) { 2045a46c0ec8Sopenharmony_ci which = ARBITRATION_NOT_ACTIVE; 2046a46c0ec8Sopenharmony_ci } else if (tablet->axes.tilt.x == 0) { 2047a46c0ec8Sopenharmony_ci which = ARBITRATION_IGNORE_ALL; 2048a46c0ec8Sopenharmony_ci } else if (tablet->arbitration != ARBITRATION_IGNORE_RECT) { 2049a46c0ec8Sopenharmony_ci /* This enables rect-based arbitration, updates are sent 2050a46c0ec8Sopenharmony_ci * elsewhere */ 2051a46c0ec8Sopenharmony_ci r = tablet_calculate_arbitration_rect(tablet); 2052a46c0ec8Sopenharmony_ci rect = &r; 2053a46c0ec8Sopenharmony_ci which = ARBITRATION_IGNORE_RECT; 2054a46c0ec8Sopenharmony_ci } else { 2055a46c0ec8Sopenharmony_ci return; 2056a46c0ec8Sopenharmony_ci } 2057a46c0ec8Sopenharmony_ci 2058a46c0ec8Sopenharmony_ci tablet_set_touch_device_enabled(tablet, 2059a46c0ec8Sopenharmony_ci which, 2060a46c0ec8Sopenharmony_ci rect, 2061a46c0ec8Sopenharmony_ci time); 2062a46c0ec8Sopenharmony_ci} 2063a46c0ec8Sopenharmony_ci 2064a46c0ec8Sopenharmony_cistatic inline void 2065a46c0ec8Sopenharmony_citablet_reset_state(struct tablet_dispatch *tablet) 2066a46c0ec8Sopenharmony_ci{ 2067a46c0ec8Sopenharmony_ci struct button_state zero = {0}; 2068a46c0ec8Sopenharmony_ci 2069a46c0ec8Sopenharmony_ci /* Update state */ 2070a46c0ec8Sopenharmony_ci memcpy(&tablet->prev_button_state, 2071a46c0ec8Sopenharmony_ci &tablet->button_state, 2072a46c0ec8Sopenharmony_ci sizeof(tablet->button_state)); 2073a46c0ec8Sopenharmony_ci tablet_unset_status(tablet, TABLET_TOOL_UPDATED); 2074a46c0ec8Sopenharmony_ci 2075a46c0ec8Sopenharmony_ci if (memcmp(&tablet->button_state, &zero, sizeof(zero)) == 0) 2076a46c0ec8Sopenharmony_ci tablet_unset_status(tablet, TABLET_BUTTONS_DOWN); 2077a46c0ec8Sopenharmony_ci else 2078a46c0ec8Sopenharmony_ci tablet_set_status(tablet, TABLET_BUTTONS_DOWN); 2079a46c0ec8Sopenharmony_ci} 2080a46c0ec8Sopenharmony_ci 2081a46c0ec8Sopenharmony_cistatic void 2082a46c0ec8Sopenharmony_citablet_proximity_out_quirk_timer_func(uint64_t now, void *data) 2083a46c0ec8Sopenharmony_ci{ 2084a46c0ec8Sopenharmony_ci struct tablet_dispatch *tablet = data; 2085a46c0ec8Sopenharmony_ci struct timeval tv = us2tv(now); 2086a46c0ec8Sopenharmony_ci struct input_event events[2] = { 2087a46c0ec8Sopenharmony_ci { .input_event_sec = tv.tv_sec, 2088a46c0ec8Sopenharmony_ci .input_event_usec = tv.tv_usec, 2089a46c0ec8Sopenharmony_ci .type = EV_KEY, 2090a46c0ec8Sopenharmony_ci .code = BTN_TOOL_PEN, 2091a46c0ec8Sopenharmony_ci .value = 0 }, 2092a46c0ec8Sopenharmony_ci { .input_event_sec = tv.tv_sec, 2093a46c0ec8Sopenharmony_ci .input_event_usec = tv.tv_usec, 2094a46c0ec8Sopenharmony_ci .type = EV_SYN, 2095a46c0ec8Sopenharmony_ci .code = SYN_REPORT, 2096a46c0ec8Sopenharmony_ci .value = 0 }, 2097a46c0ec8Sopenharmony_ci }; 2098a46c0ec8Sopenharmony_ci 2099a46c0ec8Sopenharmony_ci if (tablet_has_status(tablet, TABLET_TOOL_IN_CONTACT) || 2100a46c0ec8Sopenharmony_ci tablet_has_status(tablet, TABLET_BUTTONS_DOWN)) { 2101a46c0ec8Sopenharmony_ci tablet_proximity_out_quirk_set_timer(tablet, now); 2102a46c0ec8Sopenharmony_ci return; 2103a46c0ec8Sopenharmony_ci } 2104a46c0ec8Sopenharmony_ci 2105a46c0ec8Sopenharmony_ci if (tablet->quirks.last_event_time > now - FORCED_PROXOUT_TIMEOUT) { 2106a46c0ec8Sopenharmony_ci tablet_proximity_out_quirk_set_timer(tablet, 2107a46c0ec8Sopenharmony_ci tablet->quirks.last_event_time); 2108a46c0ec8Sopenharmony_ci return; 2109a46c0ec8Sopenharmony_ci } 2110a46c0ec8Sopenharmony_ci 2111a46c0ec8Sopenharmony_ci evdev_log_debug(tablet->device, "tablet: forcing proximity after timeout\n"); 2112a46c0ec8Sopenharmony_ci 2113a46c0ec8Sopenharmony_ci tablet->quirks.proximity_out_in_progress = true; 2114a46c0ec8Sopenharmony_ci ARRAY_FOR_EACH(events, e) { 2115a46c0ec8Sopenharmony_ci tablet->base.interface->process(&tablet->base, 2116a46c0ec8Sopenharmony_ci tablet->device, 2117a46c0ec8Sopenharmony_ci e, 2118a46c0ec8Sopenharmony_ci now); 2119a46c0ec8Sopenharmony_ci } 2120a46c0ec8Sopenharmony_ci tablet->quirks.proximity_out_in_progress = false; 2121a46c0ec8Sopenharmony_ci 2122a46c0ec8Sopenharmony_ci tablet->quirks.proximity_out_forced = true; 2123a46c0ec8Sopenharmony_ci} 2124a46c0ec8Sopenharmony_ci 2125a46c0ec8Sopenharmony_cistatic void 2126a46c0ec8Sopenharmony_citablet_process(struct evdev_dispatch *dispatch, 2127a46c0ec8Sopenharmony_ci struct evdev_device *device, 2128a46c0ec8Sopenharmony_ci struct input_event *e, 2129a46c0ec8Sopenharmony_ci uint64_t time) 2130a46c0ec8Sopenharmony_ci{ 2131a46c0ec8Sopenharmony_ci struct tablet_dispatch *tablet = tablet_dispatch(dispatch); 2132a46c0ec8Sopenharmony_ci 2133a46c0ec8Sopenharmony_ci switch (e->type) { 2134a46c0ec8Sopenharmony_ci case EV_ABS: 2135a46c0ec8Sopenharmony_ci tablet_process_absolute(tablet, device, e, time); 2136a46c0ec8Sopenharmony_ci break; 2137a46c0ec8Sopenharmony_ci case EV_REL: 2138a46c0ec8Sopenharmony_ci tablet_process_relative(tablet, device, e, time); 2139a46c0ec8Sopenharmony_ci break; 2140a46c0ec8Sopenharmony_ci case EV_KEY: 2141a46c0ec8Sopenharmony_ci tablet_process_key(tablet, device, e, time); 2142a46c0ec8Sopenharmony_ci break; 2143a46c0ec8Sopenharmony_ci case EV_MSC: 2144a46c0ec8Sopenharmony_ci tablet_process_misc(tablet, device, e, time); 2145a46c0ec8Sopenharmony_ci break; 2146a46c0ec8Sopenharmony_ci case EV_SYN: 2147a46c0ec8Sopenharmony_ci tablet_flush(tablet, device, time); 2148a46c0ec8Sopenharmony_ci tablet_toggle_touch_device(tablet, device, time); 2149a46c0ec8Sopenharmony_ci tablet_reset_state(tablet); 2150a46c0ec8Sopenharmony_ci tablet->quirks.last_event_time = time; 2151a46c0ec8Sopenharmony_ci break; 2152a46c0ec8Sopenharmony_ci default: 2153a46c0ec8Sopenharmony_ci evdev_log_error(device, 2154a46c0ec8Sopenharmony_ci "Unexpected event type %s (%#x)\n", 2155a46c0ec8Sopenharmony_ci libevdev_event_type_get_name(e->type), 2156a46c0ec8Sopenharmony_ci e->type); 2157a46c0ec8Sopenharmony_ci break; 2158a46c0ec8Sopenharmony_ci } 2159a46c0ec8Sopenharmony_ci} 2160a46c0ec8Sopenharmony_ci 2161a46c0ec8Sopenharmony_cistatic void 2162a46c0ec8Sopenharmony_citablet_suspend(struct evdev_dispatch *dispatch, 2163a46c0ec8Sopenharmony_ci struct evdev_device *device) 2164a46c0ec8Sopenharmony_ci{ 2165a46c0ec8Sopenharmony_ci struct tablet_dispatch *tablet = tablet_dispatch(dispatch); 2166a46c0ec8Sopenharmony_ci struct libinput *li = tablet_libinput_context(tablet); 2167a46c0ec8Sopenharmony_ci uint64_t now = libinput_now(li); 2168a46c0ec8Sopenharmony_ci 2169a46c0ec8Sopenharmony_ci tablet_set_touch_device_enabled(tablet, 2170a46c0ec8Sopenharmony_ci ARBITRATION_NOT_ACTIVE, 2171a46c0ec8Sopenharmony_ci NULL, 2172a46c0ec8Sopenharmony_ci now); 2173a46c0ec8Sopenharmony_ci 2174a46c0ec8Sopenharmony_ci if (!tablet_has_status(tablet, TABLET_TOOL_OUT_OF_PROXIMITY)) { 2175a46c0ec8Sopenharmony_ci tablet_set_status(tablet, TABLET_TOOL_LEAVING_PROXIMITY); 2176a46c0ec8Sopenharmony_ci tablet_flush(tablet, device, libinput_now(li)); 2177a46c0ec8Sopenharmony_ci } 2178a46c0ec8Sopenharmony_ci} 2179a46c0ec8Sopenharmony_ci 2180a46c0ec8Sopenharmony_cistatic void 2181a46c0ec8Sopenharmony_citablet_destroy(struct evdev_dispatch *dispatch) 2182a46c0ec8Sopenharmony_ci{ 2183a46c0ec8Sopenharmony_ci struct tablet_dispatch *tablet = tablet_dispatch(dispatch); 2184a46c0ec8Sopenharmony_ci struct libinput_tablet_tool *tool; 2185a46c0ec8Sopenharmony_ci struct libinput *li = tablet_libinput_context(tablet); 2186a46c0ec8Sopenharmony_ci 2187a46c0ec8Sopenharmony_ci libinput_timer_cancel(&tablet->quirks.prox_out_timer); 2188a46c0ec8Sopenharmony_ci libinput_timer_destroy(&tablet->quirks.prox_out_timer); 2189a46c0ec8Sopenharmony_ci 2190a46c0ec8Sopenharmony_ci list_for_each_safe(tool, &tablet->tool_list, link) { 2191a46c0ec8Sopenharmony_ci libinput_tablet_tool_unref(tool); 2192a46c0ec8Sopenharmony_ci } 2193a46c0ec8Sopenharmony_ci 2194a46c0ec8Sopenharmony_ci libinput_libwacom_unref(li); 2195a46c0ec8Sopenharmony_ci 2196a46c0ec8Sopenharmony_ci free(tablet); 2197a46c0ec8Sopenharmony_ci} 2198a46c0ec8Sopenharmony_ci 2199a46c0ec8Sopenharmony_cistatic void 2200a46c0ec8Sopenharmony_citablet_setup_touch_arbitration(struct evdev_device *device, 2201a46c0ec8Sopenharmony_ci struct evdev_device *new_device) 2202a46c0ec8Sopenharmony_ci{ 2203a46c0ec8Sopenharmony_ci struct tablet_dispatch *tablet = tablet_dispatch(device->dispatch); 2204a46c0ec8Sopenharmony_ci 2205a46c0ec8Sopenharmony_ci /* We enable touch arbitration with the first touch screen/external 2206a46c0ec8Sopenharmony_ci * touchpad we see. This may be wrong in some cases, so we have some 2207a46c0ec8Sopenharmony_ci * heuristics in case we find a "better" device. 2208a46c0ec8Sopenharmony_ci */ 2209a46c0ec8Sopenharmony_ci if (tablet->touch_device != NULL) { 2210a46c0ec8Sopenharmony_ci struct libinput_device_group *group1 = libinput_device_get_device_group(&device->base); 2211a46c0ec8Sopenharmony_ci struct libinput_device_group *group2 = libinput_device_get_device_group(&new_device->base); 2212a46c0ec8Sopenharmony_ci 2213a46c0ec8Sopenharmony_ci /* same phsical device? -> better, otherwise keep the one we have */ 2214a46c0ec8Sopenharmony_ci if (group1 != group2) 2215a46c0ec8Sopenharmony_ci return; 2216a46c0ec8Sopenharmony_ci 2217a46c0ec8Sopenharmony_ci /* We found a better device, let's swap it out */ 2218a46c0ec8Sopenharmony_ci struct libinput *li = tablet_libinput_context(tablet); 2219a46c0ec8Sopenharmony_ci tablet_set_touch_device_enabled(tablet, 2220a46c0ec8Sopenharmony_ci ARBITRATION_NOT_ACTIVE, 2221a46c0ec8Sopenharmony_ci NULL, 2222a46c0ec8Sopenharmony_ci libinput_now(li)); 2223a46c0ec8Sopenharmony_ci evdev_log_debug(device, 2224a46c0ec8Sopenharmony_ci "touch-arbitration: removing pairing for %s<->%s\n", 2225a46c0ec8Sopenharmony_ci device->devname, 2226a46c0ec8Sopenharmony_ci tablet->touch_device->devname); 2227a46c0ec8Sopenharmony_ci } 2228a46c0ec8Sopenharmony_ci 2229a46c0ec8Sopenharmony_ci evdev_log_debug(device, 2230a46c0ec8Sopenharmony_ci "touch-arbitration: activated for %s<->%s\n", 2231a46c0ec8Sopenharmony_ci device->devname, 2232a46c0ec8Sopenharmony_ci new_device->devname); 2233a46c0ec8Sopenharmony_ci tablet->touch_device = new_device; 2234a46c0ec8Sopenharmony_ci} 2235a46c0ec8Sopenharmony_ci 2236a46c0ec8Sopenharmony_cistatic void 2237a46c0ec8Sopenharmony_citablet_setup_rotation(struct evdev_device *device, 2238a46c0ec8Sopenharmony_ci struct evdev_device *new_device) 2239a46c0ec8Sopenharmony_ci{ 2240a46c0ec8Sopenharmony_ci struct tablet_dispatch *tablet = tablet_dispatch(device->dispatch); 2241a46c0ec8Sopenharmony_ci struct libinput_device_group *group1 = libinput_device_get_device_group(&device->base); 2242a46c0ec8Sopenharmony_ci struct libinput_device_group *group2 = libinput_device_get_device_group(&new_device->base); 2243a46c0ec8Sopenharmony_ci 2244a46c0ec8Sopenharmony_ci if (tablet->rotation.touch_device == NULL && (group1 == group2)) { 2245a46c0ec8Sopenharmony_ci evdev_log_debug(device, 2246a46c0ec8Sopenharmony_ci "tablet-rotation: %s will rotate %s\n", 2247a46c0ec8Sopenharmony_ci device->devname, 2248a46c0ec8Sopenharmony_ci new_device->devname); 2249a46c0ec8Sopenharmony_ci tablet->rotation.touch_device = new_device; 2250a46c0ec8Sopenharmony_ci 2251a46c0ec8Sopenharmony_ci if (libinput_device_config_left_handed_get(&new_device->base)) { 2252a46c0ec8Sopenharmony_ci tablet->rotation.touch_device_left_handed_state = true; 2253a46c0ec8Sopenharmony_ci tablet_change_rotation(device, DO_NOTIFY); 2254a46c0ec8Sopenharmony_ci } 2255a46c0ec8Sopenharmony_ci } 2256a46c0ec8Sopenharmony_ci} 2257a46c0ec8Sopenharmony_ci 2258a46c0ec8Sopenharmony_cistatic void 2259a46c0ec8Sopenharmony_citablet_device_added(struct evdev_device *device, 2260a46c0ec8Sopenharmony_ci struct evdev_device *added_device) 2261a46c0ec8Sopenharmony_ci{ 2262a46c0ec8Sopenharmony_ci bool is_touchscreen, is_ext_touchpad; 2263a46c0ec8Sopenharmony_ci 2264a46c0ec8Sopenharmony_ci is_touchscreen = evdev_device_has_capability(added_device, 2265a46c0ec8Sopenharmony_ci LIBINPUT_DEVICE_CAP_TOUCH); 2266a46c0ec8Sopenharmony_ci is_ext_touchpad = evdev_device_has_capability(added_device, 2267a46c0ec8Sopenharmony_ci LIBINPUT_DEVICE_CAP_POINTER) && 2268a46c0ec8Sopenharmony_ci (added_device->tags & EVDEV_TAG_EXTERNAL_TOUCHPAD); 2269a46c0ec8Sopenharmony_ci 2270a46c0ec8Sopenharmony_ci if (is_touchscreen || is_ext_touchpad) 2271a46c0ec8Sopenharmony_ci tablet_setup_touch_arbitration(device, added_device); 2272a46c0ec8Sopenharmony_ci 2273a46c0ec8Sopenharmony_ci if (is_ext_touchpad) 2274a46c0ec8Sopenharmony_ci tablet_setup_rotation(device, added_device); 2275a46c0ec8Sopenharmony_ci} 2276a46c0ec8Sopenharmony_ci 2277a46c0ec8Sopenharmony_cistatic void 2278a46c0ec8Sopenharmony_citablet_device_removed(struct evdev_device *device, 2279a46c0ec8Sopenharmony_ci struct evdev_device *removed_device) 2280a46c0ec8Sopenharmony_ci{ 2281a46c0ec8Sopenharmony_ci struct tablet_dispatch *tablet = tablet_dispatch(device->dispatch); 2282a46c0ec8Sopenharmony_ci 2283a46c0ec8Sopenharmony_ci if (tablet->touch_device == removed_device) 2284a46c0ec8Sopenharmony_ci tablet->touch_device = NULL; 2285a46c0ec8Sopenharmony_ci 2286a46c0ec8Sopenharmony_ci if (tablet->rotation.touch_device == removed_device) { 2287a46c0ec8Sopenharmony_ci tablet->rotation.touch_device = NULL; 2288a46c0ec8Sopenharmony_ci tablet->rotation.touch_device_left_handed_state = false; 2289a46c0ec8Sopenharmony_ci tablet_change_rotation(device, DO_NOTIFY); 2290a46c0ec8Sopenharmony_ci } 2291a46c0ec8Sopenharmony_ci} 2292a46c0ec8Sopenharmony_ci 2293a46c0ec8Sopenharmony_cistatic void 2294a46c0ec8Sopenharmony_citablet_check_initial_proximity(struct evdev_device *device, 2295a46c0ec8Sopenharmony_ci struct evdev_dispatch *dispatch) 2296a46c0ec8Sopenharmony_ci{ 2297a46c0ec8Sopenharmony_ci struct tablet_dispatch *tablet = tablet_dispatch(dispatch); 2298a46c0ec8Sopenharmony_ci struct libinput *li = tablet_libinput_context(tablet); 2299a46c0ec8Sopenharmony_ci int code, state; 2300a46c0ec8Sopenharmony_ci enum libinput_tablet_tool_type tool; 2301a46c0ec8Sopenharmony_ci 2302a46c0ec8Sopenharmony_ci for (tool = LIBINPUT_TABLET_TOOL_TYPE_PEN; 2303a46c0ec8Sopenharmony_ci tool <= LIBINPUT_TABLET_TOOL_TYPE_MAX; 2304a46c0ec8Sopenharmony_ci tool++) { 2305a46c0ec8Sopenharmony_ci code = tablet_tool_to_evcode(tool); 2306a46c0ec8Sopenharmony_ci 2307a46c0ec8Sopenharmony_ci /* we only expect one tool to be in proximity at a time */ 2308a46c0ec8Sopenharmony_ci if (libevdev_fetch_event_value(device->evdev, 2309a46c0ec8Sopenharmony_ci EV_KEY, 2310a46c0ec8Sopenharmony_ci code, 2311a46c0ec8Sopenharmony_ci &state) && state) { 2312a46c0ec8Sopenharmony_ci tablet->tool_state = bit(tool); 2313a46c0ec8Sopenharmony_ci tablet->prev_tool_state = bit(tool); 2314a46c0ec8Sopenharmony_ci break; 2315a46c0ec8Sopenharmony_ci } 2316a46c0ec8Sopenharmony_ci } 2317a46c0ec8Sopenharmony_ci 2318a46c0ec8Sopenharmony_ci if (!tablet->tool_state) 2319a46c0ec8Sopenharmony_ci return; 2320a46c0ec8Sopenharmony_ci 2321a46c0ec8Sopenharmony_ci tablet_update_tool(tablet, device, tool, state); 2322a46c0ec8Sopenharmony_ci if (tablet->quirks.need_to_force_prox_out) 2323a46c0ec8Sopenharmony_ci tablet_proximity_out_quirk_set_timer(tablet, libinput_now(li)); 2324a46c0ec8Sopenharmony_ci 2325a46c0ec8Sopenharmony_ci tablet->current_tool.id = 2326a46c0ec8Sopenharmony_ci libevdev_get_event_value(device->evdev, 2327a46c0ec8Sopenharmony_ci EV_ABS, 2328a46c0ec8Sopenharmony_ci ABS_MISC); 2329a46c0ec8Sopenharmony_ci 2330a46c0ec8Sopenharmony_ci /* we can't fetch MSC_SERIAL from the kernel, so we set the serial 2331a46c0ec8Sopenharmony_ci * to 0 for now. On the first real event from the device we get the 2332a46c0ec8Sopenharmony_ci * serial (if any) and that event will be converted into a proximity 2333a46c0ec8Sopenharmony_ci * event */ 2334a46c0ec8Sopenharmony_ci tablet->current_tool.serial = 0; 2335a46c0ec8Sopenharmony_ci} 2336a46c0ec8Sopenharmony_ci 2337a46c0ec8Sopenharmony_ci/* Called when the touchpad toggles to left-handed */ 2338a46c0ec8Sopenharmony_cistatic void 2339a46c0ec8Sopenharmony_citablet_left_handed_toggled(struct evdev_dispatch *dispatch, 2340a46c0ec8Sopenharmony_ci struct evdev_device *device, 2341a46c0ec8Sopenharmony_ci bool left_handed_enabled) 2342a46c0ec8Sopenharmony_ci{ 2343a46c0ec8Sopenharmony_ci struct tablet_dispatch *tablet = tablet_dispatch(dispatch); 2344a46c0ec8Sopenharmony_ci 2345a46c0ec8Sopenharmony_ci if (!tablet->rotation.touch_device) 2346a46c0ec8Sopenharmony_ci return; 2347a46c0ec8Sopenharmony_ci 2348a46c0ec8Sopenharmony_ci evdev_log_debug(device, 2349a46c0ec8Sopenharmony_ci "tablet-rotation: touchpad is %s\n", 2350a46c0ec8Sopenharmony_ci left_handed_enabled ? "left-handed" : "right-handed"); 2351a46c0ec8Sopenharmony_ci 2352a46c0ec8Sopenharmony_ci /* Our left-handed config is independent even though rotation is 2353a46c0ec8Sopenharmony_ci * locked. So we rotate when either device is left-handed. But it 2354a46c0ec8Sopenharmony_ci * can only be actually changed when the device is in a neutral 2355a46c0ec8Sopenharmony_ci * state, hence the want_rotate. 2356a46c0ec8Sopenharmony_ci */ 2357a46c0ec8Sopenharmony_ci tablet->rotation.touch_device_left_handed_state = left_handed_enabled; 2358a46c0ec8Sopenharmony_ci tablet_change_rotation(device, DONT_NOTIFY); 2359a46c0ec8Sopenharmony_ci} 2360a46c0ec8Sopenharmony_ci 2361a46c0ec8Sopenharmony_cistatic struct evdev_dispatch_interface tablet_interface = { 2362a46c0ec8Sopenharmony_ci .process = tablet_process, 2363a46c0ec8Sopenharmony_ci .suspend = tablet_suspend, 2364a46c0ec8Sopenharmony_ci .remove = NULL, 2365a46c0ec8Sopenharmony_ci .destroy = tablet_destroy, 2366a46c0ec8Sopenharmony_ci .device_added = tablet_device_added, 2367a46c0ec8Sopenharmony_ci .device_removed = tablet_device_removed, 2368a46c0ec8Sopenharmony_ci .device_suspended = NULL, 2369a46c0ec8Sopenharmony_ci .device_resumed = NULL, 2370a46c0ec8Sopenharmony_ci .post_added = tablet_check_initial_proximity, 2371a46c0ec8Sopenharmony_ci .touch_arbitration_toggle = NULL, 2372a46c0ec8Sopenharmony_ci .touch_arbitration_update_rect = NULL, 2373a46c0ec8Sopenharmony_ci .get_switch_state = NULL, 2374a46c0ec8Sopenharmony_ci .left_handed_toggle = tablet_left_handed_toggled, 2375a46c0ec8Sopenharmony_ci}; 2376a46c0ec8Sopenharmony_ci 2377a46c0ec8Sopenharmony_cistatic void 2378a46c0ec8Sopenharmony_citablet_init_calibration(struct tablet_dispatch *tablet, 2379a46c0ec8Sopenharmony_ci struct evdev_device *device) 2380a46c0ec8Sopenharmony_ci{ 2381a46c0ec8Sopenharmony_ci if (libevdev_has_property(device->evdev, INPUT_PROP_DIRECT)) 2382a46c0ec8Sopenharmony_ci evdev_init_calibration(device, &tablet->calibration); 2383a46c0ec8Sopenharmony_ci} 2384a46c0ec8Sopenharmony_ci 2385a46c0ec8Sopenharmony_cistatic void 2386a46c0ec8Sopenharmony_citablet_init_proximity_threshold(struct tablet_dispatch *tablet, 2387a46c0ec8Sopenharmony_ci struct evdev_device *device) 2388a46c0ec8Sopenharmony_ci{ 2389a46c0ec8Sopenharmony_ci /* This rules out most of the bamboos and other devices, we're 2390a46c0ec8Sopenharmony_ci * pretty much down to 2391a46c0ec8Sopenharmony_ci */ 2392a46c0ec8Sopenharmony_ci if (!libevdev_has_event_code(device->evdev, EV_KEY, BTN_TOOL_MOUSE) && 2393a46c0ec8Sopenharmony_ci !libevdev_has_event_code(device->evdev, EV_KEY, BTN_TOOL_LENS)) 2394a46c0ec8Sopenharmony_ci return; 2395a46c0ec8Sopenharmony_ci 2396a46c0ec8Sopenharmony_ci /* 42 is the default proximity threshold the xf86-input-wacom driver 2397a46c0ec8Sopenharmony_ci * uses for Intuos/Cintiq models. Graphire models have a threshold 2398a46c0ec8Sopenharmony_ci * of 10 but since they haven't been manufactured in ages and the 2399a46c0ec8Sopenharmony_ci * intersection of users having a graphire, running libinput and 2400a46c0ec8Sopenharmony_ci * wanting to use the mouse/lens cursor tool is small enough to not 2401a46c0ec8Sopenharmony_ci * worry about it for now. If we need to, we can introduce a udev 2402a46c0ec8Sopenharmony_ci * property later. 2403a46c0ec8Sopenharmony_ci * 2404a46c0ec8Sopenharmony_ci * Value is in device coordinates. 2405a46c0ec8Sopenharmony_ci */ 2406a46c0ec8Sopenharmony_ci tablet->cursor_proximity_threshold = 42; 2407a46c0ec8Sopenharmony_ci} 2408a46c0ec8Sopenharmony_ci 2409a46c0ec8Sopenharmony_cistatic uint32_t 2410a46c0ec8Sopenharmony_citablet_accel_config_get_profiles(struct libinput_device *libinput_device) 2411a46c0ec8Sopenharmony_ci{ 2412a46c0ec8Sopenharmony_ci return LIBINPUT_CONFIG_ACCEL_PROFILE_NONE; 2413a46c0ec8Sopenharmony_ci} 2414a46c0ec8Sopenharmony_ci 2415a46c0ec8Sopenharmony_cistatic enum libinput_config_status 2416a46c0ec8Sopenharmony_citablet_accel_config_set_profile(struct libinput_device *libinput_device, 2417a46c0ec8Sopenharmony_ci enum libinput_config_accel_profile profile) 2418a46c0ec8Sopenharmony_ci{ 2419a46c0ec8Sopenharmony_ci return LIBINPUT_CONFIG_STATUS_UNSUPPORTED; 2420a46c0ec8Sopenharmony_ci} 2421a46c0ec8Sopenharmony_ci 2422a46c0ec8Sopenharmony_cistatic enum libinput_config_accel_profile 2423a46c0ec8Sopenharmony_citablet_accel_config_get_profile(struct libinput_device *libinput_device) 2424a46c0ec8Sopenharmony_ci{ 2425a46c0ec8Sopenharmony_ci return LIBINPUT_CONFIG_ACCEL_PROFILE_NONE; 2426a46c0ec8Sopenharmony_ci} 2427a46c0ec8Sopenharmony_ci 2428a46c0ec8Sopenharmony_cistatic enum libinput_config_accel_profile 2429a46c0ec8Sopenharmony_citablet_accel_config_get_default_profile(struct libinput_device *libinput_device) 2430a46c0ec8Sopenharmony_ci{ 2431a46c0ec8Sopenharmony_ci return LIBINPUT_CONFIG_ACCEL_PROFILE_NONE; 2432a46c0ec8Sopenharmony_ci} 2433a46c0ec8Sopenharmony_ci 2434a46c0ec8Sopenharmony_cistatic int 2435a46c0ec8Sopenharmony_citablet_init_accel(struct tablet_dispatch *tablet, struct evdev_device *device) 2436a46c0ec8Sopenharmony_ci{ 2437a46c0ec8Sopenharmony_ci const struct input_absinfo *x, *y; 2438a46c0ec8Sopenharmony_ci struct motion_filter *filter; 2439a46c0ec8Sopenharmony_ci 2440a46c0ec8Sopenharmony_ci x = device->abs.absinfo_x; 2441a46c0ec8Sopenharmony_ci y = device->abs.absinfo_y; 2442a46c0ec8Sopenharmony_ci 2443a46c0ec8Sopenharmony_ci filter = create_pointer_accelerator_filter_tablet(x->resolution, 2444a46c0ec8Sopenharmony_ci y->resolution); 2445a46c0ec8Sopenharmony_ci if (!filter) 2446a46c0ec8Sopenharmony_ci return -1; 2447a46c0ec8Sopenharmony_ci 2448a46c0ec8Sopenharmony_ci evdev_device_init_pointer_acceleration(device, filter); 2449a46c0ec8Sopenharmony_ci 2450a46c0ec8Sopenharmony_ci /* we override the profile hooks for accel configuration with hooks 2451a46c0ec8Sopenharmony_ci * that don't allow selection of profiles */ 2452a46c0ec8Sopenharmony_ci device->pointer.config.get_profiles = tablet_accel_config_get_profiles; 2453a46c0ec8Sopenharmony_ci device->pointer.config.set_profile = tablet_accel_config_set_profile; 2454a46c0ec8Sopenharmony_ci device->pointer.config.get_profile = tablet_accel_config_get_profile; 2455a46c0ec8Sopenharmony_ci device->pointer.config.get_default_profile = tablet_accel_config_get_default_profile; 2456a46c0ec8Sopenharmony_ci 2457a46c0ec8Sopenharmony_ci return 0; 2458a46c0ec8Sopenharmony_ci} 2459a46c0ec8Sopenharmony_ci 2460a46c0ec8Sopenharmony_cistatic void 2461a46c0ec8Sopenharmony_citablet_init_left_handed(struct evdev_device *device) 2462a46c0ec8Sopenharmony_ci{ 2463a46c0ec8Sopenharmony_ci if (evdev_tablet_has_left_handed(device)) 2464a46c0ec8Sopenharmony_ci evdev_init_left_handed(device, 2465a46c0ec8Sopenharmony_ci tablet_change_to_left_handed); 2466a46c0ec8Sopenharmony_ci} 2467a46c0ec8Sopenharmony_ci 2468a46c0ec8Sopenharmony_cistatic bool 2469a46c0ec8Sopenharmony_citablet_is_aes(struct evdev_device *device, 2470a46c0ec8Sopenharmony_ci struct tablet_dispatch *tablet) 2471a46c0ec8Sopenharmony_ci{ 2472a46c0ec8Sopenharmony_ci bool is_aes = false; 2473a46c0ec8Sopenharmony_ci#if HAVE_LIBWACOM 2474a46c0ec8Sopenharmony_ci const char *devnode; 2475a46c0ec8Sopenharmony_ci WacomDeviceDatabase *db; 2476a46c0ec8Sopenharmony_ci WacomDevice *libwacom_device = NULL; 2477a46c0ec8Sopenharmony_ci const int *stylus_ids; 2478a46c0ec8Sopenharmony_ci int nstyli; 2479a46c0ec8Sopenharmony_ci int vid = evdev_device_get_id_vendor(device); 2480a46c0ec8Sopenharmony_ci 2481a46c0ec8Sopenharmony_ci /* Wacom-specific check for whether smoothing is required: 2482a46c0ec8Sopenharmony_ci * libwacom keeps all the AES pens in a single group, so any device 2483a46c0ec8Sopenharmony_ci * that supports AES pens will list all AES pens. 0x11 is one of the 2484a46c0ec8Sopenharmony_ci * lenovo pens so we use that as the flag of whether the tablet 2485a46c0ec8Sopenharmony_ci * is an AES tablet 2486a46c0ec8Sopenharmony_ci */ 2487a46c0ec8Sopenharmony_ci if (vid != VENDOR_ID_WACOM) 2488a46c0ec8Sopenharmony_ci goto out; 2489a46c0ec8Sopenharmony_ci 2490a46c0ec8Sopenharmony_ci db = tablet_libinput_context(tablet)->libwacom.db; 2491a46c0ec8Sopenharmony_ci if (!db) 2492a46c0ec8Sopenharmony_ci goto out; 2493a46c0ec8Sopenharmony_ci 2494a46c0ec8Sopenharmony_ci devnode = udev_device_get_devnode(device->udev_device); 2495a46c0ec8Sopenharmony_ci libwacom_device = libwacom_new_from_path(db, devnode, WFALLBACK_NONE, NULL); 2496a46c0ec8Sopenharmony_ci if (!libwacom_device) 2497a46c0ec8Sopenharmony_ci goto out; 2498a46c0ec8Sopenharmony_ci 2499a46c0ec8Sopenharmony_ci stylus_ids = libwacom_get_supported_styli(libwacom_device, &nstyli); 2500a46c0ec8Sopenharmony_ci for (int i = 0; i < nstyli; i++) { 2501a46c0ec8Sopenharmony_ci if (stylus_ids[i] == 0x11) { 2502a46c0ec8Sopenharmony_ci is_aes = true; 2503a46c0ec8Sopenharmony_ci break; 2504a46c0ec8Sopenharmony_ci } 2505a46c0ec8Sopenharmony_ci } 2506a46c0ec8Sopenharmony_ci 2507a46c0ec8Sopenharmony_ci libwacom_destroy(libwacom_device); 2508a46c0ec8Sopenharmony_ci 2509a46c0ec8Sopenharmony_ciout: 2510a46c0ec8Sopenharmony_ci#endif 2511a46c0ec8Sopenharmony_ci return is_aes; 2512a46c0ec8Sopenharmony_ci} 2513a46c0ec8Sopenharmony_ci 2514a46c0ec8Sopenharmony_cistatic void 2515a46c0ec8Sopenharmony_citablet_init_smoothing(struct evdev_device *device, 2516a46c0ec8Sopenharmony_ci struct tablet_dispatch *tablet) 2517a46c0ec8Sopenharmony_ci{ 2518a46c0ec8Sopenharmony_ci size_t history_size = ARRAY_LENGTH(tablet->history.samples); 2519a46c0ec8Sopenharmony_ci struct quirks_context *quirks = NULL; 2520a46c0ec8Sopenharmony_ci struct quirks *q = NULL; 2521a46c0ec8Sopenharmony_ci bool use_smoothing = true; 2522a46c0ec8Sopenharmony_ci 2523a46c0ec8Sopenharmony_ci quirks = evdev_libinput_context(device)->quirks; 2524a46c0ec8Sopenharmony_ci q = quirks_fetch_for_device(quirks, device->udev_device); 2525a46c0ec8Sopenharmony_ci 2526a46c0ec8Sopenharmony_ci /* By default, always enable smoothing except on AES devices. 2527a46c0ec8Sopenharmony_ci * AttrTabletSmoothing can override this, if necessary. 2528a46c0ec8Sopenharmony_ci */ 2529a46c0ec8Sopenharmony_ci if (!q || !quirks_get_bool(q, QUIRK_ATTR_TABLET_SMOOTHING, &use_smoothing)) 2530a46c0ec8Sopenharmony_ci use_smoothing = !tablet_is_aes(device, tablet); 2531a46c0ec8Sopenharmony_ci 2532a46c0ec8Sopenharmony_ci /* Setting the history size to 1 means we never do any actual smoothing. */ 2533a46c0ec8Sopenharmony_ci if (!use_smoothing) 2534a46c0ec8Sopenharmony_ci history_size = 1; 2535a46c0ec8Sopenharmony_ci 2536a46c0ec8Sopenharmony_ci quirks_unref(q); 2537a46c0ec8Sopenharmony_ci tablet->history.size = history_size; 2538a46c0ec8Sopenharmony_ci} 2539a46c0ec8Sopenharmony_ci 2540a46c0ec8Sopenharmony_cistatic bool 2541a46c0ec8Sopenharmony_citablet_reject_device(struct evdev_device *device) 2542a46c0ec8Sopenharmony_ci{ 2543a46c0ec8Sopenharmony_ci struct libevdev *evdev = device->evdev; 2544a46c0ec8Sopenharmony_ci double w, h; 2545a46c0ec8Sopenharmony_ci bool has_xy, has_pen, has_btn_stylus, has_size; 2546a46c0ec8Sopenharmony_ci 2547a46c0ec8Sopenharmony_ci has_xy = libevdev_has_event_code(evdev, EV_ABS, ABS_X) && 2548a46c0ec8Sopenharmony_ci libevdev_has_event_code(evdev, EV_ABS, ABS_Y); 2549a46c0ec8Sopenharmony_ci has_pen = libevdev_has_event_code(evdev, EV_KEY, BTN_TOOL_PEN); 2550a46c0ec8Sopenharmony_ci has_btn_stylus = libevdev_has_event_code(evdev, EV_KEY, BTN_STYLUS); 2551a46c0ec8Sopenharmony_ci has_size = evdev_device_get_size(device, &w, &h) == 0; 2552a46c0ec8Sopenharmony_ci 2553a46c0ec8Sopenharmony_ci if (has_xy && (has_pen || has_btn_stylus) && has_size) 2554a46c0ec8Sopenharmony_ci return false; 2555a46c0ec8Sopenharmony_ci 2556a46c0ec8Sopenharmony_ci evdev_log_bug_libinput(device, 2557a46c0ec8Sopenharmony_ci "missing tablet capabilities:%s%s%s%s. " 2558a46c0ec8Sopenharmony_ci "Ignoring this device.\n", 2559a46c0ec8Sopenharmony_ci has_xy ? "" : " xy", 2560a46c0ec8Sopenharmony_ci has_pen ? "" : " pen", 2561a46c0ec8Sopenharmony_ci has_btn_stylus ? "" : " btn-stylus", 2562a46c0ec8Sopenharmony_ci has_size ? "" : " resolution"); 2563a46c0ec8Sopenharmony_ci return true; 2564a46c0ec8Sopenharmony_ci} 2565a46c0ec8Sopenharmony_ci 2566a46c0ec8Sopenharmony_cistatic int 2567a46c0ec8Sopenharmony_citablet_init(struct tablet_dispatch *tablet, 2568a46c0ec8Sopenharmony_ci struct evdev_device *device) 2569a46c0ec8Sopenharmony_ci{ 2570a46c0ec8Sopenharmony_ci struct libevdev *evdev = device->evdev; 2571a46c0ec8Sopenharmony_ci enum libinput_tablet_tool_axis axis; 2572a46c0ec8Sopenharmony_ci int rc; 2573a46c0ec8Sopenharmony_ci 2574a46c0ec8Sopenharmony_ci tablet->base.dispatch_type = DISPATCH_TABLET; 2575a46c0ec8Sopenharmony_ci tablet->base.interface = &tablet_interface; 2576a46c0ec8Sopenharmony_ci tablet->device = device; 2577a46c0ec8Sopenharmony_ci tablet->status = TABLET_NONE; 2578a46c0ec8Sopenharmony_ci tablet->current_tool.type = LIBINPUT_TOOL_NONE; 2579a46c0ec8Sopenharmony_ci list_init(&tablet->tool_list); 2580a46c0ec8Sopenharmony_ci 2581a46c0ec8Sopenharmony_ci if (tablet_reject_device(device)) 2582a46c0ec8Sopenharmony_ci return -1; 2583a46c0ec8Sopenharmony_ci 2584a46c0ec8Sopenharmony_ci if (!libevdev_has_event_code(evdev, EV_KEY, BTN_TOOL_PEN)) { 2585a46c0ec8Sopenharmony_ci libevdev_enable_event_code(evdev, EV_KEY, BTN_TOOL_PEN, NULL); 2586a46c0ec8Sopenharmony_ci tablet->quirks.proximity_out_forced = true; 2587a46c0ec8Sopenharmony_ci } 2588a46c0ec8Sopenharmony_ci 2589a46c0ec8Sopenharmony_ci /* Our rotation code only works with Wacoms, let's wait until 2590a46c0ec8Sopenharmony_ci * someone shouts */ 2591a46c0ec8Sopenharmony_ci if (evdev_device_get_id_vendor(device) != VENDOR_ID_WACOM) { 2592a46c0ec8Sopenharmony_ci libevdev_disable_event_code(evdev, EV_KEY, BTN_TOOL_MOUSE); 2593a46c0ec8Sopenharmony_ci libevdev_disable_event_code(evdev, EV_KEY, BTN_TOOL_LENS); 2594a46c0ec8Sopenharmony_ci } 2595a46c0ec8Sopenharmony_ci 2596a46c0ec8Sopenharmony_ci tablet_init_calibration(tablet, device); 2597a46c0ec8Sopenharmony_ci tablet_init_proximity_threshold(tablet, device); 2598a46c0ec8Sopenharmony_ci rc = tablet_init_accel(tablet, device); 2599a46c0ec8Sopenharmony_ci if (rc != 0) 2600a46c0ec8Sopenharmony_ci return rc; 2601a46c0ec8Sopenharmony_ci 2602a46c0ec8Sopenharmony_ci evdev_init_sendevents(device, &tablet->base); 2603a46c0ec8Sopenharmony_ci tablet_init_left_handed(device); 2604a46c0ec8Sopenharmony_ci tablet_init_smoothing(device, tablet); 2605a46c0ec8Sopenharmony_ci 2606a46c0ec8Sopenharmony_ci for (axis = LIBINPUT_TABLET_TOOL_AXIS_X; 2607a46c0ec8Sopenharmony_ci axis <= LIBINPUT_TABLET_TOOL_AXIS_MAX; 2608a46c0ec8Sopenharmony_ci axis++) { 2609a46c0ec8Sopenharmony_ci if (tablet_device_has_axis(tablet, axis)) 2610a46c0ec8Sopenharmony_ci set_bit(tablet->axis_caps, axis); 2611a46c0ec8Sopenharmony_ci } 2612a46c0ec8Sopenharmony_ci 2613a46c0ec8Sopenharmony_ci tablet_set_status(tablet, TABLET_TOOL_OUT_OF_PROXIMITY); 2614a46c0ec8Sopenharmony_ci 2615a46c0ec8Sopenharmony_ci /* We always enable the proximity out quirk, but disable it once a 2616a46c0ec8Sopenharmony_ci device gives us the right event sequence */ 2617a46c0ec8Sopenharmony_ci tablet->quirks.need_to_force_prox_out = true; 2618a46c0ec8Sopenharmony_ci 2619a46c0ec8Sopenharmony_ci libinput_timer_init(&tablet->quirks.prox_out_timer, 2620a46c0ec8Sopenharmony_ci tablet_libinput_context(tablet), 2621a46c0ec8Sopenharmony_ci "proxout", 2622a46c0ec8Sopenharmony_ci tablet_proximity_out_quirk_timer_func, 2623a46c0ec8Sopenharmony_ci tablet); 2624a46c0ec8Sopenharmony_ci 2625a46c0ec8Sopenharmony_ci return 0; 2626a46c0ec8Sopenharmony_ci} 2627a46c0ec8Sopenharmony_ci 2628a46c0ec8Sopenharmony_cistruct evdev_dispatch * 2629a46c0ec8Sopenharmony_cievdev_tablet_create(struct evdev_device *device) 2630a46c0ec8Sopenharmony_ci{ 2631a46c0ec8Sopenharmony_ci struct tablet_dispatch *tablet; 2632a46c0ec8Sopenharmony_ci struct libinput *li = evdev_libinput_context(device); 2633a46c0ec8Sopenharmony_ci 2634a46c0ec8Sopenharmony_ci libinput_libwacom_ref(li); 2635a46c0ec8Sopenharmony_ci 2636a46c0ec8Sopenharmony_ci /* Stop false positives caused by the forced proximity code */ 2637a46c0ec8Sopenharmony_ci if (getenv("LIBINPUT_RUNNING_TEST_SUITE")) 2638a46c0ec8Sopenharmony_ci FORCED_PROXOUT_TIMEOUT = 150 * 1000; /* µs */ 2639a46c0ec8Sopenharmony_ci 2640a46c0ec8Sopenharmony_ci tablet = zalloc(sizeof *tablet); 2641a46c0ec8Sopenharmony_ci 2642a46c0ec8Sopenharmony_ci if (tablet_init(tablet, device) != 0) { 2643a46c0ec8Sopenharmony_ci tablet_destroy(&tablet->base); 2644a46c0ec8Sopenharmony_ci return NULL; 2645a46c0ec8Sopenharmony_ci } 2646a46c0ec8Sopenharmony_ci 2647a46c0ec8Sopenharmony_ci return &tablet->base; 2648a46c0ec8Sopenharmony_ci} 2649