Commit 3d30015d authored by Peter Hutterer's avatar Peter Hutterer

Merge branch 'wip/sw-tablet-mode'

parents d92d8554 986604fd
......@@ -156,7 +156,6 @@ src_libinput = [
'src/libinput-private.h',
'src/evdev.c',
'src/evdev.h',
'src/evdev-lid.c',
'src/evdev-middle-button.c',
'src/evdev-mt-touchpad.c',
'src/evdev-mt-touchpad.h',
......@@ -566,6 +565,7 @@ if get_option('tests')
'test/litest-device-synaptics-st.c',
'test/litest-device-synaptics-t440.c',
'test/litest-device-synaptics-x1-carbon-3rd.c',
'test/litest-device-thinkpad-extrabuttons.c',
'test/litest-device-trackpoint.c',
'test/litest-device-touch-screen.c',
'test/litest-device-touchscreen-fuzz.c',
......
/*
* Copyright © 2017 James Ye <jye836@gmail.com>
* Copyright © 2017 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 "evdev.h"
#include "libinput-private.h"
struct lid_switch_dispatch {
struct evdev_dispatch base;
struct evdev_device *device;
enum switch_reliability reliability;
bool lid_is_closed;
bool lid_is_closed_client_state;
struct {
struct evdev_device *keyboard;
struct libinput_event_listener listener;
} keyboard;
};
static inline struct lid_switch_dispatch*
lid_dispatch(struct evdev_dispatch *dispatch)
{
evdev_verify_dispatch_type(dispatch, DISPATCH_LID_SWITCH);
return container_of(dispatch, struct lid_switch_dispatch, base);
}
static void
lid_switch_notify_toggle(struct lid_switch_dispatch *dispatch,
struct evdev_device *device,
uint64_t time)
{
if (dispatch->lid_is_closed ^ dispatch->lid_is_closed_client_state) {
switch_notify_toggle(&device->base,
time,
LIBINPUT_SWITCH_LID,
dispatch->lid_is_closed);
dispatch->lid_is_closed_client_state = dispatch->lid_is_closed;
}
}
static void
lid_switch_keyboard_event(uint64_t time,
struct libinput_event *event,
void *data)
{
struct lid_switch_dispatch *dispatch = lid_dispatch(data);
if (!dispatch->lid_is_closed)
return;
if (event->type != LIBINPUT_EVENT_KEYBOARD_KEY)
return;
if (dispatch->reliability == RELIABILITY_WRITE_OPEN) {
int fd = libevdev_get_fd(dispatch->device->evdev);
struct input_event ev[2] = {
{{ 0, 0 }, EV_SW, SW_LID, 0 },
{{ 0, 0 }, EV_SYN, SYN_REPORT, 0 },
};
(void)write(fd, ev, sizeof(ev));
/* In case write() fails, we sync the lid state manually
* regardless. */
}
/* Posting the event here means we preempt the keyboard events that
* caused us to wake up, so the lid event is always passed on before
* the key event.
*/
dispatch->lid_is_closed = false;
lid_switch_notify_toggle(dispatch, dispatch->device, time);
}
static void
lid_switch_toggle_keyboard_listener(struct lid_switch_dispatch *dispatch,
bool is_closed)
{
if (!dispatch->keyboard.keyboard)
return;
if (is_closed) {
libinput_device_add_event_listener(
&dispatch->keyboard.keyboard->base,
&dispatch->keyboard.listener,
lid_switch_keyboard_event,
dispatch);
} else {
libinput_device_remove_event_listener(
&dispatch->keyboard.listener);
libinput_device_init_event_listener(
&dispatch->keyboard.listener);
}
}
static void
lid_switch_process_switch(struct lid_switch_dispatch *dispatch,
struct evdev_device *device,
struct input_event *e,
uint64_t time)
{
bool is_closed;
switch (e->code) {
case SW_LID:
is_closed = !!e->value;
if (dispatch->lid_is_closed == is_closed)
return;
lid_switch_toggle_keyboard_listener(dispatch,
is_closed);
dispatch->lid_is_closed = is_closed;
lid_switch_notify_toggle(dispatch, device, time);
break;
}
}
static void
lid_switch_process(struct evdev_dispatch *evdev_dispatch,
struct evdev_device *device,
struct input_event *event,
uint64_t time)
{
struct lid_switch_dispatch *dispatch = lid_dispatch(evdev_dispatch);
switch (event->type) {
case EV_SW:
lid_switch_process_switch(dispatch, device, event, time);
break;
case EV_SYN:
break;
default:
assert(0 && "Unknown event type");
break;
}
}
static inline enum switch_reliability
evdev_read_switch_reliability_prop(struct evdev_device *device)
{
const char *prop;
enum switch_reliability r;
prop = udev_device_get_property_value(device->udev_device,
"LIBINPUT_ATTR_LID_SWITCH_RELIABILITY");
if (!parse_switch_reliability_property(prop, &r)) {
evdev_log_error(device,
"%s: switch reliability set to unknown value '%s'\n",
device->devname,
prop);
r = RELIABILITY_UNKNOWN;
} else if (r == RELIABILITY_WRITE_OPEN) {
evdev_log_info(device,
"%s: will write switch open events\n",
device->devname);
}
return r;
}
static void
lid_switch_remove(struct evdev_dispatch *evdev_dispatch)
{
struct lid_switch_dispatch *dispatch = lid_dispatch(evdev_dispatch);
if (!dispatch->keyboard.keyboard)
return;
libinput_device_remove_event_listener(&dispatch->keyboard.listener);
}
static void
lid_switch_destroy(struct evdev_dispatch *evdev_dispatch)
{
struct lid_switch_dispatch *dispatch = lid_dispatch(evdev_dispatch);
free(dispatch);
}
static void
lid_switch_pair_keyboard(struct evdev_device *lid_switch,
struct evdev_device *keyboard)
{
struct lid_switch_dispatch *dispatch =
lid_dispatch(lid_switch->dispatch);
if ((keyboard->tags & EVDEV_TAG_KEYBOARD) == 0)
return;
if (dispatch->keyboard.keyboard)
return;
if (keyboard->tags & EVDEV_TAG_INTERNAL_KEYBOARD) {
dispatch->keyboard.keyboard = keyboard;
evdev_log_debug(lid_switch,
"lid: keyboard paired with %s<->%s\n",
lid_switch->devname,
keyboard->devname);
/* We need to init the event listener now only if the reported state
* is closed. */
if (dispatch->lid_is_closed)
lid_switch_toggle_keyboard_listener(dispatch,
dispatch->lid_is_closed);
}
}
static void
lid_switch_interface_device_added(struct evdev_device *device,
struct evdev_device *added_device)
{
lid_switch_pair_keyboard(device, added_device);
}
static void
lid_switch_interface_device_removed(struct evdev_device *device,
struct evdev_device *removed_device)
{
struct lid_switch_dispatch *dispatch = lid_dispatch(device->dispatch);
if (removed_device == dispatch->keyboard.keyboard) {
libinput_device_remove_event_listener(
&dispatch->keyboard.listener);
libinput_device_init_event_listener(
&dispatch->keyboard.listener);
dispatch->keyboard.keyboard = NULL;
}
}
static void
lid_switch_sync_initial_state(struct evdev_device *device,
struct evdev_dispatch *evdev_dispatch)
{
struct lid_switch_dispatch *dispatch = lid_dispatch(device->dispatch);
struct libevdev *evdev = device->evdev;
dispatch->reliability = evdev_read_switch_reliability_prop(device);
dispatch->lid_is_closed = libevdev_get_event_value(evdev, EV_SW, SW_LID);
dispatch->lid_is_closed_client_state = false;
/* For the initial state sync, we depend on whether the lid switch
* is reliable. If we know it's reliable, we sync as expected.
* If we're not sure, we ignore the initial state and only sync on
* the first future lid close event. Laptops with a broken switch
* that always have the switch in 'on' state thus don't mess up our
* touchpad.
*/
if (dispatch->lid_is_closed &&
dispatch->reliability == RELIABILITY_RELIABLE) {
uint64_t time;
time = libinput_now(evdev_libinput_context(device));
lid_switch_notify_toggle(dispatch, device, time);
}
}
struct evdev_dispatch_interface lid_switch_interface = {
lid_switch_process,
NULL, /* suspend */
lid_switch_remove,
lid_switch_destroy,
lid_switch_interface_device_added,
lid_switch_interface_device_removed,
lid_switch_interface_device_removed, /* device_suspended, treat as remove */
lid_switch_interface_device_added, /* device_resumed, treat as add */
lid_switch_sync_initial_state,
NULL, /* toggle_touch */
};
struct evdev_dispatch *
evdev_lid_switch_dispatch_create(struct evdev_device *lid_device)
{
struct lid_switch_dispatch *dispatch;
int type;
dispatch = zalloc(sizeof *dispatch);
dispatch->base.dispatch_type = DISPATCH_LID_SWITCH;
dispatch->base.interface = &lid_switch_interface;
dispatch->device = lid_device;
libinput_device_init_event_listener(&dispatch->keyboard.listener);
evdev_init_sendevents(lid_device, &dispatch->base);
dispatch->lid_is_closed = false;
for (type = EV_KEY; type < EV_CNT; type++) {
if (type == EV_SW)
continue;
libevdev_disable_event_type(lid_device->evdev, type);
}
return &dispatch->base;
}
......@@ -1479,6 +1479,10 @@ tp_remove_sendevents(struct tp_dispatch *tp)
if (tp->lid_switch.lid_switch)
libinput_device_remove_event_listener(
&tp->lid_switch.listener);
if (tp->tablet_mode_switch.tablet_mode_switch)
libinput_device_remove_event_listener(
&tp->tablet_mode_switch.listener);
}
static void
......@@ -1861,23 +1865,34 @@ tp_pair_trackpoint(struct evdev_device *touchpad,
}
static void
tp_lid_switch_event(uint64_t time, struct libinput_event *event, void *data)
tp_switch_event(uint64_t time, struct libinput_event *event, void *data)
{
struct tp_dispatch *tp = data;
struct libinput_event_switch *swev;
const char *which = NULL;
if (libinput_event_get_type(event) != LIBINPUT_EVENT_SWITCH_TOGGLE)
return;
swev = libinput_event_get_switch_event(event);
switch (libinput_event_switch_get_switch(swev)) {
case LIBINPUT_SWITCH_LID:
which = "lid";
break;
case LIBINPUT_SWITCH_TABLET_MODE:
which = "tablet-mode";
break;
}
switch (libinput_event_switch_get_switch_state(swev)) {
case LIBINPUT_SWITCH_STATE_OFF:
tp_resume(tp, tp->device);
evdev_log_debug(tp->device, "lid: resume touchpad\n");
evdev_log_debug(tp->device, "%s: resume touchpad\n", which);
break;
case LIBINPUT_SWITCH_STATE_ON:
tp_suspend(tp, tp->device);
evdev_log_debug(tp->device, "lid: suspend touchpad\n");
evdev_log_debug(tp->device, "%s: suspend touchpad\n", which);
break;
}
}
......@@ -1899,11 +1914,39 @@ tp_pair_lid_switch(struct evdev_device *touchpad,
libinput_device_add_event_listener(&lid_switch->base,
&tp->lid_switch.listener,
tp_lid_switch_event, tp);
tp_switch_event, tp);
tp->lid_switch.lid_switch = lid_switch;
}
}
static void
tp_pair_tablet_mode_switch(struct evdev_device *touchpad,
struct evdev_device *tablet_mode_switch)
{
struct tp_dispatch *tp = (struct tp_dispatch*)touchpad->dispatch;
if ((tablet_mode_switch->tags & EVDEV_TAG_TABLET_MODE_SWITCH) == 0)
return;
if (tp->tablet_mode_switch.tablet_mode_switch == NULL) {
evdev_log_debug(touchpad,
"tablet_mode_switch: activated for %s<->%s\n",
touchpad->devname,
tablet_mode_switch->devname);
libinput_device_add_event_listener(&tablet_mode_switch->base,
&tp->tablet_mode_switch.listener,
tp_switch_event, tp);
tp->tablet_mode_switch.tablet_mode_switch = tablet_mode_switch;
if (evdev_device_switch_get_state(tablet_mode_switch,
LIBINPUT_SWITCH_TABLET_MODE)
== LIBINPUT_SWITCH_STATE_ON) {
tp_suspend(tp, touchpad);
}
}
}
static void
tp_interface_device_added(struct evdev_device *device,
struct evdev_device *added_device)
......@@ -1913,6 +1956,7 @@ tp_interface_device_added(struct evdev_device *device,
tp_pair_trackpoint(device, added_device);
tp_dwt_pair_keyboard(device, added_device);
tp_pair_lid_switch(device, added_device);
tp_pair_tablet_mode_switch(device, added_device);
if (tp->sendevents.current_mode !=
LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE)
......@@ -1954,6 +1998,12 @@ tp_interface_device_removed(struct evdev_device *device,
tp->lid_switch.lid_switch = NULL;
}
if (removed_device == tp->tablet_mode_switch.tablet_mode_switch) {
libinput_device_remove_event_listener(
&tp->tablet_mode_switch.listener);
tp->tablet_mode_switch.tablet_mode_switch = NULL;
}
if (tp->sendevents.current_mode !=
LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE)
return;
......
......@@ -407,6 +407,11 @@ struct tp_dispatch {
struct libinput_event_listener listener;
struct evdev_device *lid_switch;
} lid_switch;
struct {
struct libinput_event_listener listener;
struct evdev_device *tablet_mode_switch;
} tablet_mode_switch;
};
static inline struct tp_dispatch*
......
This diff is collapsed.
......@@ -75,6 +75,7 @@ enum evdev_device_tags {
EVDEV_TAG_LID_SWITCH = (1 << 5),
EVDEV_TAG_INTERNAL_KEYBOARD = (1 << 6),
EVDEV_TAG_EXTERNAL_KEYBOARD = (1 << 7),
EVDEV_TAG_TABLET_MODE_SWITCH = (1 << 8),
};
enum evdev_middlebutton_state {
......@@ -345,6 +346,7 @@ evdev_verify_dispatch_type(struct evdev_dispatch *dispatch,
struct fallback_dispatch {
struct evdev_dispatch base;
struct evdev_device *device;
struct libinput_device_config_calibration calibration;
......@@ -375,6 +377,10 @@ struct fallback_dispatch {
struct device_coords rel;
struct {
int state;
} tablet_mode;
/* Bitmask of pressed keys used to ignore initial release events from
* the kernel. */
unsigned long hw_key_mask[NLONGS(KEY_CNT)];
......@@ -391,6 +397,15 @@ struct fallback_dispatch {
uint64_t button_up_time;
struct libinput_timer timer;
} debounce;
struct {
enum switch_reliability reliability;
bool is_closed;
bool is_closed_client_state;
struct evdev_device *keyboard;
struct libinput_event_listener listener;
} lid;
};
static inline struct fallback_dispatch*
......@@ -489,6 +504,10 @@ evdev_device_has_button(struct evdev_device *device, uint32_t code);
int
evdev_device_has_key(struct evdev_device *device, uint32_t code);
int
evdev_device_has_switch(struct evdev_device *device,
enum libinput_switch sw);
int
evdev_device_tablet_pad_get_num_buttons(struct evdev_device *device);
......@@ -505,6 +524,10 @@ struct libinput_tablet_pad_mode_group *
evdev_device_tablet_pad_get_mode_group(struct evdev_device *device,
unsigned int index);
enum libinput_switch_state
evdev_device_switch_get_state(struct evdev_device *device,
enum libinput_switch sw);
double
evdev_device_transform_x(struct evdev_device *device,
double x,
......
......@@ -2936,6 +2936,13 @@ libinput_device_keyboard_has_key(struct libinput_device *device, uint32_t code)
return evdev_device_has_key((struct evdev_device *)device, code);
}
LIBINPUT_EXPORT int
libinput_device_switch_has_switch(struct libinput_device *device,
enum libinput_switch sw)
{
return evdev_device_has_switch((struct evdev_device *)device, sw);
}
LIBINPUT_EXPORT int
libinput_device_tablet_pad_get_num_buttons(struct libinput_device *device)
{
......
......@@ -624,6 +624,22 @@ enum libinput_switch {
* LIBINPUT_SWITCH_STATE_OFF.
*/
LIBINPUT_SWITCH_LID = 1,
/**
* This switch indicates whether the device is in normal laptop mode
* or behaves like a tablet-like device where the primary
* interaction is usually a touch screen. When in tablet mode, the
* keyboard and touchpad are usually inaccessible.
*
* If the switch is in state @ref LIBINPUT_SWITCH_STATE_OFF, the
* device is in laptop mode. If the switch is in state @ref
* LIBINPUT_SWITCH_STATE_ON, the device is in tablet mode and the
* keyboard or touchpad may not be accessible.
*
* It is up to the caller to identify which devices are inaccessible
* in tablet mode.
*/
LIBINPUT_SWITCH_TABLET_MODE,
};
/**
......@@ -3696,6 +3712,22 @@ int
libinput_device_keyboard_has_key(struct libinput_device *device,
uint32_t code);
/**
* @ingroup device
*
* Check if a @ref LIBINPUT_DEVICE_CAP_SWITCH device has a switch of the
* given type.
*
* @param device A current input device
* @param sw Switch to check for
*
* @return 1 if the device supports this switch, 0 if it does not, -1
* on error.
*/
int
libinput_device_switch_has_switch(struct libinput_device *device,
enum libinput_switch sw);
/**
* @ingroup device
*
......
......@@ -289,3 +289,7 @@ LIBINPUT_1.7 {
libinput_event_switch_get_time;
libinput_event_switch_get_time_usec;
} LIBINPUT_1.5;
LIBINPUT_1.9 {
libinput_device_switch_has_switch;
} LIBINPUT_1.7;
/*
* Copyright © 2017 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 "litest.h"
#include "litest-int.h"
static void litest_extrabuttons_setup(void)
{
struct litest_device *d = litest_create_device(LITEST_THINKPAD_EXTRABUTTONS);
litest_set_current_device(d);
}
static struct input_id input_id = {
.bustype = 0x19,
.vendor = 0x17aa,
.product = 0x5054,
};
static int events[] = {
EV_KEY, KEY_MUTE,
EV_KEY, KEY_VOLUMEUP,
EV_KEY, KEY_VOLUMEDOWN,
EV_KEY, KEY_SCALE,
EV_KEY, KEY_SLEEP,
EV_KEY, KEY_FILE,
EV_KEY, KEY_PROG1,
EV_KEY, KEY_COFFEE,
EV_KEY, KEY_BACK,
EV_KEY, KEY_CONFIG,
EV_KEY, KEY_REFRESH,
EV_KEY, KEY_F20,
EV_KEY, KEY_F21,
EV_KEY, KEY_F24,
EV_KEY, KEY_SUSPEND,
EV_KEY, KEY_CAMERA,
EV_KEY, KEY_SEARCH,
EV_KEY, KEY_BRIGHTNESSDOWN,
EV_KEY, KEY_BRIGHTNESSUP,
EV_KEY, KEY_SWITCHVIDEOMODE,
EV_KEY, KEY_KBDILLUMTOGGLE,
EV_KEY, KEY_BATTERY,
EV_KEY, KEY_WLAN,
EV_KEY, KEY_UNKNOWN,
EV_KEY, KEY_ZOOM,
EV_KEY, KEY_FN_F1,
EV_KEY, KEY_FN_F10,
EV_KEY, KEY_FN_F11,
EV_KEY, KEY_VOICECOMMAND,
EV_KEY, KEY_BRIGHTNESS_MIN,
EV_SW, SW_RFKILL_ALL,
EV_SW, SW_TABLET_MODE,
-1, -1,
};
struct litest_test_device litest_thinkpad_extrabuttons_device = {
.type = LITEST_THINKPAD_EXTRABUTTONS,
.features = LITEST_KEYS | LITEST_SWITCH,
.shortname = "thinkpad-extrabuttons",
.setup = litest_extrabuttons_setup,
.interface = NULL,
.name = "ThinkPad Extra Buttons",
.id = &input_id,
.events = events,
.absinfo = NULL,
};
......@@ -417,6 +417,7 @@ extern struct litest_test_device litest_appletouch_device;
extern struct litest_test_device litest_gpio_keys_device;
extern struct litest_test_device litest_ignored_mouse_device;
extern struct litest_test_device litest_wacom_mobilestudio_13hdt_pad_device;
extern struct litest_test_device litest_thinkpad_extrabuttons_device;
struct litest_test_device* devices[] = {
&litest_synaptics_clickpad_device,
......@@ -485,6 +486,7 @@ struct litest_test_device* devices[] = {
&litest_gpio_keys_device,
&litest_ignored_mouse_device,
&litest_wacom_mobilestudio_13hdt_pad_device,
&litest_thinkpad_extrabuttons_device,
NULL,
};
......@@ -2189,6 +2191,9 @@ litest_switch_action(struct litest_device *dev,
case LIBINPUT_SWITCH_LID:
code = SW_LID;
break;
case LIBINPUT_SWITCH_TABLET_MODE:
code = SW_TABLET_MODE;
break;
default:
litest_abort_msg("Invalid switch %d", sw);
break;
......
......@@ -237,6 +237,7 @@ enum litest_device_type {
LITEST_GPIO_KEYS,
LITEST_IGNORED_MOUSE,
LITEST_WACOM_MOBILESTUDIO_PRO_16_PAD,
LITEST_THINKPAD_EXTRABUTTONS,
};
enum litest_device_feature {
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment