mirror of
https://gitlab.freedesktop.org/libinput/libinput.git
synced 2025-12-20 05:40:04 +01:00
tablet: implement the tablet proximity timer as plugin
Together with the "forced-tool" quirk (that enforces BTN_TOOL_PEN to be set even if the tablet doesn't) since those two go hand-in-hand. Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1218>
This commit is contained in:
parent
e5e28ea7e6
commit
2d23d7f4aa
9 changed files with 609 additions and 159 deletions
|
|
@ -376,6 +376,8 @@ src_libinput = src_libfilter + [
|
||||||
'src/libinput.c',
|
'src/libinput.c',
|
||||||
'src/libinput-plugin.c',
|
'src/libinput-plugin.c',
|
||||||
'src/libinput-plugin-tablet-double-tool.c',
|
'src/libinput-plugin-tablet-double-tool.c',
|
||||||
|
'src/libinput-plugin-tablet-forced-tool.c',
|
||||||
|
'src/libinput-plugin-tablet-proximity-timer.c',
|
||||||
'src/libinput-private-config.c',
|
'src/libinput-private-config.c',
|
||||||
'src/evdev.c',
|
'src/evdev.c',
|
||||||
'src/evdev-debounce.c',
|
'src/evdev-debounce.c',
|
||||||
|
|
|
||||||
|
|
@ -40,11 +40,6 @@ enum notify {
|
||||||
DO_NOTIFY,
|
DO_NOTIFY,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* The tablet sends events every ~2ms , 50ms should be plenty enough to
|
|
||||||
detect out-of-range.
|
|
||||||
This value is higher during test suite runs */
|
|
||||||
static int FORCED_PROXOUT_TIMEOUT = 50 * 1000; /* µs */
|
|
||||||
|
|
||||||
#define tablet_set_status(tablet_,s_) (tablet_)->status |= (s_)
|
#define tablet_set_status(tablet_,s_) (tablet_)->status |= (s_)
|
||||||
#define tablet_unset_status(tablet_,s_) (tablet_)->status &= ~(s_)
|
#define tablet_unset_status(tablet_,s_) (tablet_)->status &= ~(s_)
|
||||||
#define tablet_has_status(tablet_,s_) (!!((tablet_)->status & (s_)))
|
#define tablet_has_status(tablet_,s_) (!!((tablet_)->status & (s_)))
|
||||||
|
|
@ -1993,34 +1988,6 @@ tablet_send_events(struct tablet_dispatch *tablet,
|
||||||
tablet_send_proximity_out(tablet, tool, device, &axes, time);
|
tablet_send_proximity_out(tablet, tool, device, &axes, time);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Handling for the proximity out workaround. Some tablets only send
|
|
||||||
* BTN_TOOL_PEN on the very first event, then leave it set even when the pen
|
|
||||||
* leaves the detectable range. To libinput this looks like we always have
|
|
||||||
* the pen in proximity.
|
|
||||||
*
|
|
||||||
* To avoid this, we set a timer on BTN_TOOL_PEN in. We expect the tablet to
|
|
||||||
* continuously send events, and while it's doing so we keep updating the
|
|
||||||
* timer. Once we go Xms without an event we assume proximity out and inject
|
|
||||||
* a BTN_TOOL_PEN event into the sequence through the timer func.
|
|
||||||
*
|
|
||||||
* We need to remember that we did that, on the first event after the
|
|
||||||
* timeout we need to emulate a BTN_TOOL_PEN event again to force proximity
|
|
||||||
* in.
|
|
||||||
*
|
|
||||||
* Other tools never send the BTN_TOOL_PEN event. For those tools, we
|
|
||||||
* piggyback along with the proximity out quirks by injecting
|
|
||||||
* the event during the first event frame.
|
|
||||||
*/
|
|
||||||
static inline void
|
|
||||||
tablet_proximity_out_quirk_set_timer(struct tablet_dispatch *tablet,
|
|
||||||
uint64_t time)
|
|
||||||
{
|
|
||||||
if (tablet->quirks.need_to_force_prox_out)
|
|
||||||
libinput_timer_set(&tablet->quirks.prox_out_timer,
|
|
||||||
time + FORCED_PROXOUT_TIMEOUT);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
tablet_update_tool_state(struct tablet_dispatch *tablet,
|
tablet_update_tool_state(struct tablet_dispatch *tablet,
|
||||||
struct evdev_device *device,
|
struct evdev_device *device,
|
||||||
|
|
@ -2030,41 +1997,6 @@ tablet_update_tool_state(struct tablet_dispatch *tablet,
|
||||||
uint32_t changed;
|
uint32_t changed;
|
||||||
int state;
|
int state;
|
||||||
|
|
||||||
/* we were already out of proximity but now got a tool update but
|
|
||||||
* our tool state is zero - i.e. we got a valid prox out from the
|
|
||||||
* device.
|
|
||||||
*/
|
|
||||||
if (tablet->quirks.proximity_out_forced &&
|
|
||||||
tablet_has_status(tablet, TABLET_TOOL_UPDATED) &&
|
|
||||||
!tablet->tool_state) {
|
|
||||||
tablet->quirks.need_to_force_prox_out = false;
|
|
||||||
tablet->quirks.proximity_out_forced = false;
|
|
||||||
}
|
|
||||||
/* We need to emulate a BTN_TOOL_PEN if we get an axis event (i.e.
|
|
||||||
* stylus is def. in proximity) and:
|
|
||||||
* - we forced a proximity out before, or
|
|
||||||
* - on the very first event after init, because if we didn't get a
|
|
||||||
* BTN_TOOL_PEN and the state for the tool was 0, this device will
|
|
||||||
* never send the event.
|
|
||||||
* We don't do this for pure button events because we discard those.
|
|
||||||
*
|
|
||||||
* But: on some devices the proximity out is delayed by the kernel,
|
|
||||||
* so we get it after our forced prox-out has triggered. In that
|
|
||||||
* case we need to just ignore the change.
|
|
||||||
*/
|
|
||||||
if (tablet_has_status(tablet, TABLET_AXES_UPDATED)) {
|
|
||||||
if (tablet->quirks.proximity_out_forced) {
|
|
||||||
if (!tablet_has_status(tablet, TABLET_TOOL_UPDATED) &&
|
|
||||||
!tablet->tool_state)
|
|
||||||
tablet->tool_state = bit(LIBINPUT_TABLET_TOOL_TYPE_PEN);
|
|
||||||
tablet->quirks.proximity_out_forced = false;
|
|
||||||
} else if (tablet->tool_state == 0 &&
|
|
||||||
tablet->current_tool.type == LIBINPUT_TOOL_NONE) {
|
|
||||||
tablet->tool_state = bit(LIBINPUT_TABLET_TOOL_TYPE_PEN);
|
|
||||||
tablet->quirks.proximity_out_forced = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tablet->tool_state == tablet->prev_tool_state)
|
if (tablet->tool_state == tablet->prev_tool_state)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
@ -2074,24 +2006,6 @@ tablet_update_tool_state(struct tablet_dispatch *tablet,
|
||||||
|
|
||||||
tablet_update_tool(tablet, device, type, state);
|
tablet_update_tool(tablet, device, type, state);
|
||||||
|
|
||||||
/* The proximity timeout is only needed for BTN_TOOL_PEN, devices
|
|
||||||
* that require it don't do erasers */
|
|
||||||
if (type == LIBINPUT_TABLET_TOOL_TYPE_PEN) {
|
|
||||||
if (state) {
|
|
||||||
tablet_proximity_out_quirk_set_timer(tablet, time);
|
|
||||||
} else {
|
|
||||||
/* If we get a BTN_TOOL_PEN 0 when *not* injecting
|
|
||||||
* events it means the tablet will give us the right
|
|
||||||
* events after all and we can disable our
|
|
||||||
* timer-based proximity out.
|
|
||||||
*/
|
|
||||||
if (!tablet->quirks.proximity_out_in_progress)
|
|
||||||
tablet->quirks.need_to_force_prox_out = false;
|
|
||||||
|
|
||||||
libinput_timer_cancel(&tablet->quirks.prox_out_timer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tablet->prev_tool_state = tablet->tool_state;
|
tablet->prev_tool_state = tablet->tool_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2307,47 +2221,6 @@ tablet_reset_state(struct tablet_dispatch *tablet)
|
||||||
tablet_set_status(tablet, TABLET_BUTTONS_DOWN);
|
tablet_set_status(tablet, TABLET_BUTTONS_DOWN);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
tablet_proximity_out_quirk_timer_func(uint64_t now, void *data)
|
|
||||||
{
|
|
||||||
struct tablet_dispatch *tablet = data;
|
|
||||||
struct evdev_event events[2] = {
|
|
||||||
{
|
|
||||||
.usage = evdev_usage_from(EVDEV_BTN_TOOL_PEN),
|
|
||||||
.value = 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.usage = evdev_usage_from(EVDEV_SYN_REPORT),
|
|
||||||
.value = 0,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (tablet_has_status(tablet, TABLET_TOOL_IN_CONTACT) ||
|
|
||||||
tablet_has_status(tablet, TABLET_BUTTONS_DOWN)) {
|
|
||||||
tablet_proximity_out_quirk_set_timer(tablet, now);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tablet->quirks.last_event_time > now - FORCED_PROXOUT_TIMEOUT) {
|
|
||||||
tablet_proximity_out_quirk_set_timer(tablet,
|
|
||||||
tablet->quirks.last_event_time);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
evdev_log_debug(tablet->device, "tablet: forcing proximity after timeout\n");
|
|
||||||
|
|
||||||
tablet->quirks.proximity_out_in_progress = true;
|
|
||||||
ARRAY_FOR_EACH(events, e) {
|
|
||||||
tablet->base.interface->process(&tablet->base,
|
|
||||||
tablet->device,
|
|
||||||
e,
|
|
||||||
now);
|
|
||||||
}
|
|
||||||
tablet->quirks.proximity_out_in_progress = false;
|
|
||||||
|
|
||||||
tablet->quirks.proximity_out_forced = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
tablet_process(struct evdev_dispatch *dispatch,
|
tablet_process(struct evdev_dispatch *dispatch,
|
||||||
struct evdev_device *device,
|
struct evdev_device *device,
|
||||||
|
|
@ -2374,7 +2247,6 @@ tablet_process(struct evdev_dispatch *dispatch,
|
||||||
tablet_flush(tablet, device, time);
|
tablet_flush(tablet, device, time);
|
||||||
tablet_toggle_touch_device(tablet, device, time);
|
tablet_toggle_touch_device(tablet, device, time);
|
||||||
tablet_reset_state(tablet);
|
tablet_reset_state(tablet);
|
||||||
tablet->quirks.last_event_time = time;
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
evdev_log_error(device,
|
evdev_log_error(device,
|
||||||
|
|
@ -2434,9 +2306,6 @@ tablet_destroy(struct evdev_dispatch *dispatch)
|
||||||
struct libinput_tablet_tool *tool;
|
struct libinput_tablet_tool *tool;
|
||||||
struct libinput *li = tablet_libinput_context(tablet);
|
struct libinput *li = tablet_libinput_context(tablet);
|
||||||
|
|
||||||
libinput_timer_cancel(&tablet->quirks.prox_out_timer);
|
|
||||||
libinput_timer_destroy(&tablet->quirks.prox_out_timer);
|
|
||||||
|
|
||||||
list_for_each_safe(tool, &tablet->tool_list, link) {
|
list_for_each_safe(tool, &tablet->tool_list, link) {
|
||||||
libinput_tablet_tool_unref(tool);
|
libinput_tablet_tool_unref(tool);
|
||||||
}
|
}
|
||||||
|
|
@ -2545,7 +2414,6 @@ tablet_check_initial_proximity(struct evdev_device *device,
|
||||||
struct evdev_dispatch *dispatch)
|
struct evdev_dispatch *dispatch)
|
||||||
{
|
{
|
||||||
struct tablet_dispatch *tablet = tablet_dispatch(dispatch);
|
struct tablet_dispatch *tablet = tablet_dispatch(dispatch);
|
||||||
struct libinput *li = tablet_libinput_context(tablet);
|
|
||||||
int code, state;
|
int code, state;
|
||||||
enum libinput_tablet_tool_type tool;
|
enum libinput_tablet_tool_type tool;
|
||||||
|
|
||||||
|
|
@ -2569,8 +2437,6 @@ tablet_check_initial_proximity(struct evdev_device *device,
|
||||||
return;
|
return;
|
||||||
|
|
||||||
tablet_update_tool(tablet, device, tool, state);
|
tablet_update_tool(tablet, device, tool, state);
|
||||||
if (tablet->quirks.need_to_force_prox_out)
|
|
||||||
tablet_proximity_out_quirk_set_timer(tablet, libinput_now(li));
|
|
||||||
|
|
||||||
tablet->current_tool.id =
|
tablet->current_tool.id =
|
||||||
libevdev_get_event_value(device->evdev,
|
libevdev_get_event_value(device->evdev,
|
||||||
|
|
@ -2983,7 +2849,6 @@ tablet_init(struct tablet_dispatch *tablet,
|
||||||
|
|
||||||
if (!libevdev_has_event_code(evdev, EV_KEY, BTN_TOOL_PEN)) {
|
if (!libevdev_has_event_code(evdev, EV_KEY, BTN_TOOL_PEN)) {
|
||||||
libevdev_enable_event_code(evdev, EV_KEY, BTN_TOOL_PEN, NULL);
|
libevdev_enable_event_code(evdev, EV_KEY, BTN_TOOL_PEN, NULL);
|
||||||
tablet->quirks.proximity_out_forced = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Our rotation code only works with Wacoms, let's wait until
|
/* Our rotation code only works with Wacoms, let's wait until
|
||||||
|
|
@ -3014,16 +2879,6 @@ tablet_init(struct tablet_dispatch *tablet,
|
||||||
|
|
||||||
tablet_set_status(tablet, TABLET_TOOL_OUT_OF_PROXIMITY);
|
tablet_set_status(tablet, TABLET_TOOL_OUT_OF_PROXIMITY);
|
||||||
|
|
||||||
/* We always enable the proximity out quirk, but disable it once a
|
|
||||||
device gives us the right event sequence */
|
|
||||||
tablet->quirks.need_to_force_prox_out = true;
|
|
||||||
|
|
||||||
libinput_timer_init(&tablet->quirks.prox_out_timer,
|
|
||||||
li,
|
|
||||||
"proxout",
|
|
||||||
tablet_proximity_out_quirk_timer_func,
|
|
||||||
tablet);
|
|
||||||
|
|
||||||
rc = 0;
|
rc = 0;
|
||||||
out:
|
out:
|
||||||
#if HAVE_LIBWACOM
|
#if HAVE_LIBWACOM
|
||||||
|
|
@ -3043,10 +2898,6 @@ evdev_tablet_create(struct evdev_device *device)
|
||||||
|
|
||||||
libinput_libwacom_ref(li);
|
libinput_libwacom_ref(li);
|
||||||
|
|
||||||
/* Stop false positives caused by the forced proximity code */
|
|
||||||
if (getenv("LIBINPUT_RUNNING_TEST_SUITE"))
|
|
||||||
FORCED_PROXOUT_TIMEOUT = 150 * 1000; /* µs */
|
|
||||||
|
|
||||||
tablet = zalloc(sizeof *tablet);
|
tablet = zalloc(sizeof *tablet);
|
||||||
|
|
||||||
if (tablet_init(tablet, device) != 0) {
|
if (tablet_init(tablet, device) != 0) {
|
||||||
|
|
|
||||||
|
|
@ -116,16 +116,6 @@ struct tablet_dispatch {
|
||||||
bool rotate;
|
bool rotate;
|
||||||
bool want_rotate;
|
bool want_rotate;
|
||||||
} rotation;
|
} rotation;
|
||||||
|
|
||||||
struct {
|
|
||||||
bool need_to_force_prox_out;
|
|
||||||
struct libinput_timer prox_out_timer;
|
|
||||||
bool proximity_out_forced;
|
|
||||||
uint64_t last_event_time;
|
|
||||||
|
|
||||||
/* true while injecting BTN_TOOL_PEN events */
|
|
||||||
bool proximity_out_in_progress;
|
|
||||||
} quirks;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline struct tablet_dispatch*
|
static inline struct tablet_dispatch*
|
||||||
|
|
|
||||||
229
src/libinput-plugin-tablet-forced-tool.c
Normal file
229
src/libinput-plugin-tablet-forced-tool.c
Normal file
|
|
@ -0,0 +1,229 @@
|
||||||
|
/*
|
||||||
|
* Copyright © 2025 Red Hat, Inc.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice (including the next
|
||||||
|
* paragraph) shall be included in all copies or substantial portions of the
|
||||||
|
* Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
* DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <libevdev/libevdev.h>
|
||||||
|
|
||||||
|
#include "util-mem.h"
|
||||||
|
#include "util-strings.h"
|
||||||
|
#include "evdev-frame.h"
|
||||||
|
|
||||||
|
#include "libinput-log.h"
|
||||||
|
#include "libinput-util.h"
|
||||||
|
#include "libinput-plugin.h"
|
||||||
|
#include "libinput-plugin-tablet-forced-tool.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Handling for tools that never set BTN_TOOL_PEN.
|
||||||
|
*/
|
||||||
|
|
||||||
|
enum tools {
|
||||||
|
PEN = 0,
|
||||||
|
RUBBER,
|
||||||
|
BRUSH,
|
||||||
|
PENCIL,
|
||||||
|
AIRBRUSH,
|
||||||
|
MOUSE,
|
||||||
|
LENS,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct plugin_device {
|
||||||
|
struct list link;
|
||||||
|
struct libinput_device *device;
|
||||||
|
bitmask_t tool_state;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct plugin_data {
|
||||||
|
struct list devices;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
plugin_device_destroy(struct plugin_device *device)
|
||||||
|
{
|
||||||
|
libinput_device_unref(device->device);
|
||||||
|
list_remove(&device->link);
|
||||||
|
free(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
plugin_data_destroy(void *d)
|
||||||
|
{
|
||||||
|
struct plugin_data *data = d;
|
||||||
|
|
||||||
|
struct plugin_device *device;
|
||||||
|
list_for_each_safe(device, &data->devices, link) {
|
||||||
|
plugin_device_destroy(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFINE_DESTROY_CLEANUP_FUNC(plugin_data);
|
||||||
|
|
||||||
|
static void
|
||||||
|
plugin_destroy(struct libinput_plugin *libinput_plugin)
|
||||||
|
{
|
||||||
|
struct plugin_data *plugin = libinput_plugin_get_user_data(libinput_plugin);
|
||||||
|
plugin_data_destroy(plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
forced_tool_plugin_device_handle_frame(struct libinput_plugin *libinput_plugin,
|
||||||
|
struct plugin_device *device,
|
||||||
|
struct evdev_frame *frame)
|
||||||
|
{
|
||||||
|
size_t nevents;
|
||||||
|
struct evdev_event *events = evdev_frame_get_events(frame, &nevents);
|
||||||
|
|
||||||
|
bool axis_change = false;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < nevents; i++) {
|
||||||
|
struct evdev_event *event = &events[i];
|
||||||
|
switch (evdev_usage_enum(event->usage)) {
|
||||||
|
case EVDEV_BTN_TOOL_PEN:
|
||||||
|
case EVDEV_BTN_TOOL_RUBBER:
|
||||||
|
case EVDEV_BTN_TOOL_BRUSH:
|
||||||
|
case EVDEV_BTN_TOOL_PENCIL:
|
||||||
|
case EVDEV_BTN_TOOL_AIRBRUSH:
|
||||||
|
case EVDEV_BTN_TOOL_MOUSE:
|
||||||
|
case EVDEV_BTN_TOOL_LENS:
|
||||||
|
if (event->value == 1) {
|
||||||
|
bitmask_set_bit(&device->tool_state,
|
||||||
|
evdev_event_code(event) - BTN_TOOL_PEN);
|
||||||
|
} else {
|
||||||
|
bitmask_clear_bit(&device->tool_state,
|
||||||
|
evdev_event_code(event)- BTN_TOOL_PEN);
|
||||||
|
}
|
||||||
|
return; /* Nothing to do */
|
||||||
|
case EVDEV_ABS_X:
|
||||||
|
case EVDEV_ABS_Y:
|
||||||
|
case EVDEV_ABS_Z: /* rotation */
|
||||||
|
/* not ABS_DISTANCE! */
|
||||||
|
case EVDEV_ABS_PRESSURE:
|
||||||
|
case EVDEV_ABS_TILT_X:
|
||||||
|
case EVDEV_ABS_TILT_Y:
|
||||||
|
case EVDEV_ABS_WHEEL: /* slider */
|
||||||
|
/* no early return here, the BTN_TOOL updates
|
||||||
|
* may come after the ABS_ events */
|
||||||
|
axis_change = true;
|
||||||
|
break;
|
||||||
|
case EVDEV_REL_WHEEL:
|
||||||
|
/* no early return here, the BTN_TOOL updates
|
||||||
|
* may come after the REL_ events */
|
||||||
|
axis_change = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!axis_change)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const bitmask_t all_tools = bitmask_from_bits(PEN,
|
||||||
|
RUBBER,
|
||||||
|
BRUSH,
|
||||||
|
PENCIL,
|
||||||
|
AIRBRUSH,
|
||||||
|
MOUSE,
|
||||||
|
LENS);
|
||||||
|
if (bitmask_any(device->tool_state, all_tools))
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* We need to force a BTN_TOOL_PEN if we get an axis event (i.e.
|
||||||
|
* stylus is def. in proximity). We don't do this for pure
|
||||||
|
* button events because we discard those.
|
||||||
|
*/
|
||||||
|
const struct evdev_event prox = {
|
||||||
|
.usage = evdev_usage_from(EVDEV_BTN_TOOL_PEN),
|
||||||
|
.value = 1,
|
||||||
|
};
|
||||||
|
evdev_frame_append(frame, &prox, 1); /* libinput's event frame will have space */
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
forced_tool_plugin_evdev_frame(struct libinput_plugin *libinput_plugin,
|
||||||
|
struct libinput_device *device,
|
||||||
|
struct evdev_frame *frame)
|
||||||
|
{
|
||||||
|
struct plugin_data *plugin = libinput_plugin_get_user_data(libinput_plugin);
|
||||||
|
struct plugin_device *pd;
|
||||||
|
|
||||||
|
list_for_each(pd, &plugin->devices, link) {
|
||||||
|
if (pd->device == device) {
|
||||||
|
forced_tool_plugin_device_handle_frame(libinput_plugin, pd, frame);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
forced_tool_plugin_device_added(struct libinput_plugin *libinput_plugin,
|
||||||
|
struct libinput_device *device)
|
||||||
|
{
|
||||||
|
if (!libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_TABLET_TOOL))
|
||||||
|
return;
|
||||||
|
|
||||||
|
struct plugin_data *plugin = libinput_plugin_get_user_data(libinput_plugin);
|
||||||
|
struct plugin_device *pd = zalloc(sizeof(*pd));
|
||||||
|
pd->device = libinput_device_ref(device);
|
||||||
|
list_take_append(&plugin->devices, pd, link);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
forced_tool_plugin_device_removed(struct libinput_plugin *libinput_plugin,
|
||||||
|
struct libinput_device *device)
|
||||||
|
{
|
||||||
|
struct plugin_data *plugin = libinput_plugin_get_user_data(libinput_plugin);
|
||||||
|
struct plugin_device *dev;
|
||||||
|
list_for_each_safe(dev, &plugin->devices, link) {
|
||||||
|
if (dev->device == device) {
|
||||||
|
plugin_device_destroy(dev);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct libinput_plugin_interface interface = {
|
||||||
|
.run = NULL,
|
||||||
|
.destroy = plugin_destroy,
|
||||||
|
.device_new = NULL,
|
||||||
|
.device_ignored = NULL,
|
||||||
|
.device_added = forced_tool_plugin_device_added,
|
||||||
|
.device_removed = forced_tool_plugin_device_removed,
|
||||||
|
.evdev_frame = forced_tool_plugin_evdev_frame,
|
||||||
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
libinput_tablet_plugin_forced_tool(struct libinput *libinput)
|
||||||
|
{
|
||||||
|
_destroy_(plugin_data) *plugin = zalloc(sizeof(*plugin));
|
||||||
|
list_init(&plugin->devices);
|
||||||
|
|
||||||
|
_unref_(libinput_plugin) *p = libinput_plugin_new(libinput,
|
||||||
|
"tablet-forced-tool",
|
||||||
|
&interface,
|
||||||
|
steal(&plugin));
|
||||||
|
}
|
||||||
30
src/libinput-plugin-tablet-forced-tool.h
Normal file
30
src/libinput-plugin-tablet-forced-tool.h
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* Copyright © 2025 Red Hat, Inc.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice (including the next
|
||||||
|
* paragraph) shall be included in all copies or substantial portions of the
|
||||||
|
* Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
* DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "libinput.h"
|
||||||
|
#include "libinput-plugin.h"
|
||||||
|
|
||||||
|
void
|
||||||
|
libinput_tablet_plugin_forced_tool(struct libinput *libinput);
|
||||||
312
src/libinput-plugin-tablet-proximity-timer.c
Normal file
312
src/libinput-plugin-tablet-proximity-timer.c
Normal file
|
|
@ -0,0 +1,312 @@
|
||||||
|
/*
|
||||||
|
* Copyright © 2025 Red Hat, Inc.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice (including the next
|
||||||
|
* paragraph) shall be included in all copies or substantial portions of the
|
||||||
|
* Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
* DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <libevdev/libevdev.h>
|
||||||
|
|
||||||
|
#include "util-mem.h"
|
||||||
|
#include "util-strings.h"
|
||||||
|
|
||||||
|
#include "evdev-frame.h"
|
||||||
|
|
||||||
|
#include "timer.h"
|
||||||
|
#include "libinput-log.h"
|
||||||
|
#include "libinput-util.h"
|
||||||
|
#include "libinput-plugin.h"
|
||||||
|
#include "libinput-plugin-tablet-proximity-timer.h"
|
||||||
|
|
||||||
|
/* The tablet sends events every ~2ms , 50ms should be plenty enough to
|
||||||
|
detect out-of-range.
|
||||||
|
This value is higher during test suite runs */
|
||||||
|
static int FORCED_PROXOUT_TIMEOUT = 50 * 1000; /* µs */
|
||||||
|
|
||||||
|
struct plugin_device {
|
||||||
|
struct list link;
|
||||||
|
|
||||||
|
struct libinput_plugin_timer *prox_out_timer;
|
||||||
|
bool proximity_out_forced;
|
||||||
|
uint64_t last_event_time;
|
||||||
|
|
||||||
|
bool pen_state;
|
||||||
|
bitmask_t button_state;
|
||||||
|
|
||||||
|
struct libinput_device *device;
|
||||||
|
struct plugin_data *parent;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
plugin_device_destroy(void *d)
|
||||||
|
{
|
||||||
|
struct plugin_device *device = d;
|
||||||
|
|
||||||
|
list_remove(&device->link);
|
||||||
|
libinput_plugin_timer_cancel(device->prox_out_timer);
|
||||||
|
libinput_plugin_timer_unref(device->prox_out_timer);
|
||||||
|
libinput_device_unref(device->device);
|
||||||
|
|
||||||
|
free(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct plugin_data {
|
||||||
|
struct list devices;
|
||||||
|
struct libinput_plugin *plugin;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
plugin_data_destroy(void *d)
|
||||||
|
{
|
||||||
|
struct plugin_data *data = d;
|
||||||
|
|
||||||
|
struct plugin_device *device;
|
||||||
|
list_for_each_safe(device, &data->devices, link) {
|
||||||
|
plugin_device_destroy(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFINE_DESTROY_CLEANUP_FUNC(plugin_data);
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
proximity_timer_plugin_set_timer(struct plugin_device *device, uint64_t time)
|
||||||
|
{
|
||||||
|
libinput_plugin_timer_set(device->prox_out_timer,
|
||||||
|
time + FORCED_PROXOUT_TIMEOUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
tablet_proximity_out_quirk_timer_func(struct libinput_plugin *plugin,
|
||||||
|
uint64_t now,
|
||||||
|
void *data)
|
||||||
|
{
|
||||||
|
struct plugin_device *device = data;
|
||||||
|
|
||||||
|
if (!bitmask_is_empty(device->button_state)) {
|
||||||
|
proximity_timer_plugin_set_timer(device, now);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (device->last_event_time > now - FORCED_PROXOUT_TIMEOUT) {
|
||||||
|
proximity_timer_plugin_set_timer(device, device->last_event_time);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
plugin_log_debug(device->parent->plugin,
|
||||||
|
"%s: forcing proximity out after timeout\n",
|
||||||
|
libinput_device_get_name(device->device));
|
||||||
|
const struct evdev_event prox_out_event = {
|
||||||
|
.usage = evdev_usage_from(EVDEV_BTN_TOOL_PEN),
|
||||||
|
.value = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
_unref_(evdev_frame) *prox_out_frame = evdev_frame_new(2);
|
||||||
|
evdev_frame_append(prox_out_frame, &prox_out_event, 1);
|
||||||
|
evdev_frame_set_time(prox_out_frame, now);
|
||||||
|
|
||||||
|
libinput_plugin_prepend_evdev_frame(device->parent->plugin,
|
||||||
|
device->device,
|
||||||
|
prox_out_frame);
|
||||||
|
|
||||||
|
device->proximity_out_forced = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Handling for the proximity out workaround. Some tablets only send
|
||||||
|
* BTN_TOOL_PEN on the very first event, then leave it set even when the pen
|
||||||
|
* leaves the detectable range. To libinput this looks like we always have
|
||||||
|
* the pen in proximity.
|
||||||
|
*
|
||||||
|
* To avoid this, we set a timer on BTN_TOOL_PEN in. We expect the tablet to
|
||||||
|
* continuously send events, and while it's doing so we keep updating the
|
||||||
|
* timer. Once we go Xms without an event we assume proximity out and inject
|
||||||
|
* a BTN_TOOL_PEN 0 event into the sequence through the timer func.
|
||||||
|
*
|
||||||
|
* On the next axis event after a prox out we enforce
|
||||||
|
* BTN_TOOL_PEN 1 to force proximity in.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
proximity_timer_plugin_device_handle_frame(struct libinput_plugin *libinput_plugin,
|
||||||
|
struct plugin_device *device,
|
||||||
|
struct evdev_frame *frame)
|
||||||
|
{
|
||||||
|
uint64_t time = evdev_frame_get_time(frame);
|
||||||
|
/* First event after adding a device - by definition the pen
|
||||||
|
*
|
||||||
|
* is in proximity if we get this one */
|
||||||
|
if (device->last_event_time == 0)
|
||||||
|
proximity_timer_plugin_set_timer(device, time);
|
||||||
|
|
||||||
|
device->last_event_time = time;
|
||||||
|
|
||||||
|
bool pen_toggled = false;
|
||||||
|
|
||||||
|
size_t nevents;
|
||||||
|
struct evdev_event *events = evdev_frame_get_events(frame, &nevents);
|
||||||
|
for (size_t i = 0; i < nevents; i++) {
|
||||||
|
struct evdev_event *event = &events[i];
|
||||||
|
|
||||||
|
/* The proximity timeout is only needed for BTN_TOOL_PEN, devices
|
||||||
|
* that require it don't do erasers */
|
||||||
|
switch (evdev_usage_enum(event->usage)) {
|
||||||
|
case EVDEV_BTN_STYLUS:
|
||||||
|
case EVDEV_BTN_STYLUS2:
|
||||||
|
case EVDEV_BTN_STYLUS3:
|
||||||
|
case EVDEV_BTN_TOUCH:
|
||||||
|
if (event->value)
|
||||||
|
bitmask_set_bit(&device->button_state,
|
||||||
|
evdev_event_code(event) - BTN_STYLUS3);
|
||||||
|
else
|
||||||
|
bitmask_clear_bit(&device->button_state,
|
||||||
|
evdev_event_code(event) - BTN_STYLUS3);
|
||||||
|
break;
|
||||||
|
case EVDEV_BTN_TOOL_PEN:
|
||||||
|
pen_toggled = true;
|
||||||
|
device->pen_state = event->value == 1;
|
||||||
|
break;
|
||||||
|
case EVDEV_BTN_TOOL_RUBBER:
|
||||||
|
case EVDEV_BTN_TOOL_BRUSH:
|
||||||
|
case EVDEV_BTN_TOOL_PENCIL:
|
||||||
|
case EVDEV_BTN_TOOL_AIRBRUSH:
|
||||||
|
case EVDEV_BTN_TOOL_FINGER:
|
||||||
|
case EVDEV_BTN_TOOL_MOUSE:
|
||||||
|
case EVDEV_BTN_TOOL_LENS:
|
||||||
|
plugin_device_destroy(device);
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pen_toggled) {
|
||||||
|
if (device->pen_state) {
|
||||||
|
proximity_timer_plugin_set_timer(device, time);
|
||||||
|
} else {
|
||||||
|
/* If we get a BTN_TOOL_PEN 0 it means the tablet will
|
||||||
|
* give us the right events after all and we can disable
|
||||||
|
* our timer-based proximity out.
|
||||||
|
*/
|
||||||
|
libinput_plugin_timer_cancel(device->prox_out_timer);
|
||||||
|
plugin_log_debug(libinput_plugin,
|
||||||
|
"%s: proximity out timer unloaded\n",
|
||||||
|
libinput_device_get_name(device->device));
|
||||||
|
plugin_device_destroy(device);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else if (device->proximity_out_forced) {
|
||||||
|
struct evdev_event pen_in_event = {
|
||||||
|
.usage = evdev_usage_from(EVDEV_BTN_TOOL_PEN),
|
||||||
|
.value = 1,
|
||||||
|
};
|
||||||
|
plugin_log_debug(libinput_plugin,
|
||||||
|
"%s: forcing proximity in\n",
|
||||||
|
libinput_device_get_name(device->device));
|
||||||
|
evdev_frame_append(frame, &pen_in_event, 1); /* libinput's event frame will have space */
|
||||||
|
device->proximity_out_forced = false;
|
||||||
|
proximity_timer_plugin_set_timer(device, time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
proximity_timer_plugin_evdev_frame(struct libinput_plugin *libinput_plugin,
|
||||||
|
struct libinput_device *device,
|
||||||
|
struct evdev_frame *frame)
|
||||||
|
{
|
||||||
|
struct plugin_data *plugin = libinput_plugin_get_user_data(libinput_plugin);
|
||||||
|
struct plugin_device *pd;
|
||||||
|
|
||||||
|
list_for_each(pd, &plugin->devices, link) {
|
||||||
|
if (pd->device == device) {
|
||||||
|
proximity_timer_plugin_device_handle_frame(libinput_plugin, pd, frame);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
proximity_timer_plugin_device_added(struct libinput_plugin *libinput_plugin,
|
||||||
|
struct libinput_device *device)
|
||||||
|
{
|
||||||
|
if (!libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_TABLET_TOOL))
|
||||||
|
return;
|
||||||
|
|
||||||
|
struct plugin_data *plugin = libinput_plugin_get_user_data(libinput_plugin);
|
||||||
|
struct plugin_device *pd = zalloc(sizeof(*pd));
|
||||||
|
pd->device = libinput_device_ref(device);
|
||||||
|
pd->parent = plugin;
|
||||||
|
pd->prox_out_timer = libinput_plugin_timer_new(libinput_plugin,
|
||||||
|
"proximity-timer",
|
||||||
|
tablet_proximity_out_quirk_timer_func,
|
||||||
|
pd);
|
||||||
|
|
||||||
|
list_take_append(&plugin->devices, pd, link);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
proximity_timer_plugin_device_removed(struct libinput_plugin *libinput_plugin,
|
||||||
|
struct libinput_device *device)
|
||||||
|
{
|
||||||
|
struct plugin_data *plugin = libinput_plugin_get_user_data(libinput_plugin);
|
||||||
|
struct plugin_device *dev;
|
||||||
|
list_for_each_safe(dev, &plugin->devices, link) {
|
||||||
|
if (dev->device == device) {
|
||||||
|
plugin_device_destroy(dev);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
plugin_destroy(struct libinput_plugin *libinput_plugin)
|
||||||
|
{
|
||||||
|
struct plugin_data *plugin = libinput_plugin_get_user_data(libinput_plugin);
|
||||||
|
plugin_data_destroy(plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct libinput_plugin_interface interface = {
|
||||||
|
.run = NULL,
|
||||||
|
.destroy = plugin_destroy,
|
||||||
|
.device_new = NULL,
|
||||||
|
.device_ignored = NULL,
|
||||||
|
.device_added = proximity_timer_plugin_device_added,
|
||||||
|
.device_removed = proximity_timer_plugin_device_removed,
|
||||||
|
.evdev_frame = proximity_timer_plugin_evdev_frame,
|
||||||
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
libinput_tablet_plugin_proximity_timer(struct libinput *libinput)
|
||||||
|
{
|
||||||
|
struct plugin_data *plugin = zalloc(sizeof(*plugin));
|
||||||
|
list_init(&plugin->devices);
|
||||||
|
|
||||||
|
/* Stop false positives caused by the forced proximity code */
|
||||||
|
if (getenv("LIBINPUT_RUNNING_TEST_SUITE"))
|
||||||
|
FORCED_PROXOUT_TIMEOUT = 150 * 1000; /* µs */
|
||||||
|
|
||||||
|
_unref_(libinput_plugin) *p = libinput_plugin_new(libinput,
|
||||||
|
"tablet-proximity-timer",
|
||||||
|
&interface,
|
||||||
|
plugin);
|
||||||
|
plugin->plugin = p;
|
||||||
|
}
|
||||||
30
src/libinput-plugin-tablet-proximity-timer.h
Normal file
30
src/libinput-plugin-tablet-proximity-timer.h
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* Copyright © 2025 Red Hat, Inc.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice (including the next
|
||||||
|
* paragraph) shall be included in all copies or substantial portions of the
|
||||||
|
* Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
* DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "libinput.h"
|
||||||
|
#include "libinput-plugin.h"
|
||||||
|
|
||||||
|
void
|
||||||
|
libinput_tablet_plugin_proximity_timer(struct libinput *libinput);
|
||||||
|
|
@ -37,6 +37,8 @@
|
||||||
#include "libinput-util.h"
|
#include "libinput-util.h"
|
||||||
#include "libinput-private.h"
|
#include "libinput-private.h"
|
||||||
#include "libinput-plugin-tablet-double-tool.h"
|
#include "libinput-plugin-tablet-double-tool.h"
|
||||||
|
#include "libinput-plugin-tablet-forced-tool.h"
|
||||||
|
#include "libinput-plugin-tablet-proximity-timer.h"
|
||||||
|
|
||||||
#include "evdev-plugin.h"
|
#include "evdev-plugin.h"
|
||||||
|
|
||||||
|
|
@ -362,7 +364,9 @@ libinput_plugin_system_load_internal_plugins(struct libinput *libinput,
|
||||||
{
|
{
|
||||||
/* FIXME: this should really be one of the first in the sequence
|
/* FIXME: this should really be one of the first in the sequence
|
||||||
* so plugins don't have to take care of this? */
|
* so plugins don't have to take care of this? */
|
||||||
|
libinput_tablet_plugin_forced_tool(libinput);
|
||||||
libinput_tablet_plugin_double_tool(libinput);
|
libinput_tablet_plugin_double_tool(libinput);
|
||||||
|
libinput_tablet_plugin_proximity_timer(libinput);
|
||||||
|
|
||||||
/* Our own event dispatch is implemented as mini-plugin,
|
/* Our own event dispatch is implemented as mini-plugin,
|
||||||
* guarantee this one to always be last (and after any
|
* guarantee this one to always be last (and after any
|
||||||
|
|
|
||||||
|
|
@ -1567,6 +1567,7 @@ START_TEST(proximity_out_not_during_contact)
|
||||||
};
|
};
|
||||||
|
|
||||||
litest_tablet_proximity_in(dev, 10, 10, axes);
|
litest_tablet_proximity_in(dev, 10, 10, axes);
|
||||||
|
litest_tablet_tip_down(dev, 10, 10, axes);
|
||||||
litest_tablet_motion(dev, 12, 12, axes);
|
litest_tablet_motion(dev, 12, 12, axes);
|
||||||
litest_drain_events(li);
|
litest_drain_events(li);
|
||||||
|
|
||||||
|
|
@ -1577,6 +1578,7 @@ START_TEST(proximity_out_not_during_contact)
|
||||||
|
|
||||||
litest_axis_set_value(axes, ABS_PRESSURE, 0);
|
litest_axis_set_value(axes, ABS_PRESSURE, 0);
|
||||||
litest_tablet_motion(dev, 14, 14, axes);
|
litest_tablet_motion(dev, 14, 14, axes);
|
||||||
|
litest_tablet_tip_up(dev, 14, 14, axes);
|
||||||
litest_drain_events(li);
|
litest_drain_events(li);
|
||||||
|
|
||||||
litest_timeout_tablet_proxout(li);
|
litest_timeout_tablet_proxout(li);
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue