/* * Copyright © 2014-2015 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 #include "evdev.h" #define MIDDLEBUTTON_TIMEOUT ms2us(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. */ 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) { evdev_log_bug_libinput(device, "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; } evdev_log_debug(device, "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) { evdev_log_bug_libinput(device, "Button mask too small for %s\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 = evdev_device(data); evdev_middlebutton_handle_event(device, now, MIDDLEBUTTON_EVENT_TIMEOUT); } 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 = 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; } enum libinput_config_middle_emulation_state evdev_middlebutton_get(struct libinput_device *device) { struct evdev_device *evdev = evdev_device(device); return evdev->middlebutton.want_enabled ? LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED : LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED; } enum libinput_config_middle_emulation_state evdev_middlebutton_get_default(struct libinput_device *device) { struct evdev_device *evdev = 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, evdev_libinput_context(device), 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; }