evdev: add support for middle button emulation

This is just the required framework, it's not hooked up to anything just yet.
Hooking it up comes as separate commit to better detail why/when a device
supports emulation.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
This commit is contained in:
Peter Hutterer 2015-04-13 16:38:44 +10:00
parent a6c4115692
commit 75f15917a3
9 changed files with 2098 additions and 1 deletions

View file

@ -1,4 +1,7 @@
EXTRA_DIST = touchpad-tap-state-machine.svg touchpad-softbutton-state-machine.svg EXTRA_DIST = \
middle-button-emulation.svg \
touchpad-tap-state-machine.svg \
touchpad-softbutton-state-machine.svg
if BUILD_DOCS if BUILD_DOCS

File diff suppressed because it is too large Load diff

After

Width:  |  Height:  |  Size: 112 KiB

View file

@ -11,6 +11,7 @@ libinput_la_SOURCES = \
libinput-private.h \ libinput-private.h \
evdev.c \ evdev.c \
evdev.h \ evdev.h \
evdev-middle-button.c \
evdev-mt-touchpad.c \ evdev-mt-touchpad.c \
evdev-mt-touchpad.h \ evdev-mt-touchpad.h \
evdev-mt-touchpad-tap.c \ evdev-mt-touchpad-tap.c \

716
src/evdev-middle-button.c Normal file
View file

@ -0,0 +1,716 @@
/*
* Copyright © 2014 Red Hat, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software and
* its documentation for any purpose is hereby granted without fee, provided
* that the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation, and that the name of the copyright holders not be used in
* advertising or publicity pertaining to distribution of the software
* without specific, written prior permission. The copyright holders make
* no representations about the suitability of this software for any
* purpose. It is provided "as is" without express or implied warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
* RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
* CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <stdint.h>
#include "evdev.h"
#define MIDDLEBUTTON_TIMEOUT 50
/*****************************************
* BEFORE YOU EDIT THIS FILE, look at the state diagram in
* doc/middle-button-emulation-state-machine.svg, or online at
* https://drive.google.com/file/d/0B1NwWmji69nodUJncXRMc1FvY1k/view?usp=sharing
* (it's a http://draw.io diagram)
*
* Any changes in this file must be represented in the diagram.
*
* Note in regards to the state machine: it only handles left, right and
* emulated middle button clicks, all other button events are passed
* through. When in the PASSTHROUGH state, all events are passed through
* as-is.
*/
#define CASE_RETURN_STRING(a) case a: return #a;
static inline const char*
middlebutton_state_to_str(enum evdev_middlebutton_state state)
{
switch (state) {
CASE_RETURN_STRING(MIDDLEBUTTON_IDLE);
CASE_RETURN_STRING(MIDDLEBUTTON_LEFT_DOWN);
CASE_RETURN_STRING(MIDDLEBUTTON_RIGHT_DOWN);
CASE_RETURN_STRING(MIDDLEBUTTON_MIDDLE);
CASE_RETURN_STRING(MIDDLEBUTTON_LEFT_UP_PENDING);
CASE_RETURN_STRING(MIDDLEBUTTON_RIGHT_UP_PENDING);
CASE_RETURN_STRING(MIDDLEBUTTON_PASSTHROUGH);
CASE_RETURN_STRING(MIDDLEBUTTON_IGNORE_LR);
CASE_RETURN_STRING(MIDDLEBUTTON_IGNORE_L);
CASE_RETURN_STRING(MIDDLEBUTTON_IGNORE_R);
}
return NULL;
}
static inline const char*
middlebutton_event_to_str(enum evdev_middlebutton_event event)
{
switch (event) {
CASE_RETURN_STRING(MIDDLEBUTTON_EVENT_L_DOWN);
CASE_RETURN_STRING(MIDDLEBUTTON_EVENT_R_DOWN);
CASE_RETURN_STRING(MIDDLEBUTTON_EVENT_OTHER);
CASE_RETURN_STRING(MIDDLEBUTTON_EVENT_L_UP);
CASE_RETURN_STRING(MIDDLEBUTTON_EVENT_R_UP);
CASE_RETURN_STRING(MIDDLEBUTTON_EVENT_TIMEOUT);
CASE_RETURN_STRING(MIDDLEBUTTON_EVENT_ALL_UP);
}
return NULL;
}
static void
middlebutton_state_error(struct evdev_device *device,
enum evdev_middlebutton_event event)
{
log_bug_libinput(device->base.seat->libinput,
"Invalid event %s in middle btn state %s\n",
middlebutton_event_to_str(event),
middlebutton_state_to_str(device->middlebutton.state));
}
static void
middlebutton_timer_set(struct evdev_device *device, uint64_t now)
{
libinput_timer_set(&device->middlebutton.timer,
now + MIDDLEBUTTON_TIMEOUT);
}
static void
middlebutton_timer_cancel(struct evdev_device *device)
{
libinput_timer_cancel(&device->middlebutton.timer);
}
static inline void
middlebutton_set_state(struct evdev_device *device,
enum evdev_middlebutton_state state,
uint64_t now)
{
switch (state) {
case MIDDLEBUTTON_LEFT_DOWN:
case MIDDLEBUTTON_RIGHT_DOWN:
middlebutton_timer_set(device, now);
device->middlebutton.first_event_time = now;
break;
case MIDDLEBUTTON_IDLE:
case MIDDLEBUTTON_MIDDLE:
case MIDDLEBUTTON_LEFT_UP_PENDING:
case MIDDLEBUTTON_RIGHT_UP_PENDING:
case MIDDLEBUTTON_PASSTHROUGH:
case MIDDLEBUTTON_IGNORE_LR:
case MIDDLEBUTTON_IGNORE_L:
case MIDDLEBUTTON_IGNORE_R:
middlebutton_timer_cancel(device);
break;
}
device->middlebutton.state = state;
}
static void
middlebutton_post_event(struct evdev_device *device,
uint64_t now,
int button,
enum libinput_button_state state)
{
evdev_pointer_notify_button(device,
now,
button,
state);
}
static int
evdev_middlebutton_idle_handle_event(struct evdev_device *device,
uint64_t time,
enum evdev_middlebutton_event event)
{
switch (event) {
case MIDDLEBUTTON_EVENT_L_DOWN:
middlebutton_set_state(device, MIDDLEBUTTON_LEFT_DOWN, time);
break;
case MIDDLEBUTTON_EVENT_R_DOWN:
middlebutton_set_state(device, MIDDLEBUTTON_RIGHT_DOWN, time);
break;
case MIDDLEBUTTON_EVENT_OTHER:
return 0;
case MIDDLEBUTTON_EVENT_R_UP:
case MIDDLEBUTTON_EVENT_L_UP:
case MIDDLEBUTTON_EVENT_TIMEOUT:
middlebutton_state_error(device, event);
break;
case MIDDLEBUTTON_EVENT_ALL_UP:
break;
}
return 1;
}
static int
evdev_middlebutton_ldown_handle_event(struct evdev_device *device,
uint64_t time,
enum evdev_middlebutton_event event)
{
switch (event) {
case MIDDLEBUTTON_EVENT_L_DOWN:
middlebutton_state_error(device, event);
break;
case MIDDLEBUTTON_EVENT_R_DOWN:
middlebutton_post_event(device, time,
BTN_MIDDLE,
LIBINPUT_BUTTON_STATE_PRESSED);
middlebutton_set_state(device, MIDDLEBUTTON_MIDDLE, time);
break;
case MIDDLEBUTTON_EVENT_OTHER:
middlebutton_post_event(device, time,
BTN_LEFT,
LIBINPUT_BUTTON_STATE_PRESSED);
middlebutton_set_state(device,
MIDDLEBUTTON_PASSTHROUGH,
time);
return 0;
case MIDDLEBUTTON_EVENT_R_UP:
middlebutton_state_error(device, event);
break;
case MIDDLEBUTTON_EVENT_L_UP:
middlebutton_post_event(device,
device->middlebutton.first_event_time,
BTN_LEFT,
LIBINPUT_BUTTON_STATE_PRESSED);
middlebutton_post_event(device, time,
BTN_LEFT,
LIBINPUT_BUTTON_STATE_RELEASED);
middlebutton_set_state(device, MIDDLEBUTTON_IDLE, time);
break;
case MIDDLEBUTTON_EVENT_TIMEOUT:
middlebutton_post_event(device,
device->middlebutton.first_event_time,
BTN_LEFT,
LIBINPUT_BUTTON_STATE_PRESSED);
middlebutton_set_state(device,
MIDDLEBUTTON_PASSTHROUGH,
time);
break;
case MIDDLEBUTTON_EVENT_ALL_UP:
middlebutton_state_error(device, event);
break;
}
return 1;
}
static int
evdev_middlebutton_rdown_handle_event(struct evdev_device *device,
uint64_t time,
enum evdev_middlebutton_event event)
{
switch (event) {
case MIDDLEBUTTON_EVENT_L_DOWN:
middlebutton_post_event(device, time,
BTN_MIDDLE,
LIBINPUT_BUTTON_STATE_PRESSED);
middlebutton_set_state(device, MIDDLEBUTTON_MIDDLE, time);
break;
case MIDDLEBUTTON_EVENT_R_DOWN:
middlebutton_state_error(device, event);
break;
case MIDDLEBUTTON_EVENT_OTHER:
middlebutton_post_event(device,
device->middlebutton.first_event_time,
BTN_RIGHT,
LIBINPUT_BUTTON_STATE_PRESSED);
middlebutton_set_state(device,
MIDDLEBUTTON_PASSTHROUGH,
time);
return 0;
case MIDDLEBUTTON_EVENT_R_UP:
middlebutton_post_event(device,
device->middlebutton.first_event_time,
BTN_RIGHT,
LIBINPUT_BUTTON_STATE_PRESSED);
middlebutton_post_event(device, time,
BTN_RIGHT,
LIBINPUT_BUTTON_STATE_RELEASED);
middlebutton_set_state(device, MIDDLEBUTTON_IDLE, time);
break;
case MIDDLEBUTTON_EVENT_L_UP:
middlebutton_state_error(device, event);
break;
case MIDDLEBUTTON_EVENT_TIMEOUT:
middlebutton_post_event(device,
device->middlebutton.first_event_time,
BTN_RIGHT,
LIBINPUT_BUTTON_STATE_PRESSED);
middlebutton_set_state(device,
MIDDLEBUTTON_PASSTHROUGH,
time);
break;
case MIDDLEBUTTON_EVENT_ALL_UP:
middlebutton_state_error(device, event);
break;
}
return 1;
}
static int
evdev_middlebutton_middle_handle_event(struct evdev_device *device,
uint64_t time,
enum evdev_middlebutton_event event)
{
switch (event) {
case MIDDLEBUTTON_EVENT_L_DOWN:
case MIDDLEBUTTON_EVENT_R_DOWN:
middlebutton_state_error(device, event);
break;
case MIDDLEBUTTON_EVENT_OTHER:
middlebutton_post_event(device, time,
BTN_MIDDLE,
LIBINPUT_BUTTON_STATE_RELEASED);
middlebutton_set_state(device, MIDDLEBUTTON_IGNORE_LR, time);
return 0;
case MIDDLEBUTTON_EVENT_R_UP:
middlebutton_post_event(device, time,
BTN_MIDDLE,
LIBINPUT_BUTTON_STATE_RELEASED);
middlebutton_set_state(device,
MIDDLEBUTTON_LEFT_UP_PENDING,
time);
break;
case MIDDLEBUTTON_EVENT_L_UP:
middlebutton_post_event(device, time,
BTN_MIDDLE,
LIBINPUT_BUTTON_STATE_RELEASED);
middlebutton_set_state(device,
MIDDLEBUTTON_RIGHT_UP_PENDING,
time);
break;
case MIDDLEBUTTON_EVENT_TIMEOUT:
middlebutton_state_error(device, event);
break;
case MIDDLEBUTTON_EVENT_ALL_UP:
middlebutton_state_error(device, event);
break;
}
return 1;
}
static int
evdev_middlebutton_lup_pending_handle_event(struct evdev_device *device,
uint64_t time,
enum evdev_middlebutton_event event)
{
switch (event) {
case MIDDLEBUTTON_EVENT_L_DOWN:
middlebutton_state_error(device, event);
break;
case MIDDLEBUTTON_EVENT_R_DOWN:
middlebutton_post_event(device, time,
BTN_MIDDLE,
LIBINPUT_BUTTON_STATE_PRESSED);
middlebutton_set_state(device, MIDDLEBUTTON_MIDDLE, time);
break;
case MIDDLEBUTTON_EVENT_OTHER:
middlebutton_set_state(device, MIDDLEBUTTON_IGNORE_L, time);
return 0;
case MIDDLEBUTTON_EVENT_R_UP:
middlebutton_state_error(device, event);
break;
case MIDDLEBUTTON_EVENT_L_UP:
middlebutton_set_state(device, MIDDLEBUTTON_IDLE, time);
break;
case MIDDLEBUTTON_EVENT_TIMEOUT:
middlebutton_state_error(device, event);
break;
case MIDDLEBUTTON_EVENT_ALL_UP:
middlebutton_state_error(device, event);
break;
}
return 1;
}
static int
evdev_middlebutton_rup_pending_handle_event(struct evdev_device *device,
uint64_t time,
enum evdev_middlebutton_event event)
{
switch (event) {
case MIDDLEBUTTON_EVENT_L_DOWN:
middlebutton_post_event(device, time,
BTN_MIDDLE,
LIBINPUT_BUTTON_STATE_PRESSED);
middlebutton_set_state(device, MIDDLEBUTTON_MIDDLE, time);
break;
case MIDDLEBUTTON_EVENT_R_DOWN:
middlebutton_state_error(device, event);
break;
case MIDDLEBUTTON_EVENT_OTHER:
middlebutton_set_state(device, MIDDLEBUTTON_IGNORE_R, time);
return 0;
case MIDDLEBUTTON_EVENT_R_UP:
middlebutton_set_state(device, MIDDLEBUTTON_IDLE, time);
break;
case MIDDLEBUTTON_EVENT_L_UP:
middlebutton_state_error(device, event);
break;
case MIDDLEBUTTON_EVENT_TIMEOUT:
middlebutton_state_error(device, event);
break;
case MIDDLEBUTTON_EVENT_ALL_UP:
middlebutton_state_error(device, event);
break;
}
return 1;
}
static int
evdev_middlebutton_passthrough_handle_event(struct evdev_device *device,
uint64_t time,
enum evdev_middlebutton_event event)
{
switch (event) {
case MIDDLEBUTTON_EVENT_L_DOWN:
case MIDDLEBUTTON_EVENT_R_DOWN:
case MIDDLEBUTTON_EVENT_OTHER:
case MIDDLEBUTTON_EVENT_R_UP:
case MIDDLEBUTTON_EVENT_L_UP:
return 0;
case MIDDLEBUTTON_EVENT_TIMEOUT:
middlebutton_state_error(device, event);
break;
case MIDDLEBUTTON_EVENT_ALL_UP:
middlebutton_set_state(device, MIDDLEBUTTON_IDLE, time);
break;
}
return 1;
}
static int
evdev_middlebutton_ignore_lr_handle_event(struct evdev_device *device,
uint64_t time,
enum evdev_middlebutton_event event)
{
switch (event) {
case MIDDLEBUTTON_EVENT_L_DOWN:
case MIDDLEBUTTON_EVENT_R_DOWN:
middlebutton_state_error(device, event);
break;
case MIDDLEBUTTON_EVENT_OTHER:
return 0;
case MIDDLEBUTTON_EVENT_R_UP:
middlebutton_set_state(device, MIDDLEBUTTON_IGNORE_L, time);
break;
case MIDDLEBUTTON_EVENT_L_UP:
middlebutton_set_state(device, MIDDLEBUTTON_IGNORE_R, time);
break;
case MIDDLEBUTTON_EVENT_TIMEOUT:
middlebutton_state_error(device, event);
break;
case MIDDLEBUTTON_EVENT_ALL_UP:
middlebutton_state_error(device, event);
break;
}
return 1;
}
static int
evdev_middlebutton_ignore_l_handle_event(struct evdev_device *device,
uint64_t time,
enum evdev_middlebutton_event event)
{
switch (event) {
case MIDDLEBUTTON_EVENT_L_DOWN:
middlebutton_state_error(device, event);
break;
case MIDDLEBUTTON_EVENT_R_DOWN:
return 0;
case MIDDLEBUTTON_EVENT_OTHER:
case MIDDLEBUTTON_EVENT_R_UP:
return 0;
case MIDDLEBUTTON_EVENT_L_UP:
middlebutton_set_state(device,
MIDDLEBUTTON_PASSTHROUGH,
time);
break;
case MIDDLEBUTTON_EVENT_TIMEOUT:
case MIDDLEBUTTON_EVENT_ALL_UP:
middlebutton_state_error(device, event);
break;
}
return 1;
}
static int
evdev_middlebutton_ignore_r_handle_event(struct evdev_device *device,
uint64_t time,
enum evdev_middlebutton_event event)
{
switch (event) {
case MIDDLEBUTTON_EVENT_L_DOWN:
return 0;
case MIDDLEBUTTON_EVENT_R_DOWN:
middlebutton_state_error(device, event);
break;
case MIDDLEBUTTON_EVENT_OTHER:
return 0;
case MIDDLEBUTTON_EVENT_R_UP:
middlebutton_set_state(device,
MIDDLEBUTTON_PASSTHROUGH,
time);
break;
case MIDDLEBUTTON_EVENT_L_UP:
return 0;
case MIDDLEBUTTON_EVENT_TIMEOUT:
case MIDDLEBUTTON_EVENT_ALL_UP:
break;
}
return 1;
}
static int
evdev_middlebutton_handle_event(struct evdev_device *device,
uint64_t time,
enum evdev_middlebutton_event event)
{
int rc;
enum evdev_middlebutton_state current;
current = device->middlebutton.state;
switch (current) {
case MIDDLEBUTTON_IDLE:
rc = evdev_middlebutton_idle_handle_event(device, time, event);
break;
case MIDDLEBUTTON_LEFT_DOWN:
rc = evdev_middlebutton_ldown_handle_event(device, time, event);
break;
case MIDDLEBUTTON_RIGHT_DOWN:
rc = evdev_middlebutton_rdown_handle_event(device, time, event);
break;
case MIDDLEBUTTON_MIDDLE:
rc = evdev_middlebutton_middle_handle_event(device, time, event);
break;
case MIDDLEBUTTON_LEFT_UP_PENDING:
rc = evdev_middlebutton_lup_pending_handle_event(device,
time,
event);
break;
case MIDDLEBUTTON_RIGHT_UP_PENDING:
rc = evdev_middlebutton_rup_pending_handle_event(device,
time,
event);
break;
case MIDDLEBUTTON_PASSTHROUGH:
rc = evdev_middlebutton_passthrough_handle_event(device,
time,
event);
break;
case MIDDLEBUTTON_IGNORE_LR:
rc = evdev_middlebutton_ignore_lr_handle_event(device,
time,
event);
break;
case MIDDLEBUTTON_IGNORE_L:
rc = evdev_middlebutton_ignore_l_handle_event(device,
time,
event);
break;
case MIDDLEBUTTON_IGNORE_R:
rc = evdev_middlebutton_ignore_r_handle_event(device,
time,
event);
break;
}
log_debug(device->base.seat->libinput,
"middlebuttonstate: %s → %s → %s, rc %d\n",
middlebutton_state_to_str(current),
middlebutton_event_to_str(event),
middlebutton_state_to_str(device->middlebutton.state),
rc);
return rc;
}
static inline void
evdev_middlebutton_apply_config(struct evdev_device *device)
{
if (device->middlebutton.want_enabled ==
device->middlebutton.enabled)
return;
if (device->middlebutton.button_mask != 0)
return;
device->middlebutton.enabled = device->middlebutton.want_enabled;
}
bool
evdev_middlebutton_filter_button(struct evdev_device *device,
uint64_t time,
int button,
enum libinput_button_state state)
{
enum evdev_middlebutton_event event;
bool is_press = state == LIBINPUT_BUTTON_STATE_PRESSED;
int rc;
unsigned int bit = (button - BTN_LEFT);
uint32_t old_mask = 0;
if (!device->middlebutton.enabled)
return false;
switch (button) {
case BTN_LEFT:
if (is_press)
event = MIDDLEBUTTON_EVENT_L_DOWN;
else
event = MIDDLEBUTTON_EVENT_L_UP;
break;
case BTN_RIGHT:
if (is_press)
event = MIDDLEBUTTON_EVENT_R_DOWN;
else
event = MIDDLEBUTTON_EVENT_R_UP;
break;
/* BTN_MIDDLE counts as "other" and resets middle button
* emulation */
case BTN_MIDDLE:
default:
event = MIDDLEBUTTON_EVENT_OTHER;
break;
}
if (button < BTN_LEFT ||
bit >= sizeof(device->middlebutton.button_mask) * 8) {
log_bug_libinput(device->base.seat->libinput,
"Button mask too small for %d\n",
libevdev_event_code_get_name(EV_KEY,
button));
return true;
}
rc = evdev_middlebutton_handle_event(device, time, event);
old_mask = device->middlebutton.button_mask;
if (is_press)
device->middlebutton.button_mask |= 1 << bit;
else
device->middlebutton.button_mask &= ~(1 << bit);
if (old_mask != device->middlebutton.button_mask &&
device->middlebutton.button_mask == 0) {
evdev_middlebutton_handle_event(device,
time,
MIDDLEBUTTON_EVENT_ALL_UP);
evdev_middlebutton_apply_config(device);
}
return rc;
}
static void
evdev_middlebutton_handle_timeout(uint64_t now, void *data)
{
struct evdev_device *device = (struct evdev_device*)data;
evdev_middlebutton_handle_event(device,
libinput_now(device->base.seat->libinput),
MIDDLEBUTTON_EVENT_TIMEOUT);
}
static int
evdev_middlebutton_is_available(struct libinput_device *device)
{
return 1;
}
static enum libinput_config_status
evdev_middlebutton_set(struct libinput_device *device,
enum libinput_config_middle_emulation_state enable)
{
struct evdev_device *evdev = (struct evdev_device*)device;
switch (enable) {
case LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED:
evdev->middlebutton.want_enabled = true;
break;
case LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED:
evdev->middlebutton.want_enabled = false;
break;
default:
return LIBINPUT_CONFIG_STATUS_INVALID;
}
evdev_middlebutton_apply_config(evdev);
return LIBINPUT_CONFIG_STATUS_SUCCESS;
}
static enum libinput_config_middle_emulation_state
evdev_middlebutton_get(struct libinput_device *device)
{
struct evdev_device *evdev = (struct evdev_device*)device;
return evdev->middlebutton.enabled ?
LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED :
LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED;
}
static enum libinput_config_middle_emulation_state
evdev_middlebutton_get_default(struct libinput_device *device)
{
struct evdev_device *evdev = (struct evdev_device*)device;
return evdev->middlebutton.enabled_default ?
LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED :
LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED;
}
void
evdev_init_middlebutton(struct evdev_device *device,
bool enable,
bool want_config)
{
libinput_timer_init(&device->middlebutton.timer,
device->base.seat->libinput,
evdev_middlebutton_handle_timeout,
device);
device->middlebutton.enabled_default = enable;
device->middlebutton.want_enabled = enable;
device->middlebutton.enabled = enable;
if (!want_config)
return;
device->middlebutton.config.available = evdev_middlebutton_is_available;
device->middlebutton.config.set = evdev_middlebutton_set;
device->middlebutton.config.get = evdev_middlebutton_get;
device->middlebutton.config.get_default = evdev_middlebutton_get_default;
device->base.config.middle_emulation = &device->middlebutton.config;
}

View file

@ -143,6 +143,12 @@ evdev_pointer_notify_physical_button(struct evdev_device *device,
int button, int button,
enum libinput_button_state state) enum libinput_button_state state)
{ {
if (evdev_middlebutton_filter_button(device,
time,
button,
state))
return;
evdev_pointer_notify_button(device, time, button, state); evdev_pointer_notify_button(device, time, button, state);
} }

View file

@ -63,6 +63,29 @@ enum evdev_device_tags {
EVDEV_TAG_TOUCHPAD_TRACKPOINT = (1 << 3), EVDEV_TAG_TOUCHPAD_TRACKPOINT = (1 << 3),
}; };
enum evdev_middlebutton_state {
MIDDLEBUTTON_IDLE,
MIDDLEBUTTON_LEFT_DOWN,
MIDDLEBUTTON_RIGHT_DOWN,
MIDDLEBUTTON_MIDDLE,
MIDDLEBUTTON_LEFT_UP_PENDING,
MIDDLEBUTTON_RIGHT_UP_PENDING,
MIDDLEBUTTON_IGNORE_LR,
MIDDLEBUTTON_IGNORE_L,
MIDDLEBUTTON_IGNORE_R,
MIDDLEBUTTON_PASSTHROUGH,
};
enum evdev_middlebutton_event {
MIDDLEBUTTON_EVENT_L_DOWN,
MIDDLEBUTTON_EVENT_R_DOWN,
MIDDLEBUTTON_EVENT_OTHER,
MIDDLEBUTTON_EVENT_L_UP,
MIDDLEBUTTON_EVENT_R_UP,
MIDDLEBUTTON_EVENT_TIMEOUT,
MIDDLEBUTTON_EVENT_ALL_UP,
};
struct mt_slot { struct mt_slot {
int32_t seat_slot; int32_t seat_slot;
struct device_coords point; struct device_coords point;
@ -158,6 +181,18 @@ struct evdev_device {
void (*change_to_enabled)(struct evdev_device *device); void (*change_to_enabled)(struct evdev_device *device);
} left_handed; } left_handed;
struct {
struct libinput_device_config_middle_emulation config;
/* middle-button emulation enabled */
bool enabled;
bool enabled_default;
bool want_enabled;
enum evdev_middlebutton_state state;
struct libinput_timer timer;
uint32_t button_mask;
uint64_t first_event_time;
} middlebutton;
int dpi; /* HW resolution */ int dpi; /* HW resolution */
struct ratelimit syn_drop_limit; /* ratelimit for SYN_DROPPED logging */ struct ratelimit syn_drop_limit; /* ratelimit for SYN_DROPPED logging */
}; };
@ -332,6 +367,17 @@ evdev_device_remove(struct evdev_device *device);
void void
evdev_device_destroy(struct evdev_device *device); evdev_device_destroy(struct evdev_device *device);
bool
evdev_middlebutton_filter_button(struct evdev_device *device,
uint64_t time,
int button,
enum libinput_button_state state);
void
evdev_init_middlebutton(struct evdev_device *device,
bool enabled,
bool want_config);
static inline double static inline double
evdev_convert_to_mm(const struct input_absinfo *absinfo, double v) evdev_convert_to_mm(const struct input_absinfo *absinfo, double v)
{ {

View file

@ -1436,6 +1436,12 @@ litest_timeout_edgescroll(void)
msleep(300); msleep(300);
} }
void
litest_timeout_middlebutton(void)
{
msleep(70);
}
void void
litest_push_event_frame(struct litest_device *dev) litest_push_event_frame(struct litest_device *dev)
{ {

View file

@ -194,6 +194,7 @@ void litest_timeout_softbuttons(void);
void litest_timeout_buttonscroll(void); void litest_timeout_buttonscroll(void);
void litest_timeout_edgescroll(void); void litest_timeout_edgescroll(void);
void litest_timeout_finger_switch(void); void litest_timeout_finger_switch(void);
void litest_timeout_middlebutton(void);
void litest_push_event_frame(struct litest_device *dev); void litest_push_event_frame(struct litest_device *dev);
void litest_pop_event_frame(struct litest_device *dev); void litest_pop_event_frame(struct litest_device *dev);

View file

@ -2895,6 +2895,9 @@ START_TEST(touchpad_left_handed_delayed)
/* left-handed takes effect now */ /* left-handed takes effect now */
litest_button_click(dev, BTN_RIGHT, 1); litest_button_click(dev, BTN_RIGHT, 1);
libinput_dispatch(li);
litest_timeout_middlebutton();
libinput_dispatch(li);
litest_button_click(dev, BTN_LEFT, 1); litest_button_click(dev, BTN_LEFT, 1);
libinput_dispatch(li); libinput_dispatch(li);