diff options
35 files changed, 1697 insertions, 177 deletions
diff --git a/doc/Makefile.am b/doc/Makefile.am index 698a316..a92d791 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -22,6 +22,7 @@ header_files = \ $(srcdir)/reporting-bugs.dox \ $(srcdir)/scrolling.dox \ $(srcdir)/seats.dox \ + $(srcdir)/switches.dox \ $(srcdir)/t440-support.dox \ $(srcdir)/tablet-support.dox \ $(srcdir)/tapping.dox \ diff --git a/doc/switches.dox b/doc/switches.dox new file mode 100644 index 0000000..4bb2675 --- /dev/null +++ b/doc/switches.dox @@ -0,0 +1,16 @@ +/** +@page switches Switches + +libinput supports a couple of switches. Unlike button events that come in +press and release pairs, switches are usually toggled once and left at the +setting for an extended period of time. + +Only some switches are handled by libinput, see @ref libinput_switch for a +list of supported switches. Switch events are exposed to the caller, but +libinput may handle some switch events internally and enable or disable +specific features based on a switch state. + +The order of switch events is guaranteed to be correct, i.e., a switch will +never send consecutive switch on, or switch off, events. + +*/ diff --git a/src/Makefile.am b/src/Makefile.am index a7ce472..08e6aa1 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -11,6 +11,7 @@ libinput_la_SOURCES = \ libinput-private.h \ evdev.c \ evdev.h \ + evdev-lid.c \ evdev-middle-button.c \ evdev-mt-touchpad.c \ evdev-mt-touchpad.h \ diff --git a/src/evdev-lid.c b/src/evdev-lid.c new file mode 100644 index 0000000..6a2b506 --- /dev/null +++ b/src/evdev-lid.c @@ -0,0 +1,302 @@ +/* + * 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; + + struct { + struct evdev_device *keyboard; + struct libinput_event_listener listener; + } keyboard; +}; + +static inline struct lid_switch_dispatch* +lid_dispatch(struct evdev_dispatch *dispatch) +{ + struct lid_switch_dispatch *l; + + evdev_verify_dispatch_type(dispatch, DISPATCH_LID_SWITCH); + + return container_of(dispatch, l, base); +} + +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. */ + } + + dispatch->lid_is_closed = false; + switch_notify_toggle(&dispatch->device->base, + time, + LIBINPUT_SWITCH_LID, + dispatch->lid_is_closed); +} + +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); + } +} + +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; + + switch_notify_toggle(&device->base, + time, + LIBINPUT_SWITCH_LID, + dispatch->lid_is_closed); + 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)) { + log_error(evdev_libinput_context(device), + "%s: switch reliability set to unknown value '%s'\n", + device->devname, + prop); + r = RELIABILITY_UNKNOWN; + } else if (r == RELIABILITY_WRITE_OPEN) { + log_info(evdev_libinput_context(device), + "%s: will write switch open events\n", + device->devname); + } + + return r; +} + +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); + unsigned int bus_kbd = libevdev_get_id_bustype(keyboard->evdev); + + if ((keyboard->tags & EVDEV_TAG_KEYBOARD) == 0) + return; + + /* If we already have a keyboard paired, override it if the new one + * is a serio device. Otherwise keep the current one */ + if (dispatch->keyboard.keyboard) { + if (bus_kbd != BUS_I8042) + return; + libinput_device_remove_event_listener(&dispatch->keyboard.listener); + } + + dispatch->keyboard.keyboard = keyboard; + log_debug(evdev_libinput_context(lid_switch), + "lid: keyboard paired with %s<->%s\n", + lid_switch->devname, + keyboard->devname); + + /* We don't init the event listener yet - we don't care about + * keyboard events until the 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); + 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; + bool is_closed = false; + + dispatch->reliability = evdev_read_switch_reliability_prop(device); + + /* 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. + */ + switch(dispatch->reliability) { + case RELIABILITY_UNKNOWN: + case RELIABILITY_WRITE_OPEN: + is_closed = false; + break; + case RELIABILITY_RELIABLE: + is_closed = libevdev_get_event_value(evdev, EV_SW, SW_LID); + break; + } + + dispatch->lid_is_closed = is_closed; + if (dispatch->lid_is_closed) { + uint64_t time; + time = libinput_now(evdev_libinput_context(device)); + switch_notify_toggle(&device->base, + time, + LIBINPUT_SWITCH_LID, + LIBINPUT_SWITCH_STATE_ON); + } +} + +struct evdev_dispatch_interface lid_switch_interface = { + lid_switch_process, + NULL, /* suspend */ + NULL, /* 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 = zalloc(sizeof *dispatch); + + if (dispatch == NULL) + return NULL; + + 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; + + return &dispatch->base; +} diff --git a/src/evdev-middle-button.c b/src/evdev-middle-button.c index 09f77de..d9330ba 100644 --- a/src/evdev-middle-button.c +++ b/src/evdev-middle-button.c @@ -638,7 +638,7 @@ evdev_middlebutton_filter_button(struct evdev_device *device, static void evdev_middlebutton_handle_timeout(uint64_t now, void *data) { - struct evdev_device *device = (struct evdev_device*)data; + struct evdev_device *device = evdev_device(data); evdev_middlebutton_handle_event(device, now, MIDDLEBUTTON_EVENT_TIMEOUT); } @@ -653,7 +653,7 @@ 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; + struct evdev_device *evdev = evdev_device(device); switch (enable) { case LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED: @@ -674,7 +674,7 @@ evdev_middlebutton_set(struct libinput_device *device, enum libinput_config_middle_emulation_state evdev_middlebutton_get(struct libinput_device *device) { - struct evdev_device *evdev = (struct evdev_device*)device; + struct evdev_device *evdev = evdev_device(device); return evdev->middlebutton.want_enabled ? LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED : @@ -684,7 +684,7 @@ evdev_middlebutton_get(struct libinput_device *device) enum libinput_config_middle_emulation_state evdev_middlebutton_get_default(struct libinput_device *device) { - struct evdev_device *evdev = (struct evdev_device*)device; + struct evdev_device *evdev = evdev_device(device); return evdev->middlebutton.enabled_default ? LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED : diff --git a/src/evdev-mt-touchpad-buttons.c b/src/evdev-mt-touchpad-buttons.c index f4fe6b7..4a68470 100644 --- a/src/evdev-mt-touchpad-buttons.c +++ b/src/evdev-mt-touchpad-buttons.c @@ -627,7 +627,7 @@ tp_init_top_softbuttons(struct tp_dispatch *tp, static inline uint32_t tp_button_config_click_get_methods(struct libinput_device *device) { - struct evdev_device *evdev = (struct evdev_device*)device; + struct evdev_device *evdev = evdev_device(device); struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch; uint32_t methods = LIBINPUT_CONFIG_CLICK_METHOD_NONE; @@ -669,7 +669,7 @@ static enum libinput_config_status tp_button_config_click_set_method(struct libinput_device *device, enum libinput_config_click_method method) { - struct evdev_device *evdev = (struct evdev_device*)device; + struct evdev_device *evdev = evdev_device(device); struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch; tp->buttons.click_method = method; @@ -681,7 +681,7 @@ tp_button_config_click_set_method(struct libinput_device *device, static enum libinput_config_click_method tp_button_config_click_get_method(struct libinput_device *device) { - struct evdev_device *evdev = (struct evdev_device*)device; + struct evdev_device *evdev = evdev_device(device); struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch; return tp->buttons.click_method; @@ -711,7 +711,7 @@ tp_click_get_default_method(struct tp_dispatch *tp) static enum libinput_config_click_method tp_button_config_click_get_default_method(struct libinput_device *device) { - struct evdev_device *evdev = (struct evdev_device*)device; + struct evdev_device *evdev = evdev_device(device); struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch; return tp_click_get_default_method(tp); @@ -746,7 +746,7 @@ static enum libinput_config_status tp_clickpad_middlebutton_set(struct libinput_device *device, enum libinput_config_middle_emulation_state enable) { - struct evdev_device *evdev = (struct evdev_device*)device; + struct evdev_device *evdev = evdev_device(device); switch (enable) { case LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED: diff --git a/src/evdev-mt-touchpad-tap.c b/src/evdev-mt-touchpad-tap.c index 9fba521..1829732 100644 --- a/src/evdev-mt-touchpad-tap.c +++ b/src/evdev-mt-touchpad-tap.c @@ -897,11 +897,8 @@ tp_tap_enabled_update(struct tp_dispatch *tp, bool suspended, bool enabled, uint static int tp_tap_config_count(struct libinput_device *device) { - struct evdev_dispatch *dispatch; - struct tp_dispatch *tp = NULL; - - dispatch = ((struct evdev_device *) device)->dispatch; - tp = container_of(dispatch, tp, base); + struct evdev_dispatch *dispatch = evdev_device(device)->dispatch; + struct tp_dispatch *tp = tp_dispatch(dispatch); return min(tp->ntouches, 3U); /* we only do up to 3 finger tap */ } @@ -910,10 +907,9 @@ static enum libinput_config_status tp_tap_config_set_enabled(struct libinput_device *device, enum libinput_config_tap_state enabled) { - struct evdev_dispatch *dispatch = ((struct evdev_device *) device)->dispatch; - struct tp_dispatch *tp = NULL; + struct evdev_dispatch *dispatch = evdev_device(device)->dispatch; + struct tp_dispatch *tp = tp_dispatch(dispatch); - tp = container_of(dispatch, tp, base); tp_tap_enabled_update(tp, tp->tap.suspended, (enabled == LIBINPUT_CONFIG_TAP_ENABLED), libinput_now(device->seat->libinput)); @@ -924,11 +920,8 @@ tp_tap_config_set_enabled(struct libinput_device *device, static enum libinput_config_tap_state tp_tap_config_is_enabled(struct libinput_device *device) { - struct evdev_dispatch *dispatch; - struct tp_dispatch *tp = NULL; - - dispatch = ((struct evdev_device *) device)->dispatch; - tp = container_of(dispatch, tp, base); + struct evdev_dispatch *dispatch = evdev_device(device)->dispatch; + struct tp_dispatch *tp = tp_dispatch(dispatch); return tp->tap.enabled ? LIBINPUT_CONFIG_TAP_ENABLED : LIBINPUT_CONFIG_TAP_DISABLED; @@ -959,7 +952,7 @@ tp_tap_default(struct evdev_device *evdev) static enum libinput_config_tap_state tp_tap_config_get_default(struct libinput_device *device) { - struct evdev_device *evdev = (struct evdev_device *)device; + struct evdev_device *evdev = evdev_device(device); return tp_tap_default(evdev); } @@ -968,10 +961,9 @@ static enum libinput_config_status tp_tap_config_set_map(struct libinput_device *device, enum libinput_config_tap_button_map map) { - struct evdev_dispatch *dispatch = ((struct evdev_device *) device)->dispatch; - struct tp_dispatch *tp = NULL; + struct evdev_dispatch *dispatch = evdev_device(device)->dispatch; + struct tp_dispatch *tp = tp_dispatch(dispatch); - tp = container_of(dispatch, tp, base); tp->tap.want_map = map; tp_tap_update_map(tp); @@ -982,10 +974,8 @@ tp_tap_config_set_map(struct libinput_device *device, static enum libinput_config_tap_button_map tp_tap_config_get_map(struct libinput_device *device) { - struct evdev_dispatch *dispatch = ((struct evdev_device *) device)->dispatch; - struct tp_dispatch *tp = NULL; - - tp = container_of(dispatch, tp, base); + struct evdev_dispatch *dispatch = evdev_device(device)->dispatch; + struct tp_dispatch *tp = tp_dispatch(dispatch); return tp->tap.want_map; } @@ -1000,10 +990,9 @@ static enum libinput_config_status tp_tap_config_set_drag_enabled(struct libinput_device *device, enum libinput_config_drag_state enabled) { - struct evdev_dispatch *dispatch = ((struct evdev_device *) device)->dispatch; - struct tp_dispatch *tp = NULL; + struct evdev_dispatch *dispatch = evdev_device(device)->dispatch; + struct tp_dispatch *tp = tp_dispatch(dispatch); - tp = container_of(dispatch, tp, base); tp->tap.drag_enabled = enabled; return LIBINPUT_CONFIG_STATUS_SUCCESS; @@ -1012,10 +1001,8 @@ tp_tap_config_set_drag_enabled(struct libinput_device *device, static enum libinput_config_drag_state tp_tap_config_get_drag_enabled(struct libinput_device *device) { - struct evdev_device *evdev = (struct evdev_device *)device; - struct tp_dispatch *tp = NULL; - - tp = container_of(evdev->dispatch, tp, base); + struct evdev_dispatch *dispatch = evdev_device(device)->dispatch; + struct tp_dispatch *tp = tp_dispatch(dispatch); return tp->tap.drag_enabled; } @@ -1029,7 +1016,7 @@ tp_drag_default(struct evdev_device *device) static enum libinput_config_drag_state tp_tap_config_get_default_drag_enabled(struct libinput_device *device) { - struct evdev_device *evdev = (struct evdev_device *)device; + struct evdev_device *evdev = evdev_device(device); return tp_drag_default(evdev); } @@ -1038,10 +1025,9 @@ static enum libinput_config_status tp_tap_config_set_draglock_enabled(struct libinput_device *device, enum libinput_config_drag_lock_state enabled) { - struct evdev_dispatch *dispatch = ((struct evdev_device *) device)->dispatch; - struct tp_dispatch *tp = NULL; + struct evdev_dispatch *dispatch = evdev_device(device)->dispatch; + struct tp_dispatch *tp = tp_dispatch(dispatch); - tp = container_of(dispatch, tp, base); tp->tap.drag_lock_enabled = enabled; return LIBINPUT_CONFIG_STATUS_SUCCESS; @@ -1050,10 +1036,8 @@ tp_tap_config_set_draglock_enabled(struct libinput_device *device, static enum libinput_config_drag_lock_state tp_tap_config_get_draglock_enabled(struct libinput_device *device) { - struct evdev_device *evdev = (struct evdev_device *)device; - struct tp_dispatch *tp = NULL; - - tp = container_of(evdev->dispatch, tp, base); + struct evdev_dispatch *dispatch = evdev_device(device)->dispatch; + struct tp_dispatch *tp = tp_dispatch(dispatch); return tp->tap.drag_lock_enabled; } @@ -1067,7 +1051,7 @@ tp_drag_lock_default(struct evdev_device *device) static enum libinput_config_drag_lock_state tp_tap_config_get_default_draglock_enabled(struct libinput_device *device) { - struct evdev_device *evdev = (struct evdev_device *)device; + struct evdev_device *evdev = evdev_device(device); return tp_drag_lock_default(evdev); } diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index e43a9ba..b10ac8b 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -1140,8 +1140,7 @@ tp_interface_process(struct evdev_dispatch *dispatch, struct input_event *e, uint64_t time) { - struct tp_dispatch *tp = - (struct tp_dispatch *)dispatch; + struct tp_dispatch *tp = tp_dispatch(dispatch); if (tp->ignore_events) return; @@ -1176,13 +1175,16 @@ tp_remove_sendevents(struct tp_dispatch *tp) if (tp->dwt.keyboard) libinput_device_remove_event_listener( &tp->dwt.keyboard_listener); + + if (tp->lid_switch.lid_switch) + libinput_device_remove_event_listener( + &tp->lid_switch.lid_switch_listener); } static void tp_interface_remove(struct evdev_dispatch *dispatch) { - struct tp_dispatch *tp = - (struct tp_dispatch*)dispatch; + struct tp_dispatch *tp = tp_dispatch(dispatch); tp_remove_tap(tp); tp_remove_buttons(tp); @@ -1194,8 +1196,7 @@ tp_interface_remove(struct evdev_dispatch *dispatch) static void tp_interface_destroy(struct evdev_dispatch *dispatch) { - struct tp_dispatch *tp = - (struct tp_dispatch*)dispatch; + struct tp_dispatch *tp = tp_dispatch(dispatch); free(tp->touches); free(tp); @@ -1257,7 +1258,7 @@ static void tp_interface_suspend(struct evdev_dispatch *dispatch, struct evdev_device *device) { - struct tp_dispatch *tp = (struct tp_dispatch *)dispatch; + struct tp_dispatch *tp = tp_dispatch(dispatch); tp_clear_state(tp); } @@ -1553,6 +1554,50 @@ tp_pair_trackpoint(struct evdev_device *touchpad, } static void +tp_lid_switch_event(uint64_t time, struct libinput_event *event, void *data) +{ + struct tp_dispatch *tp = data; + struct libinput_event_switch *swev; + + if (libinput_event_get_type(event) != LIBINPUT_EVENT_SWITCH_TOGGLE) + return; + + swev = libinput_event_get_switch_event(event); + switch (libinput_event_switch_get_switch_state(swev)) { + case LIBINPUT_SWITCH_STATE_OFF: + tp_resume(tp, tp->device); + log_debug(tp_libinput_context(tp), "lid: resume touchpad\n"); + break; + case LIBINPUT_SWITCH_STATE_ON: + tp_suspend(tp, tp->device); + log_debug(tp_libinput_context(tp), "lid: suspend touchpad\n"); + break; + } +} + +static void +tp_pair_lid_switch(struct evdev_device *touchpad, + struct evdev_device *lid_switch) +{ + struct tp_dispatch *tp = (struct tp_dispatch*)touchpad->dispatch; + + if ((lid_switch->tags & EVDEV_TAG_LID_SWITCH) == 0) + return; + + if (tp->lid_switch.lid_switch == NULL) { + log_debug(tp_libinput_context(tp), + "lid_switch: activated for %s<->%s\n", + touchpad->devname, + lid_switch->devname); + + libinput_device_add_event_listener(&lid_switch->base, + &tp->lid_switch.lid_switch_listener, + tp_lid_switch_event, tp); + tp->lid_switch.lid_switch = lid_switch; + } +} + +static void tp_interface_device_added(struct evdev_device *device, struct evdev_device *added_device) { @@ -1560,6 +1605,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); if (tp->sendevents.current_mode != LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE) @@ -1599,7 +1645,7 @@ tp_interface_device_removed(struct evdev_device *device, return; list_for_each(dev, &device->base.seat->devices_list, link) { - struct evdev_device *d = (struct evdev_device*)dev; + struct evdev_device *d = evdev_device(dev); if (d != removed_device && (d->tags & EVDEV_TAG_EXTERNAL_MOUSE)) { return; @@ -1693,7 +1739,7 @@ tp_interface_toggle_touch(struct evdev_dispatch *dispatch, struct evdev_device *device, bool enable) { - struct tp_dispatch *tp = (struct tp_dispatch*)dispatch; + struct tp_dispatch *tp = tp_dispatch(dispatch); bool ignore_events = !enable; if (ignore_events == tp->ignore_events) @@ -1936,7 +1982,7 @@ tp_scroll_get_methods(struct tp_dispatch *tp) static uint32_t tp_scroll_config_scroll_method_get_methods(struct libinput_device *device) { - struct evdev_device *evdev = (struct evdev_device*)device; + struct evdev_device *evdev = evdev_device(device); struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch; return tp_scroll_get_methods(tp); @@ -1946,7 +1992,7 @@ static enum libinput_config_status tp_scroll_config_scroll_method_set_method(struct libinput_device *device, enum libinput_config_scroll_method method) { - struct evdev_device *evdev = (struct evdev_device*)device; + struct evdev_device *evdev = evdev_device(device); struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch; uint64_t time = libinput_now(tp_libinput_context(tp)); @@ -1964,7 +2010,7 @@ tp_scroll_config_scroll_method_set_method(struct libinput_device *device, static enum libinput_config_scroll_method tp_scroll_config_scroll_method_get_method(struct libinput_device *device) { - struct evdev_device *evdev = (struct evdev_device*)device; + struct evdev_device *evdev = evdev_device(device); struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch; return tp->scroll.method; @@ -1993,7 +2039,7 @@ tp_scroll_get_default_method(struct tp_dispatch *tp) static enum libinput_config_scroll_method tp_scroll_config_scroll_method_get_default_method(struct libinput_device *device) { - struct evdev_device *evdev = (struct evdev_device*)device; + struct evdev_device *evdev = evdev_device(device); struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch; return tp_scroll_get_default_method(tp); @@ -2028,7 +2074,7 @@ static enum libinput_config_status tp_dwt_config_set(struct libinput_device *device, enum libinput_config_dwt_state enable) { - struct evdev_device *evdev = (struct evdev_device*)device; + struct evdev_device *evdev = evdev_device(device); struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch; switch(enable) { @@ -2047,7 +2093,7 @@ tp_dwt_config_set(struct libinput_device *device, static enum libinput_config_dwt_state tp_dwt_config_get(struct libinput_device *device) { - struct evdev_device *evdev = (struct evdev_device*)device; + struct evdev_device *evdev = evdev_device(device); struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch; return tp->dwt.dwt_enabled ? @@ -2064,7 +2110,7 @@ tp_dwt_default_enabled(struct tp_dispatch *tp) static enum libinput_config_dwt_state tp_dwt_config_get_default(struct libinput_device *device) { - struct evdev_device *evdev = (struct evdev_device*)device; + struct evdev_device *evdev = evdev_device(device); struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch; return tp_dwt_default_enabled(tp) ? @@ -2271,6 +2317,7 @@ static int tp_init(struct tp_dispatch *tp, struct evdev_device *device) { + tp->base.dispatch_type = DISPATCH_TOUCHPAD; tp->base.interface = &tp_interface; tp->device = device; @@ -2316,7 +2363,7 @@ tp_init(struct tp_dispatch *tp, static uint32_t tp_sendevents_get_modes(struct libinput_device *device) { - struct evdev_device *evdev = (struct evdev_device*)device; + struct evdev_device *evdev = evdev_device(device); uint32_t modes = LIBINPUT_CONFIG_SEND_EVENTS_DISABLED; if (evdev->tags & EVDEV_TAG_INTERNAL_TOUCHPAD) @@ -2332,7 +2379,7 @@ tp_suspend_conditional(struct tp_dispatch *tp, struct libinput_device *dev; list_for_each(dev, &device->base.seat->devices_list, link) { - struct evdev_device *d = (struct evdev_device*)dev; + struct evdev_device *d = evdev_device(dev); if (d->tags & EVDEV_TAG_EXTERNAL_MOUSE) { tp_suspend(tp, device); return; @@ -2344,7 +2391,7 @@ static enum libinput_config_status tp_sendevents_set_mode(struct libinput_device *device, enum libinput_config_send_events_mode mode) { - struct evdev_device *evdev = (struct evdev_device*)device; + struct evdev_device *evdev = evdev_device(device); struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch; /* DISABLED overrides any DISABLED_ON_ */ @@ -2377,7 +2424,7 @@ tp_sendevents_set_mode(struct libinput_device *device, static enum libinput_config_send_events_mode tp_sendevents_get_mode(struct libinput_device *device) { - struct evdev_device *evdev = (struct evdev_device*)device; + struct evdev_device *evdev = evdev_device(device); struct tp_dispatch *dispatch = (struct tp_dispatch*)evdev->dispatch; return dispatch->sendevents.current_mode; diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h index fb15956..a54eb5c 100644 --- a/src/evdev-mt-touchpad.h +++ b/src/evdev-mt-touchpad.h @@ -376,8 +376,23 @@ struct tp_dispatch { */ unsigned int nonmotion_event_count; } quirks; + + struct { + struct libinput_event_listener lid_switch_listener; + struct evdev_device *lid_switch; + } lid_switch; }; +static inline struct tp_dispatch* +tp_dispatch(struct evdev_dispatch *dispatch) +{ + struct tp_dispatch *tp; + + evdev_verify_dispatch_type(dispatch, DISPATCH_TOUCHPAD); + + return container_of(dispatch, tp, base); +} + #define tp_for_each_touch(_tp, _t) \ for (unsigned int _i = 0; _i < (_tp)->ntouches && (_t = &(_tp)->touches[_i]); _i++) diff --git a/src/evdev-tablet-pad.c b/src/evdev-tablet-pad.c index 82542bc..bed43b6 100644 --- a/src/evdev-tablet-pad.c +++ b/src/evdev-tablet-pad.c @@ -452,7 +452,7 @@ pad_process(struct evdev_dispatch *dispatch, struct input_event *e, uint64_t time) { - struct pad_dispatch *pad = (struct pad_dispatch *)dispatch; + struct pad_dispatch *pad = pad_dispatch(dispatch); switch (e->type) { case EV_ABS: @@ -481,7 +481,7 @@ static void pad_suspend(struct evdev_dispatch *dispatch, struct evdev_device *device) { - struct pad_dispatch *pad = (struct pad_dispatch *)dispatch; + struct pad_dispatch *pad = pad_dispatch(dispatch); struct libinput *libinput = pad_libinput_context(pad); unsigned int code; @@ -496,7 +496,7 @@ pad_suspend(struct evdev_dispatch *dispatch, static void pad_destroy(struct evdev_dispatch *dispatch) { - struct pad_dispatch *pad = (struct pad_dispatch*)dispatch; + struct pad_dispatch *pad = pad_dispatch(dispatch); pad_destroy_leds(pad); free(pad); @@ -556,6 +556,7 @@ pad_init_left_handed(struct evdev_device *device) static int pad_init(struct pad_dispatch *pad, struct evdev_device *device) { + pad->base.dispatch_type = DISPATCH_TABLET_PAD; pad->base.interface = &pad_interface; pad->device = device; pad->status = PAD_NONE; @@ -579,7 +580,7 @@ static enum libinput_config_status pad_sendevents_set_mode(struct libinput_device *device, enum libinput_config_send_events_mode mode) { - struct evdev_device *evdev = (struct evdev_device*)device; + struct evdev_device *evdev = evdev_device(device); struct pad_dispatch *pad = (struct pad_dispatch*)evdev->dispatch; if (mode == pad->sendevents.current_mode) @@ -603,7 +604,7 @@ pad_sendevents_set_mode(struct libinput_device *device, static enum libinput_config_send_events_mode pad_sendevents_get_mode(struct libinput_device *device) { - struct evdev_device *evdev = (struct evdev_device*)device; + struct evdev_device *evdev = evdev_device(device); struct pad_dispatch *dispatch = (struct pad_dispatch*)evdev->dispatch; return dispatch->sendevents.current_mode; diff --git a/src/evdev-tablet-pad.h b/src/evdev-tablet-pad.h index 9002fca..5569007 100644 --- a/src/evdev-tablet-pad.h +++ b/src/evdev-tablet-pad.h @@ -70,6 +70,16 @@ struct pad_dispatch { } modes; }; +static inline struct pad_dispatch* +pad_dispatch(struct evdev_dispatch *dispatch) +{ + struct pad_dispatch *p; + + evdev_verify_dispatch_type(dispatch, DISPATCH_TABLET_PAD); + + return container_of(dispatch, p, base); +} + static inline struct libinput * pad_libinput_context(const struct pad_dispatch *pad) { diff --git a/src/evdev-tablet.c b/src/evdev-tablet.c index b76d8ea..12a014b 100644 --- a/src/evdev-tablet.c +++ b/src/evdev-tablet.c @@ -201,8 +201,7 @@ tablet_process_absolute(struct tablet_dispatch *tablet, static void tablet_change_to_left_handed(struct evdev_device *device) { - struct tablet_dispatch *tablet = - (struct tablet_dispatch*)device->dispatch; + struct tablet_dispatch *tablet = tablet_dispatch(device->dispatch); if (device->left_handed.enabled == device->left_handed.want_enabled) return; @@ -1484,8 +1483,7 @@ tablet_process(struct evdev_dispatch *dispatch, struct input_event *e, uint64_t time) { - struct tablet_dispatch *tablet = - (struct tablet_dispatch *)dispatch; + struct tablet_dispatch *tablet = tablet_dispatch(dispatch); switch (e->type) { case EV_ABS: @@ -1518,8 +1516,7 @@ static void tablet_suspend(struct evdev_dispatch *dispatch, struct evdev_device *device) { - struct tablet_dispatch *tablet = - (struct tablet_dispatch *)dispatch; + struct tablet_dispatch *tablet = tablet_dispatch(dispatch); tablet_set_touch_device_enabled(tablet->touch_device, true); } @@ -1527,8 +1524,7 @@ tablet_suspend(struct evdev_dispatch *dispatch, static void tablet_destroy(struct evdev_dispatch *dispatch) { - struct tablet_dispatch *tablet = - (struct tablet_dispatch*)dispatch; + struct tablet_dispatch *tablet = tablet_dispatch(dispatch); struct libinput_tablet_tool *tool, *tmp; list_for_each_safe(tool, tmp, &tablet->tool_list, link) { @@ -1542,8 +1538,7 @@ static void tablet_device_added(struct evdev_device *device, struct evdev_device *added_device) { - struct tablet_dispatch *tablet = - (struct tablet_dispatch*)device->dispatch; + struct tablet_dispatch *tablet = tablet_dispatch(device->dispatch); if (libinput_device_get_device_group(&device->base) != libinput_device_get_device_group(&added_device->base)) @@ -1560,8 +1555,7 @@ static void tablet_device_removed(struct evdev_device *device, struct evdev_device *removed_device) { - struct tablet_dispatch *tablet = - (struct tablet_dispatch*)device->dispatch; + struct tablet_dispatch *tablet = tablet_dispatch(device->dispatch); if (tablet->touch_device == removed_device) tablet->touch_device = NULL; @@ -1571,10 +1565,10 @@ static void tablet_check_initial_proximity(struct evdev_device *device, struct evdev_dispatch *dispatch) { + struct tablet_dispatch *tablet = tablet_dispatch(dispatch); bool tool_in_prox = false; int code, state; enum libinput_tablet_tool_type tool; - struct tablet_dispatch *tablet = (struct tablet_dispatch*)dispatch; for (tool = LIBINPUT_TABLET_TOOL_TYPE_PEN; tool <= LIBINPUT_TABLET_TOOL_TYPE_MAX; tool++) { code = tablet_tool_to_evcode(tool); @@ -1746,6 +1740,7 @@ tablet_init(struct tablet_dispatch *tablet, enum libinput_tablet_tool_axis axis; int rc; + tablet->base.dispatch_type = DISPATCH_TABLET; tablet->base.interface = &tablet_interface; tablet->device = device; tablet->status = TABLET_NONE; diff --git a/src/evdev-tablet.h b/src/evdev-tablet.h index 2279e03..2b2b1a7 100644 --- a/src/evdev-tablet.h +++ b/src/evdev-tablet.h @@ -76,6 +76,16 @@ struct tablet_dispatch { struct evdev_device *touch_device; }; +static inline struct tablet_dispatch* +tablet_dispatch(struct evdev_dispatch *dispatch) +{ + struct tablet_dispatch *t; + + evdev_verify_dispatch_type(dispatch, DISPATCH_TABLET); + + return container_of(dispatch, t, base); +} + static inline enum libinput_tablet_tool_axis evcode_to_axis(const uint32_t evcode) { diff --git a/src/evdev.c b/src/evdev.c index d086c18..afee590 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -68,6 +68,7 @@ enum evdev_device_udev_tags { EVDEV_UDEV_TAG_TABLET_PAD = (1 << 8), EVDEV_UDEV_TAG_POINTINGSTICK = (1 << 9), EVDEV_UDEV_TAG_TRACKBALL = (1 << 10), + EVDEV_UDEV_TAG_SWITCH = (1 << 11), }; struct evdev_udev_tag_match { @@ -88,6 +89,7 @@ static const struct evdev_udev_tag_match evdev_udev_tag_matches[] = { {"ID_INPUT_ACCELEROMETER", EVDEV_UDEV_TAG_ACCELEROMETER}, {"ID_INPUT_POINTINGSTICK", EVDEV_UDEV_TAG_POINTINGSTICK}, {"ID_INPUT_TRACKBALL", EVDEV_UDEV_TAG_TRACKBALL}, + {"ID_INPUT_SWITCH", EVDEV_UDEV_TAG_SWITCH}, /* sentinel value */ { 0 }, @@ -1083,12 +1085,19 @@ evdev_tag_keyboard(struct evdev_device *device, } static void +evdev_tag_lid_switch(struct evdev_device *device, + struct udev_device *udev_device) +{ + device->tags |= EVDEV_TAG_LID_SWITCH; +} + +static void fallback_process(struct evdev_dispatch *evdev_dispatch, struct evdev_device *device, struct input_event *event, uint64_t time) { - struct fallback_dispatch *dispatch = (struct fallback_dispatch*)evdev_dispatch; + struct fallback_dispatch *dispatch = fallback_dispatch(evdev_dispatch); enum evdev_event_type sent; if (dispatch->ignore_events) @@ -1217,7 +1226,7 @@ static void fallback_suspend(struct evdev_dispatch *evdev_dispatch, struct evdev_device *device) { - struct fallback_dispatch *dispatch = (struct fallback_dispatch*)evdev_dispatch; + struct fallback_dispatch *dispatch = fallback_dispatch(evdev_dispatch); fallback_return_to_neutral_state(dispatch, device); } @@ -1227,7 +1236,7 @@ fallback_toggle_touch(struct evdev_dispatch *evdev_dispatch, struct evdev_device *device, bool enable) { - struct fallback_dispatch *dispatch = (struct fallback_dispatch*)evdev_dispatch; + struct fallback_dispatch *dispatch = fallback_dispatch(evdev_dispatch); bool ignore_events = !enable; if (ignore_events == dispatch->ignore_events) @@ -1242,7 +1251,7 @@ fallback_toggle_touch(struct evdev_dispatch *evdev_dispatch, static void fallback_destroy(struct evdev_dispatch *evdev_dispatch) { - struct fallback_dispatch *dispatch = (struct fallback_dispatch*)evdev_dispatch; + struct fallback_dispatch *dispatch = fallback_dispatch(evdev_dispatch); free(dispatch->mt.slots); free(dispatch); @@ -1251,7 +1260,7 @@ fallback_destroy(struct evdev_dispatch *evdev_dispatch) static int evdev_calibration_has_matrix(struct libinput_device *libinput_device) { - struct evdev_device *device = (struct evdev_device*)libinput_device; + struct evdev_device *device = evdev_device(libinput_device); return device->abs.absinfo_x && device->abs.absinfo_y; } @@ -1260,7 +1269,7 @@ static enum libinput_config_status evdev_calibration_set_matrix(struct libinput_device *libinput_device, const float matrix[6]) { - struct evdev_device *device = (struct evdev_device*)libinput_device; + struct evdev_device *device = evdev_device(libinput_device); evdev_device_calibrate(device, matrix); @@ -1271,7 +1280,7 @@ static int evdev_calibration_get_matrix(struct libinput_device *libinput_device, float matrix[6]) { - struct evdev_device *device = (struct evdev_device*)libinput_device; + struct evdev_device *device = evdev_device(libinput_device); matrix_to_farray6(&device->abs.usermatrix, matrix); @@ -1282,7 +1291,7 @@ static int evdev_calibration_get_default_matrix(struct libinput_device *libinput_device, float matrix[6]) { - struct evdev_device *device = (struct evdev_device*)libinput_device; + struct evdev_device *device = evdev_device(libinput_device); matrix_to_farray6(&device->abs.default_calibration, matrix); @@ -1312,7 +1321,7 @@ static enum libinput_config_status evdev_sendevents_set_mode(struct libinput_device *device, enum libinput_config_send_events_mode mode) { - struct evdev_device *evdev = (struct evdev_device*)device; + struct evdev_device *evdev = evdev_device(device); struct evdev_dispatch *dispatch = evdev->dispatch; if (mode == dispatch->sendevents.current_mode) @@ -1337,7 +1346,7 @@ evdev_sendevents_set_mode(struct libinput_device *device, static enum libinput_config_send_events_mode evdev_sendevents_get_mode(struct libinput_device *device) { - struct evdev_device *evdev = (struct evdev_device*)device; + struct evdev_device *evdev = evdev_device(device); struct evdev_dispatch *dispatch = evdev->dispatch; return dispatch->sendevents.current_mode; @@ -1360,7 +1369,7 @@ evdev_left_handed_has(struct libinput_device *device) static void evdev_change_to_left_handed(struct evdev_device *device) { - struct fallback_dispatch *dispatch = (struct fallback_dispatch*)device->dispatch; + struct fallback_dispatch *dispatch = fallback_dispatch(device->dispatch); if (device->left_handed.want_enabled == device->left_handed.enabled) return; @@ -1374,11 +1383,11 @@ evdev_change_to_left_handed(struct evdev_device *device) static enum libinput_config_status evdev_left_handed_set(struct libinput_device *device, int left_handed) { - struct evdev_device *evdev_device = (struct evdev_device *)device; + struct evdev_device *evdev = evdev_device(device); - evdev_device->left_handed.want_enabled = left_handed ? true : false; + evdev->left_handed.want_enabled = left_handed ? true : false; - evdev_device->left_handed.change_to_enabled(evdev_device); + evdev->left_handed.change_to_enabled(evdev); return LIBINPUT_CONFIG_STATUS_SUCCESS; } @@ -1386,11 +1395,11 @@ evdev_left_handed_set(struct libinput_device *device, int left_handed) static int evdev_left_handed_get(struct libinput_device *device) { - struct evdev_device *evdev_device = (struct evdev_device *)device; + struct evdev_device *evdev = evdev_device(device); /* return the wanted configuration, even if it hasn't taken * effect yet! */ - return evdev_device->left_handed.want_enabled; + return evdev->left_handed.want_enabled; } static int @@ -1422,7 +1431,7 @@ evdev_scroll_get_methods(struct libinput_device *device) static void evdev_change_scroll_method(struct evdev_device *device) { - struct fallback_dispatch *dispatch = (struct fallback_dispatch*)device->dispatch; + struct fallback_dispatch *dispatch = fallback_dispatch(device->dispatch); if (device->scroll.want_method == device->scroll.method && device->scroll.want_button == device->scroll.button) @@ -1439,7 +1448,7 @@ static enum libinput_config_status evdev_scroll_set_method(struct libinput_device *device, enum libinput_config_scroll_method method) { - struct evdev_device *evdev = (struct evdev_device*)device; + struct evdev_device *evdev = evdev_device(device); evdev->scroll.want_method = method; evdev->scroll.change_scroll_method(evdev); @@ -1450,7 +1459,7 @@ evdev_scroll_set_method(struct libinput_device *device, static enum libinput_config_scroll_method evdev_scroll_get_method(struct libinput_device *device) { - struct evdev_device *evdev = (struct evdev_device *)device; + struct evdev_device *evdev = evdev_device(device); /* return the wanted configuration, even if it hasn't taken * effect yet! */ @@ -1460,7 +1469,7 @@ evdev_scroll_get_method(struct libinput_device *device) static enum libinput_config_scroll_method evdev_scroll_get_default_method(struct libinput_device *device) { - struct evdev_device *evdev = (struct evdev_device *)device; + struct evdev_device *evdev = evdev_device(device); if (evdev->tags & EVDEV_TAG_TRACKPOINT) return LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN; @@ -1479,7 +1488,7 @@ static enum libinput_config_status evdev_scroll_set_button(struct libinput_device *device, uint32_t button) { - struct evdev_device *evdev = (struct evdev_device*)device; + struct evdev_device *evdev = evdev_device(device); evdev->scroll.want_button = button; evdev->scroll.change_scroll_method(evdev); @@ -1490,7 +1499,7 @@ evdev_scroll_set_button(struct libinput_device *device, static uint32_t evdev_scroll_get_button(struct libinput_device *device) { - struct evdev_device *evdev = (struct evdev_device *)device; + struct evdev_device *evdev = evdev_device(device); /* return the wanted configuration, even if it hasn't taken * effect yet! */ @@ -1500,7 +1509,7 @@ evdev_scroll_get_button(struct libinput_device *device) static uint32_t evdev_scroll_get_default_button(struct libinput_device *device) { - struct evdev_device *evdev = (struct evdev_device *)device; + struct evdev_device *evdev = evdev_device(device); if (libevdev_has_event_code(evdev->evdev, EV_KEY, BTN_MIDDLE)) return BTN_MIDDLE; @@ -1542,7 +1551,7 @@ evdev_init_calibration(struct evdev_device *device, calibration->get_default_matrix = evdev_calibration_get_default_matrix; } -static void +void evdev_init_sendevents(struct evdev_device *device, struct evdev_dispatch *dispatch) { @@ -1565,7 +1574,7 @@ static enum libinput_config_status evdev_scroll_config_natural_set(struct libinput_device *device, int enabled) { - struct evdev_device *dev = (struct evdev_device *)device; + struct evdev_device *dev = evdev_device(device); dev->scroll.natural_scrolling_enabled = enabled ? true : false; @@ -1575,7 +1584,7 @@ evdev_scroll_config_natural_set(struct libinput_device *device, static int evdev_scroll_config_natural_get(struct libinput_device *device) { - struct evdev_device *dev = (struct evdev_device *)device; + struct evdev_device *dev = evdev_device(device); return dev->scroll.natural_scrolling_enabled ? 1 : 0; } @@ -1610,8 +1619,8 @@ static enum libinput_config_status evdev_rotation_config_set_angle(struct libinput_device *libinput_device, unsigned int degrees_cw) { - struct evdev_device *device = (struct evdev_device*)libinput_device; - struct fallback_dispatch *dispatch = (struct fallback_dispatch*)device->dispatch; + struct evdev_device *device = evdev_device(libinput_device); + struct fallback_dispatch *dispatch = fallback_dispatch(device->dispatch); dispatch->rotation.angle = degrees_cw; matrix_init_rotate(&dispatch->rotation.matrix, degrees_cw); @@ -1622,8 +1631,8 @@ evdev_rotation_config_set_angle(struct libinput_device *libinput_device, static unsigned int evdev_rotation_config_get_angle(struct libinput_device *libinput_device) { - struct evdev_device *device = (struct evdev_device*)libinput_device; - struct fallback_dispatch *dispatch = (struct fallback_dispatch*)device->dispatch; + struct evdev_device *device = evdev_device(libinput_device); + struct fallback_dispatch *dispatch = fallback_dispatch(device->dispatch); return dispatch->rotation.angle; } @@ -1757,53 +1766,54 @@ fallback_dispatch_init_abs(struct fallback_dispatch *dispatch, } static struct evdev_dispatch * -fallback_dispatch_create(struct libinput_device *device) +fallback_dispatch_create(struct libinput_device *libinput_device) { struct fallback_dispatch *dispatch = zalloc(sizeof *dispatch); - struct evdev_device *evdev_device = (struct evdev_device *)device; + struct evdev_device *device = evdev_device(libinput_device); if (dispatch == NULL) return NULL; + dispatch->base.dispatch_type = DISPATCH_FALLBACK; dispatch->base.interface = &fallback_interface; dispatch->pending_event = EVDEV_NONE; - fallback_dispatch_init_rel(dispatch, evdev_device); - fallback_dispatch_init_abs(dispatch, evdev_device); - if (fallback_dispatch_init_slots(dispatch, evdev_device) == -1) { + fallback_dispatch_init_rel(dispatch, device); + fallback_dispatch_init_abs(dispatch, device); + if (fallback_dispatch_init_slots(dispatch, device) == -1) { free(dispatch); return NULL; } - if (evdev_device->left_handed.want_enabled) - evdev_init_left_handed(evdev_device, + if (device->left_handed.want_enabled) + evdev_init_left_handed(device, evdev_change_to_left_handed); - if (evdev_device->scroll.want_button) - evdev_init_button_scroll(evdev_device, + if (device->scroll.want_button) + evdev_init_button_scroll(device, evdev_change_scroll_method); - if (evdev_device->scroll.natural_scrolling_enabled) - evdev_init_natural_scroll(evdev_device); + if (device->scroll.natural_scrolling_enabled) + evdev_init_natural_scroll(device); - evdev_init_calibration(evdev_device, &dispatch->calibration); - evdev_init_sendevents(evdev_device, &dispatch->base); - evdev_init_rotation(evdev_device, dispatch); + evdev_init_calibration(device, &dispatch->calibration); + evdev_init_sendevents(device, &dispatch->base); + evdev_init_rotation(device, dispatch); /* BTN_MIDDLE is set on mice even when it's not present. So * we can only use the absence of BTN_MIDDLE to mean something, i.e. * we enable it by default on anything that only has L&R. * If we have L&R and no middle, we don't expose it as config * option */ - if (libevdev_has_event_code(evdev_device->evdev, EV_KEY, BTN_LEFT) && - libevdev_has_event_code(evdev_device->evdev, EV_KEY, BTN_RIGHT)) { - bool has_middle = libevdev_has_event_code(evdev_device->evdev, + if (libevdev_has_event_code(device->evdev, EV_KEY, BTN_LEFT) && + libevdev_has_event_code(device->evdev, EV_KEY, BTN_RIGHT)) { + bool has_middle = libevdev_has_event_code(device->evdev, EV_KEY, BTN_MIDDLE); bool want_config = has_middle; bool enable_by_default = !has_middle; - evdev_init_middlebutton(evdev_device, + evdev_init_middlebutton(device, enable_by_default, want_config); } @@ -1942,7 +1952,7 @@ evdev_accel_config_available(struct libinput_device *device) static enum libinput_config_status evdev_accel_config_set_speed(struct libinput_device *device, double speed) { - struct evdev_device *dev = (struct evdev_device *)device; + struct evdev_device *dev = evdev_device(device); if (!filter_set_speed(dev->pointer.filter, speed)) return LIBINPUT_CONFIG_STATUS_INVALID; @@ -1953,7 +1963,7 @@ evdev_accel_config_set_speed(struct libinput_device *device, double speed) static double evdev_accel_config_get_speed(struct libinput_device *device) { - struct evdev_device *dev = (struct evdev_device *)device; + struct evdev_device *dev = evdev_device(device); return filter_get_speed(dev->pointer.filter); } @@ -1967,7 +1977,7 @@ evdev_accel_config_get_default_speed(struct libinput_device *device) static uint32_t evdev_accel_config_get_profiles(struct libinput_device *libinput_device) { - struct evdev_device *device = (struct evdev_device*)libinput_device; + struct evdev_device *device = evdev_device(libinput_device); if (!device->pointer.filter) return LIBINPUT_CONFIG_ACCEL_PROFILE_NONE; @@ -1980,7 +1990,7 @@ static enum libinput_config_status evdev_accel_config_set_profile(struct libinput_device *libinput_device, enum libinput_config_accel_profile profile) { - struct evdev_device *device = (struct evdev_device*)libinput_device; + struct evdev_device *device = evdev_device(libinput_device); struct motion_filter *filter; double speed; @@ -2004,7 +2014,7 @@ evdev_accel_config_set_profile(struct libinput_device *libinput_device, static enum libinput_config_accel_profile evdev_accel_config_get_profile(struct libinput_device *libinput_device) { - struct evdev_device *device = (struct evdev_device*)libinput_device; + struct evdev_device *device = evdev_device(libinput_device); return filter_get_type(device->pointer.filter); } @@ -2012,7 +2022,7 @@ evdev_accel_config_get_profile(struct libinput_device *libinput_device) static enum libinput_config_accel_profile evdev_accel_config_get_default_profile(struct libinput_device *libinput_device) { - struct evdev_device *device = (struct evdev_device*)libinput_device; + struct evdev_device *device = evdev_device(libinput_device); if (!device->pointer.filter) return LIBINPUT_CONFIG_ACCEL_PROFILE_NONE; @@ -2534,7 +2544,7 @@ evdev_configure_device(struct evdev_device *device) } log_info(libinput, - "input device '%s', %s is tagged by udev as:%s%s%s%s%s%s%s%s%s%s\n", + "input device '%s', %s is tagged by udev as:%s%s%s%s%s%s%s%s%s%s%s\n", device->devname, devnode, udev_tags & EVDEV_UDEV_TAG_KEYBOARD ? " Keyboard" : "", udev_tags & EVDEV_UDEV_TAG_MOUSE ? " Mouse" : "", @@ -2545,7 +2555,8 @@ evdev_configure_device(struct evdev_device *device) udev_tags & EVDEV_UDEV_TAG_JOYSTICK ? " Joystick" : "", udev_tags & EVDEV_UDEV_TAG_ACCELEROMETER ? " Accelerometer" : "", udev_tags & EVDEV_UDEV_TAG_TABLET_PAD ? " TabletPad" : "", - udev_tags & EVDEV_UDEV_TAG_TRACKBALL ? " Trackball" : ""); + udev_tags & EVDEV_UDEV_TAG_TRACKBALL ? " Trackball" : "", + udev_tags & EVDEV_UDEV_TAG_SWITCH ? " Switch" : ""); if (udev_tags & EVDEV_UDEV_TAG_ACCELEROMETER) { log_info(libinput, @@ -2657,6 +2668,17 @@ evdev_configure_device(struct evdev_device *device) device->devname, devnode); } + if (udev_tags & EVDEV_UDEV_TAG_SWITCH && + libevdev_has_event_code(evdev, EV_SW, SW_LID)) { + dispatch = evdev_lid_switch_dispatch_create(device); + device->seat_caps |= EVDEV_DEVICE_SWITCH; + evdev_tag_lid_switch(device, device->udev_device); + log_info(libinput, + "input device '%s', %s is a switch device\n", + device->devname, devnode); + return dispatch; + } + if (device->seat_caps & EVDEV_DEVICE_POINTER && libevdev_has_event_code(evdev, EV_REL, REL_X) && libevdev_has_event_code(evdev, EV_REL, REL_Y) && @@ -2676,7 +2698,7 @@ evdev_notify_added_device(struct evdev_device *device) struct libinput_device *dev; list_for_each(dev, &device->base.seat->devices_list, link) { - struct evdev_device *d = (struct evdev_device*)dev; + struct evdev_device *d = evdev_device(dev); if (dev == &device->base) continue; @@ -3089,6 +3111,8 @@ evdev_device_has_capability(struct evdev_device *device, return !!(device->seat_caps & EVDEV_DEVICE_TABLET); case LIBINPUT_DEVICE_CAP_TABLET_PAD: return !!(device->seat_caps & EVDEV_DEVICE_TABLET_PAD); + case LIBINPUT_DEVICE_CAP_SWITCH: + return !!(device->seat_caps & EVDEV_DEVICE_SWITCH); default: return false; } @@ -3258,7 +3282,7 @@ evdev_notify_suspended_device(struct evdev_device *device) return; list_for_each(it, &device->base.seat->devices_list, link) { - struct evdev_device *d = (struct evdev_device*)it; + struct evdev_device *d = evdev_device(it); if (it == &device->base) continue; @@ -3278,7 +3302,7 @@ evdev_notify_resumed_device(struct evdev_device *device) return; list_for_each(it, &device->base.seat->devices_list, link) { - struct evdev_device *d = (struct evdev_device*)it; + struct evdev_device *d = evdev_device(it); if (it == &device->base) continue; @@ -3385,7 +3409,7 @@ evdev_device_remove(struct evdev_device *device) struct libinput_device *dev; list_for_each(dev, &device->base.seat->devices_list, link) { - struct evdev_device *d = (struct evdev_device*)dev; + struct evdev_device *d = evdev_device(dev); if (dev == &device->base) continue; diff --git a/src/evdev.h b/src/evdev.h index 3fe5d3a..1a24d4b 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -63,6 +63,7 @@ enum evdev_device_seat_capability { EVDEV_DEVICE_TABLET = (1 << 3), EVDEV_DEVICE_TABLET_PAD = (1 << 4), EVDEV_DEVICE_GESTURE = (1 << 5), + EVDEV_DEVICE_SWITCH = (1 << 6), }; enum evdev_device_tags { @@ -71,6 +72,7 @@ enum evdev_device_tags { EVDEV_TAG_EXTERNAL_TOUCHPAD = (1 << 2), EVDEV_TAG_TRACKPOINT = (1 << 3), EVDEV_TAG_KEYBOARD = (1 << 4), + EVDEV_TAG_LID_SWITCH = (1 << 5), }; enum evdev_middlebutton_state { @@ -235,6 +237,14 @@ struct evdev_device { } middlebutton; }; +static inline struct evdev_device * +evdev_device(struct libinput_device *device) +{ + struct evdev_device *d; + + return container_of(device, d, base); +} + #define EVDEV_UNHANDLED_DEVICE ((struct evdev_device *) 1) struct evdev_dispatch; @@ -282,7 +292,16 @@ struct evdev_dispatch_interface { bool enable); }; +enum evdev_dispatch_type { + DISPATCH_FALLBACK, + DISPATCH_TOUCHPAD, + DISPATCH_TABLET, + DISPATCH_TABLET_PAD, + DISPATCH_LID_SWITCH, +}; + struct evdev_dispatch { + enum evdev_dispatch_type dispatch_type; struct evdev_dispatch_interface *interface; struct { @@ -291,6 +310,14 @@ struct evdev_dispatch { } sendevents; }; +static inline void +evdev_verify_dispatch_type(struct evdev_dispatch *dispatch, + enum evdev_dispatch_type type) +{ + if (dispatch->dispatch_type != type) + abort(); +} + struct fallback_dispatch { struct evdev_dispatch base; @@ -334,6 +361,16 @@ struct fallback_dispatch { bool ignore_events; }; +static inline struct fallback_dispatch* +fallback_dispatch(struct evdev_dispatch *dispatch) +{ + struct fallback_dispatch *f; + + evdev_verify_dispatch_type(dispatch, DISPATCH_FALLBACK); + + return container_of(dispatch, f, base); +} + struct evdev_device * evdev_device_create(struct libinput_seat *seat, struct udev_device *device); @@ -354,6 +391,10 @@ void evdev_read_calibration_prop(struct evdev_device *device); void +evdev_init_sendevents(struct evdev_device *device, + struct evdev_dispatch *dispatch); + +void evdev_device_init_pointer_acceleration(struct evdev_device *device, struct motion_filter *filter); @@ -369,6 +410,9 @@ evdev_tablet_create(struct evdev_device *device); struct evdev_dispatch * evdev_tablet_pad_create(struct evdev_device *device); +struct evdev_dispatch * +evdev_lid_switch_dispatch_create(struct evdev_device *device); + void evdev_device_led_update(struct evdev_device *device, enum libinput_led leds); diff --git a/src/libinput-private.h b/src/libinput-private.h index 39c169c..205dbf8 100644 --- a/src/libinput-private.h +++ b/src/libinput-private.h @@ -467,6 +467,9 @@ libinput_device_set_device_group(struct libinput_device *device, struct libinput_device_group *group); void +libinput_device_init_event_listener(struct libinput_event_listener *listener); + +void libinput_device_add_event_listener(struct libinput_device *device, struct libinput_event_listener *listener, void (*notify_func)( @@ -623,6 +626,11 @@ tablet_pad_notify_strip(struct libinput_device *device, double value, enum libinput_tablet_pad_strip_axis_source source, struct libinput_tablet_pad_mode_group *group); +void +switch_notify_toggle(struct libinput_device *device, + uint64_t time, + enum libinput_switch sw, + enum libinput_switch_state state); static inline uint64_t libinput_now(struct libinput *libinput) diff --git a/src/libinput-util.c b/src/libinput-util.c index 40e1e6e..aa69a37 100644 --- a/src/libinput-util.c +++ b/src/libinput-util.c @@ -278,6 +278,25 @@ parse_dimension_property(const char *prop, size_t *w, size_t *h) return true; } +bool +parse_switch_reliability_property(const char *prop, + enum switch_reliability *reliability) +{ + if (!prop) { + *reliability = RELIABILITY_UNKNOWN; + return true; + } + + if (streq(prop, "reliable")) + *reliability = RELIABILITY_RELIABLE; + else if (streq(prop, "write_open")) + *reliability = RELIABILITY_WRITE_OPEN; + else + return false; + + return true; +} + /** * Return the next word in a string pointed to by state before the first * separator character. Call repeatedly to tokenize a whole string. diff --git a/src/libinput-util.h b/src/libinput-util.h index ba09ab6..1d03ce1 100644 --- a/src/libinput-util.h +++ b/src/libinput-util.h @@ -377,6 +377,16 @@ int parse_mouse_wheel_click_count_property(const char *prop); double parse_trackpoint_accel_property(const char *prop); bool parse_dimension_property(const char *prop, size_t *width, size_t *height); +enum switch_reliability { + RELIABILITY_UNKNOWN, + RELIABILITY_RELIABLE, + RELIABILITY_WRITE_OPEN, +}; + +bool +parse_switch_reliability_property(const char *prop, + enum switch_reliability *reliability); + static inline uint64_t us(uint64_t us) { diff --git a/src/libinput.c b/src/libinput.c index 4daa11d..84e329d 100644 --- a/src/libinput.c +++ b/src/libinput.c @@ -159,6 +159,13 @@ struct libinput_event_tablet_pad { } strip; }; +struct libinput_event_switch { + struct libinput_event base; + uint64_t time; + enum libinput_switch sw; + enum libinput_switch_state state; +}; + LIBINPUT_ATTRIBUTE_PRINTF(3, 0) static void libinput_default_log_func(struct libinput *libinput, @@ -365,6 +372,17 @@ libinput_event_get_device_notify_event(struct libinput_event *event) return (struct libinput_event_device_notify *) event; } +LIBINPUT_EXPORT struct libinput_event_switch * +libinput_event_get_switch_event(struct libinput_event *event) +{ + require_event_type(libinput_event_get_context(event), + event->type, + NULL, + LIBINPUT_EVENT_SWITCH_TOGGLE); + + return (struct libinput_event_switch *) event; +} + LIBINPUT_EXPORT uint32_t libinput_event_keyboard_get_time(struct libinput_event_keyboard *event) { @@ -498,8 +516,7 @@ libinput_event_pointer_get_dy_unaccelerated( LIBINPUT_EXPORT double libinput_event_pointer_get_absolute_x(struct libinput_event_pointer *event) { - struct evdev_device *device = - (struct evdev_device *) event->base.device; + struct evdev_device *device = evdev_device(event->base.device); require_event_type(libinput_event_get_context(&event->base), event->base.type, @@ -512,8 +529,7 @@ libinput_event_pointer_get_absolute_x(struct libinput_event_pointer *event) LIBINPUT_EXPORT double libinput_event_pointer_get_absolute_y(struct libinput_event_pointer *event) { - struct evdev_device *device = - (struct evdev_device *) event->base.device; + struct evdev_device *device = evdev_device(event->base.device); require_event_type(libinput_event_get_context(&event->base), event->base.type, @@ -528,8 +544,7 @@ libinput_event_pointer_get_absolute_x_transformed( struct libinput_event_pointer *event, uint32_t width) { - struct evdev_device *device = - (struct evdev_device *) event->base.device; + struct evdev_device *device = evdev_device(event->base.device); require_event_type(libinput_event_get_context(&event->base), event->base.type, @@ -544,8 +559,7 @@ libinput_event_pointer_get_absolute_y_transformed( struct libinput_event_pointer *event, uint32_t height) { - struct evdev_device *device = - (struct evdev_device *) event->base.device; + struct evdev_device *device = evdev_device(event->base.device); require_event_type(libinput_event_get_context(&event->base), event->base.type, @@ -734,8 +748,7 @@ libinput_event_touch_get_seat_slot(struct libinput_event_touch *event) LIBINPUT_EXPORT double libinput_event_touch_get_x(struct libinput_event_touch *event) { - struct evdev_device *device = - (struct evdev_device *) event->base.device; + struct evdev_device *device = evdev_device(event->base.device); require_event_type(libinput_event_get_context(&event->base), event->base.type, @@ -750,8 +763,7 @@ LIBINPUT_EXPORT double libinput_event_touch_get_x_transformed(struct libinput_event_touch *event, uint32_t width) { - struct evdev_device *device = - (struct evdev_device *) event->base.device; + struct evdev_device *device = evdev_device(event->base.device); require_event_type(libinput_event_get_context(&event->base), event->base.type, @@ -766,8 +778,7 @@ LIBINPUT_EXPORT double libinput_event_touch_get_y_transformed(struct libinput_event_touch *event, uint32_t height) { - struct evdev_device *device = - (struct evdev_device *) event->base.device; + struct evdev_device *device = evdev_device(event->base.device); require_event_type(libinput_event_get_context(&event->base), event->base.type, @@ -781,8 +792,7 @@ libinput_event_touch_get_y_transformed(struct libinput_event_touch *event, LIBINPUT_EXPORT double libinput_event_touch_get_y(struct libinput_event_touch *event) { - struct evdev_device *device = - (struct evdev_device *) event->base.device; + struct evdev_device *device = evdev_device(event->base.device); require_event_type(libinput_event_get_context(&event->base), event->base.type, @@ -1092,8 +1102,7 @@ libinput_event_tablet_tool_wheel_has_changed( LIBINPUT_EXPORT double libinput_event_tablet_tool_get_x(struct libinput_event_tablet_tool *event) { - struct evdev_device *device = - (struct evdev_device *) event->base.device; + struct evdev_device *device = evdev_device(event->base.device); require_event_type(libinput_event_get_context(&event->base), event->base.type, @@ -1110,8 +1119,7 @@ libinput_event_tablet_tool_get_x(struct libinput_event_tablet_tool *event) LIBINPUT_EXPORT double libinput_event_tablet_tool_get_y(struct libinput_event_tablet_tool *event) { - struct evdev_device *device = - (struct evdev_device *) event->base.device; + struct evdev_device *device = evdev_device(event->base.device); require_event_type(libinput_event_get_context(&event->base), event->base.type, @@ -1259,8 +1267,7 @@ LIBINPUT_EXPORT double libinput_event_tablet_tool_get_x_transformed(struct libinput_event_tablet_tool *event, uint32_t width) { - struct evdev_device *device = - (struct evdev_device *) event->base.device; + struct evdev_device *device = evdev_device(event->base.device); require_event_type(libinput_event_get_context(&event->base), event->base.type, @@ -1279,8 +1286,7 @@ LIBINPUT_EXPORT double libinput_event_tablet_tool_get_y_transformed(struct libinput_event_tablet_tool *event, uint32_t height) { - struct evdev_device *device = - (struct evdev_device *) event->base.device; + struct evdev_device *device = evdev_device(event->base.device); require_event_type(libinput_event_get_context(&event->base), event->base.type, @@ -1508,6 +1514,61 @@ libinput_tablet_tool_unref(struct libinput_tablet_tool *tool) return NULL; } +LIBINPUT_EXPORT struct libinput_event * +libinput_event_switch_get_base_event(struct libinput_event_switch *event) +{ + require_event_type(libinput_event_get_context(&event->base), + event->base.type, + NULL, + LIBINPUT_EVENT_SWITCH_TOGGLE); + + return &event->base; +} + +LIBINPUT_EXPORT enum libinput_switch +libinput_event_switch_get_switch(struct libinput_event_switch *event) +{ + require_event_type(libinput_event_get_context(&event->base), + event->base.type, + 0, + LIBINPUT_EVENT_SWITCH_TOGGLE); + + return event->sw; +} + +LIBINPUT_EXPORT enum libinput_switch_state +libinput_event_switch_get_switch_state(struct libinput_event_switch *event) +{ + require_event_type(libinput_event_get_context(&event->base), + event->base.type, + 0, + LIBINPUT_EVENT_SWITCH_TOGGLE); + + return event->state; +} + +LIBINPUT_EXPORT uint32_t +libinput_event_switch_get_time(struct libinput_event_switch *event) +{ + require_event_type(libinput_event_get_context(&event->base), + event->base.type, + 0, + LIBINPUT_EVENT_SWITCH_TOGGLE); + + return us2ms(event->time); +} + +LIBINPUT_EXPORT uint64_t +libinput_event_switch_get_time_usec(struct libinput_event_switch *event) +{ + require_event_type(libinput_event_get_context(&event->base), + event->base.type, + 0, + LIBINPUT_EVENT_SWITCH_TOGGLE); + + return event->time; +} + struct libinput_source * libinput_add_fd(struct libinput *libinput, int fd, @@ -1825,7 +1886,7 @@ static void libinput_device_destroy(struct libinput_device *device) { assert(list_empty(&device->event_listeners)); - evdev_device_destroy((struct evdev_device *) device); + evdev_device_destroy(evdev_device(device)); } LIBINPUT_EXPORT struct libinput_device * @@ -1872,6 +1933,12 @@ libinput_dispatch(struct libinput *libinput) } void +libinput_device_init_event_listener(struct libinput_event_listener *listener) +{ + list_init(&listener->link); +} + +void libinput_device_add_event_listener(struct libinput_device *device, struct libinput_event_listener *listener, void (*notify_func)( @@ -2024,6 +2091,9 @@ device_has_cap(struct libinput_device *device, case LIBINPUT_DEVICE_CAP_TABLET_PAD: capability = "CAP_TABLET_PAD"; break; + case LIBINPUT_DEVICE_CAP_SWITCH: + capability = "CAP_SWITCH"; + break; } log_bug_libinput(device->seat->libinput, @@ -2619,6 +2689,7 @@ event_type_to_str(enum libinput_event_type type) CASE_RETURN_STRING(LIBINPUT_EVENT_GESTURE_PINCH_BEGIN); CASE_RETURN_STRING(LIBINPUT_EVENT_GESTURE_PINCH_UPDATE); CASE_RETURN_STRING(LIBINPUT_EVENT_GESTURE_PINCH_END); + CASE_RETURN_STRING(LIBINPUT_EVENT_SWITCH_TOGGLE); case LIBINPUT_EVENT_NONE: abort(); } @@ -2626,6 +2697,32 @@ event_type_to_str(enum libinput_event_type type) return NULL; } +void +switch_notify_toggle(struct libinput_device *device, + uint64_t time, + enum libinput_switch sw, + enum libinput_switch_state state) +{ + struct libinput_event_switch *switch_event; + + if (!device_has_cap(device, LIBINPUT_DEVICE_CAP_SWITCH)) + return; + + switch_event = zalloc(sizeof *switch_event); + if (!switch_event) + return; + + *switch_event = (struct libinput_event_switch) { + .time = time, + .sw = sw, + .state = state, + }; + + post_device_event(device, time, + LIBINPUT_EVENT_SWITCH_TOGGLE, + &switch_event->base); +} + static void libinput_post_event(struct libinput *libinput, struct libinput_event *event) diff --git a/src/libinput.h b/src/libinput.h index fdbba3c..436923d 100644 --- a/src/libinput.h +++ b/src/libinput.h @@ -187,6 +187,7 @@ enum libinput_device_capability { LIBINPUT_DEVICE_CAP_TABLET_TOOL = 3, LIBINPUT_DEVICE_CAP_TABLET_PAD = 4, LIBINPUT_DEVICE_CAP_GESTURE = 5, + LIBINPUT_DEVICE_CAP_SWITCH = 6, }; /** @@ -598,6 +599,42 @@ libinput_tablet_pad_mode_group_get_user_data( struct libinput_tablet_pad_mode_group *group); /** + * @ingroup device + * + * The state of a switch. The default state of a switch is @ref + * LIBINPUT_SWITCH_STATE_OFF and no event is sent to confirm a switch in the + * off position. If a switch is logically on during initialization, libinput + * sends an event of type @ref LIBINPUT_EVENT_SWITCH_TOGGLE with a state + * @ref LIBINPUT_SWITCH_STATE_ON. + */ +enum libinput_switch_state { + LIBINPUT_SWITCH_STATE_OFF = 0, + LIBINPUT_SWITCH_STATE_ON = 1, +}; + +/** + * @ingroup device + * + * The type of a switch. + */ +enum libinput_switch { + /** + * The laptop lid was closed when the switch state is @ref + * LIBINPUT_SWITCH_STATE_ON, or was opened when it is @ref + * LIBINPUT_SWITCH_STATE_OFF. + */ + LIBINPUT_SWITCH_LID = 1, +}; + +/** + * @ingroup event_switch + * @struct libinput_event_switch + * + * A switch event representing a changed state in a switch. + */ +struct libinput_event_switch; + +/** * @ingroup base * * Event type for events returned by libinput_get_event(). @@ -752,6 +789,8 @@ enum libinput_event_type { LIBINPUT_EVENT_GESTURE_PINCH_BEGIN, LIBINPUT_EVENT_GESTURE_PINCH_UPDATE, LIBINPUT_EVENT_GESTURE_PINCH_END, + + LIBINPUT_EVENT_SWITCH_TOGGLE = 900, }; /** @@ -891,6 +930,19 @@ libinput_event_get_tablet_pad_event(struct libinput_event *event); /** * @ingroup event * + * Return the switch event that is this input event. If the event type does + * not match the switch event types, this function returns NULL. + * + * The inverse of this function is libinput_event_switch_get_base_event(). + * + * @return A switch event, or NULL for other events + */ +struct libinput_event_switch * +libinput_event_get_switch_event(struct libinput_event *event); + +/** + * @ingroup event + * * Return the device event that is this input event. If the event type does * not match the device event types, this function returns NULL. * @@ -2699,6 +2751,70 @@ uint64_t libinput_event_tablet_pad_get_time_usec(struct libinput_event_tablet_pad *event); /** + * @defgroup event_switch Switch events + * + * Events that come from switch devices. + */ + +/** + * @ingroup event_switch + * + * Return the switch that triggered this event. + * For pointer events that are not of type @ref + * LIBINPUT_EVENT_SWITCH_TOGGLE, this function returns 0. + * + * @note It is an application bug to call this function for events other than + * @ref LIBINPUT_EVENT_SWITCH_TOGGLE. + * + * @param event The libinput switch event + * @return The switch triggering this event + */ +enum libinput_switch +libinput_event_switch_get_switch(struct libinput_event_switch *event); + +/** + * @ingroup event_switch + * + * Return the switch state that triggered this event. + * For switch events that are not of type @ref + * LIBINPUT_EVENT_SWITCH_TOGGLE, this function returns 0. + * + * @note It is an application bug to call this function for events other than + * @ref LIBINPUT_EVENT_SWITCH_TOGGLE. + * + * @param event The libinput switch event + * @return The switch state triggering this event + */ +enum libinput_switch_state +libinput_event_switch_get_switch_state(struct libinput_event_switch *event); + +/** + * @ingroup event_switch + * + * @return The generic libinput_event of this event + */ +struct libinput_event * +libinput_event_switch_get_base_event(struct libinput_event_switch *event); + +/** + * @ingroup event_switch + * + * @param event The libinput switch event + * @return The event time for this event + */ +uint32_t +libinput_event_switch_get_time(struct libinput_event_switch *event); + +/** + * @ingroup event_switch + * + * @param event The libinput switch event + * @return The event time for this event in microseconds + */ +uint64_t +libinput_event_switch_get_time_usec(struct libinput_event_switch *event); + +/** * @defgroup base Initialization and manipulation of libinput contexts */ diff --git a/src/libinput.sym b/src/libinput.sym index 97bb57f..f440521 100644 --- a/src/libinput.sym +++ b/src/libinput.sym @@ -280,3 +280,12 @@ LIBINPUT_1.5 { libinput_device_config_tap_get_default_button_map; libinput_device_config_tap_set_button_map; } LIBINPUT_1.4; + +LIBINPUT_SWITCH { + libinput_event_get_switch_event; + libinput_event_switch_get_base_event; + libinput_event_switch_get_switch_state; + libinput_event_switch_get_switch; + libinput_event_switch_get_time; + libinput_event_switch_get_time_usec; +} LIBINPUT_1.5; diff --git a/src/path-seat.c b/src/path-seat.c index d806bfb..e6289a2 100644 --- a/src/path-seat.c +++ b/src/path-seat.c @@ -244,11 +244,11 @@ path_device_change_seat(struct libinput_device *device, const char *seat_name) { struct libinput *libinput = device->seat->libinput; - struct evdev_device *evdev_device = (struct evdev_device *)device; + struct evdev_device *evdev = evdev_device(device); struct udev_device *udev_device = NULL; int rc = -1; - udev_device = evdev_device->udev_device; + udev_device = evdev->udev_device; udev_device_ref(udev_device); libinput_path_remove_device(device); @@ -361,7 +361,7 @@ libinput_path_remove_device(struct libinput_device *device) struct libinput *libinput = device->seat->libinput; struct path_input *input = (struct path_input*)libinput; struct libinput_seat *seat; - struct evdev_device *evdev = (struct evdev_device*)device; + struct evdev_device *evdev = evdev_device(device); struct path_device *dev; if (libinput->interface_backend != &interface_backend) { diff --git a/src/udev-seat.c b/src/udev-seat.c index d1eaed8..a19afb4 100644 --- a/src/udev-seat.c +++ b/src/udev-seat.c @@ -322,8 +322,8 @@ udev_device_change_seat(struct libinput_device *device, { struct libinput *libinput = device->seat->libinput; struct udev_input *input = (struct udev_input *)libinput; - struct evdev_device *evdev_device = (struct evdev_device *)device; - struct udev_device *udev_device = evdev_device->udev_device; + struct evdev_device *evdev = evdev_device(device); + struct udev_device *udev_device = evdev->udev_device; int rc; udev_device_ref(udev_device); diff --git a/test/Makefile.am b/test/Makefile.am index 0d54dab..2e565de 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -32,6 +32,8 @@ liblitest_la_SOURCES = \ litest-device-keyboard.c \ litest-device-keyboard-all-codes.c \ litest-device-keyboard-razer-blackwidow.c \ + litest-device-lid-switch.c \ + litest-device-lid-switch-surface3.c \ litest-device-logitech-trackball.c \ litest-device-nexus4-touch-screen.c \ litest-device-magic-trackpad.c \ @@ -117,7 +119,8 @@ libinput_test_suite_runner_SOURCES = test-udev.c \ test-misc.c \ test-keyboard.c \ test-device.c \ - test-gestures.c + test-gestures.c \ + test-lid.c libinput_test_suite_runner_CFLAGS = $(AM_CFLAGS) -DLIBINPUT_LT_VERSION="\"$(LIBINPUT_LT_VERSION)\"" libinput_test_suite_runner_LDADD = $(TEST_LIBS) diff --git a/test/litest-device-lid-switch-surface3.c b/test/litest-device-lid-switch-surface3.c new file mode 100644 index 0000000..fde15c2 --- /dev/null +++ b/test/litest-device-lid-switch-surface3.c @@ -0,0 +1,71 @@ +/* + * 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 "litest.h" +#include "litest-int.h" + +static void +litest_lid_switch_setup(void) +{ + struct litest_device *d = litest_create_device(LITEST_LID_SWITCH_SURFACE3); + litest_set_current_device(d); +} + +static struct input_id input_id = { + .bustype = 0x19, + .vendor = 0x0, + .product = 0x5, +}; + +static int events[] = { + EV_SW, SW_LID, + -1, -1, +}; + +static const char udev_rule[] = +"ACTION==\"remove\", GOTO=\"switch_end\"\n" +"KERNEL!=\"event*\", GOTO=\"switch_end\"\n" +"\n" +"ATTRS{name}==\"litest Lid Switch Surface3*\",\\\n" +" ENV{ID_INPUT_SWITCH}=\"1\",\\\n" +" ENV{LIBINPUT_ATTR_LID_SWITCH_RELIABILITY}=\"write_open\"\n" +"\n" +"LABEL=\"switch_end\""; + +struct litest_test_device litest_lid_switch_surface3_device = { + .type = LITEST_LID_SWITCH_SURFACE3, + .features = LITEST_SWITCH, + .shortname = "lid-switch-surface3", + .setup = litest_lid_switch_setup, + .interface = NULL, + + .name = "Lid Switch Surface3", + .id = &input_id, + .events = events, + .absinfo = NULL, + + .udev_rule = udev_rule, +}; diff --git a/test/litest-device-lid-switch.c b/test/litest-device-lid-switch.c new file mode 100644 index 0000000..b96592d --- /dev/null +++ b/test/litest-device-lid-switch.c @@ -0,0 +1,70 @@ +/* + * Copyright © 2017 James Ye <jye836@gmail.com> + * + * 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_lid_switch_setup(void) +{ + struct litest_device *d = litest_create_device(LITEST_LID_SWITCH); + litest_set_current_device(d); +} + +static struct input_id input_id = { + .bustype = 0x19, + .vendor = 0x0, + .product = 0x5, +}; + +static int events[] = { + EV_SW, SW_LID, + -1, -1, +}; + +static const char udev_rule[] = +"ACTION==\"remove\", GOTO=\"switch_end\"\n" +"KERNEL!=\"event*\", GOTO=\"switch_end\"\n" +"\n" +"ATTRS{name}==\"litest Lid Switch*\",\\\n" +" ENV{ID_INPUT_SWITCH}=\"1\",\\\n" +" ENV{LIBINPUT_ATTR_LID_SWITCH_RELIABILITY}=\"reliable\"\n" +"\n" +"LABEL=\"switch_end\""; + +struct litest_test_device litest_lid_switch_device = { + .type = LITEST_LID_SWITCH, + .features = LITEST_SWITCH, + .shortname = "lid switch", + .setup = litest_lid_switch_setup, + .interface = NULL, + + .name = "Lid Switch", + .id = &input_id, + .events = events, + .absinfo = NULL, + + .udev_rule = udev_rule, +}; diff --git a/test/litest.c b/test/litest.c index b3e7ba3..1756940 100644 --- a/test/litest.c +++ b/test/litest.c @@ -408,6 +408,8 @@ extern struct litest_test_device litest_acer_hawaii_keyboard_device; extern struct litest_test_device litest_acer_hawaii_touchpad_device; extern struct litest_test_device litest_synaptics_rmi4_device; extern struct litest_test_device litest_mouse_wheel_tilt_device; +extern struct litest_test_device litest_lid_switch_device; +extern struct litest_test_device litest_lid_switch_surface3_device; struct litest_test_device* devices[] = { &litest_synaptics_clickpad_device, @@ -470,6 +472,8 @@ struct litest_test_device* devices[] = { &litest_acer_hawaii_touchpad_device, &litest_synaptics_rmi4_device, &litest_mouse_wheel_tilt_device, + &litest_lid_switch_device, + &litest_lid_switch_surface3_device, NULL, }; @@ -1907,6 +1911,14 @@ litest_keyboard_key(struct litest_device *d, unsigned int key, bool is_press) litest_button_click(d, key, is_press); } +void +litest_lid_action(struct litest_device *dev, + enum libinput_switch_state state) +{ + litest_event(dev, EV_SW, SW_LID, state); + litest_event(dev, EV_SYN, SYN_REPORT, 0); +} + static int litest_scale_axis(const struct litest_device *d, unsigned int axis, @@ -2203,6 +2215,9 @@ litest_event_type_str(enum libinput_event_type type) case LIBINPUT_EVENT_TABLET_PAD_STRIP: str = "TABLET PAD STRIP"; break; + case LIBINPUT_EVENT_SWITCH_TOGGLE: + str = "SWITCH TOGGLE"; + break; } return str; } @@ -2816,6 +2831,25 @@ litest_is_pad_strip_event(struct libinput_event *event, return p; } +struct libinput_event_switch * +litest_is_switch_event(struct libinput_event *event, + enum libinput_switch sw, + enum libinput_switch_state state) +{ + struct libinput_event_switch *swev; + enum libinput_event_type type = LIBINPUT_EVENT_SWITCH_TOGGLE; + + litest_assert_notnull(event); + litest_assert_event_type(event, type); + swev = libinput_event_get_switch_event(event); + + litest_assert_int_eq(libinput_event_switch_get_switch(swev), sw); + litest_assert_int_eq(libinput_event_switch_get_switch_state(swev), + state); + + return swev; +} + void litest_assert_pad_button_event(struct libinput *li, unsigned int button, @@ -3320,6 +3354,7 @@ main(int argc, char **argv) litest_setup_tests_keyboard(); litest_setup_tests_device(); litest_setup_tests_gestures(); + litest_setup_tests_lid(); if (mode == LITEST_MODE_LIST) { litest_list_tests(&all_tests); diff --git a/test/litest.h b/test/litest.h index e6d35a9..d779f0f 100644 --- a/test/litest.h +++ b/test/litest.h @@ -49,6 +49,7 @@ extern void litest_setup_tests_misc(void); extern void litest_setup_tests_keyboard(void); extern void litest_setup_tests_device(void); extern void litest_setup_tests_gestures(void); +extern void litest_setup_tests_lid(void); void litest_fail_condition(const char *file, @@ -228,6 +229,8 @@ enum litest_device_type { LITEST_ACER_HAWAII_TOUCHPAD, LITEST_SYNAPTICS_RMI4, LITEST_MOUSE_WHEEL_TILT, + LITEST_LID_SWITCH, + LITEST_LID_SWITCH_SURFACE3, }; enum litest_device_feature { @@ -259,6 +262,7 @@ enum litest_device_feature { LITEST_STRIP = 1 << 23, LITEST_TRACKBALL = 1 << 24, LITEST_LEDS = 1 << 25, + LITEST_SWITCH = 1 << 26, }; struct litest_device { @@ -526,6 +530,9 @@ litest_keyboard_key(struct litest_device *d, unsigned int key, bool is_press); +void litest_lid_action(struct litest_device *d, + enum libinput_switch_state state); + void litest_wait_for_event(struct libinput *li); @@ -589,6 +596,11 @@ litest_is_pad_strip_event(struct libinput_event *event, unsigned int number, enum libinput_tablet_pad_strip_axis_source source); +struct libinput_event_switch * +litest_is_switch_event(struct libinput_event *event, + enum libinput_switch sw, + enum libinput_switch_state state); + void litest_assert_button_event(struct libinput *li, unsigned int button, diff --git a/test/test-device.c b/test/test-device.c index 3fa35d8..8f06f7e 100644 --- a/test/test-device.c +++ b/test/test-device.c @@ -1493,6 +1493,7 @@ START_TEST(device_capability_at_least_one) LIBINPUT_DEVICE_CAP_TABLET_TOOL, LIBINPUT_DEVICE_CAP_TABLET_PAD, LIBINPUT_DEVICE_CAP_GESTURE, + LIBINPUT_DEVICE_CAP_SWITCH, }; enum libinput_device_capability *cap; int ncaps = 0; @@ -1512,7 +1513,7 @@ START_TEST(device_capability_check_invalid) struct libinput_device *device = dev->libinput_device; ck_assert(!libinput_device_has_capability(device, -1)); - ck_assert(!libinput_device_has_capability(device, 6)); + ck_assert(!libinput_device_has_capability(device, 7)); ck_assert(!libinput_device_has_capability(device, 0xffff)); } diff --git a/test/test-lid.c b/test/test-lid.c new file mode 100644 index 0000000..a5f439a --- /dev/null +++ b/test/test-lid.c @@ -0,0 +1,473 @@ +/* + * Copyright © 2017 James Ye <jye836@gmail.com> + * + * 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 <check.h> +#include <libinput.h> + +#include "libinput-util.h" +#include "litest.h" + +START_TEST(lid_switch) +{ + struct litest_device *sw = litest_current_device(); + struct libinput *li = sw->libinput; + struct libinput_event *event; + + litest_drain_events(li); + + /* lid closed */ + litest_lid_action(sw, LIBINPUT_SWITCH_STATE_ON); + libinput_dispatch(li); + + event = libinput_get_event(li); + litest_is_switch_event(event, + LIBINPUT_SWITCH_LID, + LIBINPUT_SWITCH_STATE_ON); + libinput_event_destroy(event); + + /* lid opened */ + litest_lid_action(sw, LIBINPUT_SWITCH_STATE_OFF); + libinput_dispatch(li); + + event = libinput_get_event(li); + litest_is_switch_event(event, + LIBINPUT_SWITCH_LID, + LIBINPUT_SWITCH_STATE_OFF); + libinput_event_destroy(event); + + litest_assert_empty_queue(li); +} +END_TEST + +START_TEST(lid_switch_double) +{ + struct litest_device *sw = litest_current_device(); + struct libinput *li = sw->libinput; + struct libinput_event *event; + + litest_drain_events(li); + + litest_lid_action(sw, LIBINPUT_SWITCH_STATE_ON); + libinput_dispatch(li); + + event = libinput_get_event(li); + litest_is_switch_event(event, + LIBINPUT_SWITCH_LID, + LIBINPUT_SWITCH_STATE_ON); + libinput_event_destroy(event); + + /* This will be filtered by the kernel, so this test is a bit + * useless */ + litest_lid_action(sw, LIBINPUT_SWITCH_STATE_ON); + libinput_dispatch(li); + + litest_assert_empty_queue(li); +} +END_TEST + +static bool +lid_switch_is_reliable(struct litest_device *dev) +{ + struct udev_device *udev_device; + const char *prop; + bool is_reliable = false; + + udev_device = libinput_device_get_udev_device(dev->libinput_device); + prop = udev_device_get_property_value(udev_device, + "LIBINPUT_ATTR_LID_SWITCH_RELIABILITY"); + + is_reliable = prop && streq(prop, "reliable"); + udev_device_unref(udev_device); + + return is_reliable; +} + +START_TEST(lid_switch_down_on_init) +{ + struct litest_device *sw = litest_current_device(); + struct libinput *li; + struct libinput_event *event; + + if (!lid_switch_is_reliable(sw)) + return; + + litest_lid_action(sw, LIBINPUT_SWITCH_STATE_ON); + + /* need separate context to test */ + li = litest_create_context(); + libinput_path_add_device(li, + libevdev_uinput_get_devnode(sw->uinput)); + libinput_dispatch(li); + + litest_wait_for_event_of_type(li, LIBINPUT_EVENT_SWITCH_TOGGLE, -1); + event = libinput_get_event(li); + litest_is_switch_event(event, + LIBINPUT_SWITCH_LID, + LIBINPUT_SWITCH_STATE_ON); + libinput_event_destroy(event); + + while ((event = libinput_get_event(li))) { + ck_assert_int_ne(libinput_event_get_type(event), + LIBINPUT_EVENT_SWITCH_TOGGLE); + libinput_event_destroy(event); + } + + litest_lid_action(sw, LIBINPUT_SWITCH_STATE_OFF); + libinput_dispatch(li); + event = libinput_get_event(li); + litest_is_switch_event(event, + LIBINPUT_SWITCH_LID, + LIBINPUT_SWITCH_STATE_OFF); + libinput_event_destroy(event); + litest_assert_empty_queue(li); + + libinput_unref(li); + +} +END_TEST + +START_TEST(lid_switch_not_down_on_init) +{ + struct litest_device *sw = litest_current_device(); + struct libinput *li; + struct libinput_event *event; + + if (lid_switch_is_reliable(sw)) + return; + + litest_lid_action(sw, LIBINPUT_SWITCH_STATE_ON); + + /* need separate context to test */ + li = litest_create_context(); + libinput_path_add_device(li, + libevdev_uinput_get_devnode(sw->uinput)); + libinput_dispatch(li); + + while ((event = libinput_get_event(li)) != NULL) { + ck_assert_int_ne(libinput_event_get_type(event), + LIBINPUT_EVENT_SWITCH_TOGGLE); + libinput_event_destroy(event); + } + + litest_lid_action(sw, LIBINPUT_SWITCH_STATE_OFF); + litest_assert_empty_queue(li); + libinput_unref(li); +} +END_TEST + +static inline struct litest_device * +lid_init_paired_touchpad(struct libinput *li) +{ + enum litest_device_type which = LITEST_SYNAPTICS_I2C; + + return litest_add_device(li, which); +} + +START_TEST(lid_disable_touchpad) +{ + struct litest_device *sw = litest_current_device(); + struct litest_device *touchpad; + struct libinput *li = sw->libinput; + + touchpad = lid_init_paired_touchpad(li); + litest_disable_tap(touchpad->libinput_device); + litest_drain_events(li); + + /* lid is down - no events */ + litest_lid_action(sw, LIBINPUT_SWITCH_STATE_ON); + litest_assert_only_typed_events(li, LIBINPUT_EVENT_SWITCH_TOGGLE); + + litest_touch_down(touchpad, 0, 50, 50); + litest_touch_move_to(touchpad, 0, 50, 50, 70, 50, 10, 1); + litest_touch_up(touchpad, 0); + litest_assert_empty_queue(li); + + /* lid is up - motion events */ + litest_lid_action(sw, LIBINPUT_SWITCH_STATE_OFF); + litest_assert_only_typed_events(li, LIBINPUT_EVENT_SWITCH_TOGGLE); + + litest_touch_down(touchpad, 0, 50, 50); + litest_touch_move_to(touchpad, 0, 50, 50, 70, 50, 10, 1); + litest_touch_up(touchpad, 0); + litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION); + + litest_delete_device(touchpad); +} +END_TEST + +START_TEST(lid_disable_touchpad_during_touch) +{ + struct litest_device *sw = litest_current_device(); + struct litest_device *touchpad; + struct libinput *li = sw->libinput; + + touchpad = lid_init_paired_touchpad(li); + litest_disable_tap(touchpad->libinput_device); + litest_drain_events(li); + + litest_touch_down(touchpad, 0, 50, 50); + litest_touch_move_to(touchpad, 0, 50, 50, 70, 50, 5, 1); + litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION); + + litest_lid_action(sw, LIBINPUT_SWITCH_STATE_ON); + litest_assert_only_typed_events(li, LIBINPUT_EVENT_SWITCH_TOGGLE); + + litest_touch_move_to(touchpad, 0, 70, 50, 50, 50, 5, 1); + litest_touch_up(touchpad, 0); + litest_assert_empty_queue(li); + + litest_delete_device(touchpad); +} +END_TEST + +START_TEST(lid_disable_touchpad_edge_scroll) +{ + struct litest_device *sw = litest_current_device(); + struct litest_device *touchpad; + struct libinput *li = sw->libinput; + + touchpad = lid_init_paired_touchpad(li); + litest_enable_edge_scroll(touchpad); + + litest_drain_events(li); + + litest_lid_action(sw, LIBINPUT_SWITCH_STATE_ON); + litest_assert_only_typed_events(li, LIBINPUT_EVENT_SWITCH_TOGGLE); + + litest_touch_down(touchpad, 0, 99, 20); + libinput_dispatch(li); + litest_timeout_edgescroll(); + libinput_dispatch(li); + litest_assert_empty_queue(li); + + litest_touch_move_to(touchpad, 0, 99, 20, 99, 80, 60, 10); + libinput_dispatch(li); + litest_assert_empty_queue(li); + + litest_touch_move_to(touchpad, 0, 99, 80, 99, 20, 60, 10); + litest_touch_up(touchpad, 0); + libinput_dispatch(li); + litest_assert_empty_queue(li); + + litest_delete_device(touchpad); +} +END_TEST + +START_TEST(lid_disable_touchpad_edge_scroll_interrupt) +{ + struct litest_device *sw = litest_current_device(); + struct litest_device *touchpad; + struct libinput *li = sw->libinput; + struct libinput_event *event; + + touchpad = lid_init_paired_touchpad(li); + litest_enable_edge_scroll(touchpad); + + litest_drain_events(li); + + litest_touch_down(touchpad, 0, 99, 20); + libinput_dispatch(li); + litest_timeout_edgescroll(); + litest_touch_move_to(touchpad, 0, 99, 20, 99, 30, 10, 10); + libinput_dispatch(li); + litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_AXIS); + + litest_lid_action(sw, LIBINPUT_SWITCH_STATE_ON); + libinput_dispatch(li); + + event = libinput_get_event(li); + litest_is_axis_event(event, + LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL, + LIBINPUT_POINTER_AXIS_SOURCE_FINGER); + libinput_event_destroy(event); + + event = libinput_get_event(li); + litest_is_switch_event(event, + LIBINPUT_SWITCH_LID, + LIBINPUT_SWITCH_STATE_ON); + libinput_event_destroy(event); + + litest_delete_device(touchpad); +} +END_TEST + +START_TEST(lid_disable_touchpad_already_open) +{ + struct litest_device *sw = litest_current_device(); + struct litest_device *touchpad; + struct libinput *li = sw->libinput; + + touchpad = lid_init_paired_touchpad(li); + litest_disable_tap(touchpad->libinput_device); + litest_drain_events(li); + + /* default: lid is up - motion events */ + litest_touch_down(touchpad, 0, 50, 50); + litest_touch_move_to(touchpad, 0, 50, 50, 70, 50, 10, 1); + litest_touch_up(touchpad, 0); + litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION); + + /* open lid - motion events */ + litest_lid_action(sw, LIBINPUT_SWITCH_STATE_OFF); + litest_assert_empty_queue(li); + + litest_touch_down(touchpad, 0, 50, 50); + litest_touch_move_to(touchpad, 0, 50, 50, 70, 50, 10, 1); + litest_touch_up(touchpad, 0); + litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION); + + litest_delete_device(touchpad); +} +END_TEST + +START_TEST(lid_open_on_key) +{ + struct litest_device *sw = litest_current_device(); + struct litest_device *keyboard; + struct libinput *li = sw->libinput; + struct libinput_event *event; + + keyboard = litest_add_device(li, LITEST_KEYBOARD); + + litest_lid_action(sw, LIBINPUT_SWITCH_STATE_ON); + litest_drain_events(li); + + litest_event(keyboard, EV_KEY, KEY_A, 1); + litest_event(keyboard, EV_SYN, SYN_REPORT, 0); + litest_event(keyboard, EV_KEY, KEY_A, 0); + litest_event(keyboard, EV_SYN, SYN_REPORT, 0); + libinput_dispatch(li); + + litest_wait_for_event_of_type(li, LIBINPUT_EVENT_SWITCH_TOGGLE, -1); + event = libinput_get_event(li); + litest_is_switch_event(event, + LIBINPUT_SWITCH_LID, + LIBINPUT_SWITCH_STATE_OFF); + + litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY); + + litest_lid_action(sw, LIBINPUT_SWITCH_STATE_OFF); + litest_assert_empty_queue(li); + + libinput_event_destroy(event); + litest_delete_device(keyboard); +} +END_TEST + +START_TEST(lid_open_on_key_touchpad_enabled) +{ + struct litest_device *sw = litest_current_device(); + struct litest_device *keyboard, *touchpad; + struct libinput *li = sw->libinput; + + keyboard = litest_add_device(li, LITEST_KEYBOARD); + touchpad = litest_add_device(li, LITEST_SYNAPTICS_I2C); + + litest_lid_action(sw, LIBINPUT_SWITCH_STATE_ON); + litest_drain_events(li); + + litest_event(keyboard, EV_KEY, KEY_A, 1); + litest_event(keyboard, EV_SYN, SYN_REPORT, 0); + litest_event(keyboard, EV_KEY, KEY_A, 0); + litest_event(keyboard, EV_SYN, SYN_REPORT, 0); + litest_drain_events(li); + litest_timeout_dwt_long(); + + litest_touch_down(touchpad, 0, 50, 50); + litest_touch_move_to(touchpad, 0, 50, 50, 70, 70, 10, 1); + litest_touch_up(touchpad, 0); + libinput_dispatch(li); + + litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION); + + litest_delete_device(keyboard); + litest_delete_device(touchpad); +} +END_TEST + +START_TEST(lid_update_hw_on_key) +{ + struct litest_device *sw = litest_current_device(); + struct libinput *li = sw->libinput; + struct libinput *li2; + struct litest_device *keyboard; + struct libinput_event *event; + + sleep(5); + keyboard = litest_add_device(li, LITEST_KEYBOARD); + + /* separate context to listen to the fake hw event */ + li2 = litest_create_context(); + libinput_path_add_device(li2, + libevdev_uinput_get_devnode(sw->uinput)); + litest_drain_events(li2); + + litest_lid_action(sw, LIBINPUT_SWITCH_STATE_ON); + litest_drain_events(li); + + libinput_dispatch(li2); + event = libinput_get_event(li2); + litest_is_switch_event(event, + LIBINPUT_SWITCH_LID, + LIBINPUT_SWITCH_STATE_ON); + libinput_event_destroy(event); + + litest_event(keyboard, EV_KEY, KEY_A, 1); + litest_event(keyboard, EV_SYN, SYN_REPORT, 0); + litest_event(keyboard, EV_KEY, KEY_A, 0); + litest_event(keyboard, EV_SYN, SYN_REPORT, 0); + litest_drain_events(li); + + libinput_dispatch(li2); + event = libinput_get_event(li2); + litest_is_switch_event(event, + LIBINPUT_SWITCH_LID, + LIBINPUT_SWITCH_STATE_OFF); + libinput_event_destroy(event); + litest_assert_empty_queue(li); + + libinput_unref(li2); + litest_delete_device(keyboard); +} +END_TEST + +void +litest_setup_tests_lid(void) +{ + litest_add("lid:switch", lid_switch, LITEST_SWITCH, LITEST_ANY); + litest_add("lid:switch", lid_switch_double, LITEST_SWITCH, LITEST_ANY); + litest_add("lid:switch", lid_switch_down_on_init, LITEST_SWITCH, LITEST_ANY); + litest_add("lid:switch", lid_switch_not_down_on_init, LITEST_SWITCH, LITEST_ANY); + litest_add("lid:disable_touchpad", lid_disable_touchpad, LITEST_SWITCH, LITEST_ANY); + litest_add("lid:disable_touchpad", lid_disable_touchpad_during_touch, LITEST_SWITCH, LITEST_ANY); + litest_add("lid:disable_touchpad", lid_disable_touchpad_edge_scroll, LITEST_SWITCH, LITEST_ANY); + litest_add("lid:disable_touchpad", lid_disable_touchpad_edge_scroll_interrupt, LITEST_SWITCH, LITEST_ANY); + litest_add("lid:disable_touchpad", lid_disable_touchpad_already_open, LITEST_SWITCH, LITEST_ANY); + + litest_add("lid:keyboard", lid_open_on_key, LITEST_SWITCH, LITEST_ANY); + litest_add("lid:keyboard", lid_open_on_key_touchpad_enabled, LITEST_SWITCH, LITEST_ANY); + + litest_add_for_device("lid:buggy", lid_update_hw_on_key, LITEST_LID_SWITCH_SURFACE3); +} diff --git a/test/test-misc.c b/test/test-misc.c index ed5471d..36cabdc 100644 --- a/test/test-misc.c +++ b/test/test-misc.c @@ -135,6 +135,7 @@ START_TEST(event_conversion_device_notify) ck_assert(libinput_event_get_gesture_event(event) == NULL); ck_assert(libinput_event_get_tablet_tool_event(event) == NULL); ck_assert(libinput_event_get_tablet_pad_event(event) == NULL); + ck_assert(libinput_event_get_switch_event(event) == NULL); litest_restore_log_handler(li); } @@ -192,6 +193,7 @@ START_TEST(event_conversion_pointer) ck_assert(libinput_event_get_gesture_event(event) == NULL); ck_assert(libinput_event_get_tablet_tool_event(event) == NULL); ck_assert(libinput_event_get_tablet_pad_event(event) == NULL); + ck_assert(libinput_event_get_switch_event(event) == NULL); litest_restore_log_handler(li); } libinput_event_destroy(event); @@ -243,6 +245,7 @@ START_TEST(event_conversion_pointer_abs) ck_assert(libinput_event_get_gesture_event(event) == NULL); ck_assert(libinput_event_get_tablet_tool_event(event) == NULL); ck_assert(libinput_event_get_tablet_pad_event(event) == NULL); + ck_assert(libinput_event_get_switch_event(event) == NULL); litest_restore_log_handler(li); } libinput_event_destroy(event); @@ -287,6 +290,7 @@ START_TEST(event_conversion_key) ck_assert(libinput_event_get_gesture_event(event) == NULL); ck_assert(libinput_event_get_tablet_tool_event(event) == NULL); ck_assert(libinput_event_get_tablet_pad_event(event) == NULL); + ck_assert(libinput_event_get_switch_event(event) == NULL); litest_restore_log_handler(li); } libinput_event_destroy(event); @@ -338,6 +342,7 @@ START_TEST(event_conversion_touch) ck_assert(libinput_event_get_gesture_event(event) == NULL); ck_assert(libinput_event_get_tablet_tool_event(event) == NULL); ck_assert(libinput_event_get_tablet_pad_event(event) == NULL); + ck_assert(libinput_event_get_switch_event(event) == NULL); litest_restore_log_handler(li); } libinput_event_destroy(event); @@ -387,6 +392,7 @@ START_TEST(event_conversion_gesture) ck_assert(libinput_event_get_keyboard_event(event) == NULL); ck_assert(libinput_event_get_touch_event(event) == NULL); ck_assert(libinput_event_get_tablet_pad_event(event) == NULL); + ck_assert(libinput_event_get_switch_event(event) == NULL); litest_restore_log_handler(li); } libinput_event_destroy(event); @@ -434,6 +440,7 @@ START_TEST(event_conversion_tablet) ck_assert(libinput_event_get_keyboard_event(event) == NULL); ck_assert(libinput_event_get_touch_event(event) == NULL); ck_assert(libinput_event_get_tablet_pad_event(event) == NULL); + ck_assert(libinput_event_get_switch_event(event) == NULL); litest_restore_log_handler(li); } libinput_event_destroy(event); @@ -477,6 +484,7 @@ START_TEST(event_conversion_tablet_pad) ck_assert(libinput_event_get_keyboard_event(event) == NULL); ck_assert(libinput_event_get_touch_event(event) == NULL); ck_assert(libinput_event_get_tablet_tool_event(event) == NULL); + ck_assert(libinput_event_get_switch_event(event) == NULL); litest_restore_log_handler(li); } libinput_event_destroy(event); @@ -486,6 +494,47 @@ START_TEST(event_conversion_tablet_pad) } END_TEST +START_TEST(event_conversion_switch) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct libinput_event *event; + int sw = 0; + + litest_lid_action(dev, LIBINPUT_SWITCH_STATE_ON); + litest_lid_action(dev, LIBINPUT_SWITCH_STATE_OFF); + libinput_dispatch(li); + + while ((event = libinput_get_event(li))) { + enum libinput_event_type type; + type = libinput_event_get_type(event); + + if (type == LIBINPUT_EVENT_SWITCH_TOGGLE) { + struct libinput_event_switch *s; + struct libinput_event *base; + s = libinput_event_get_switch_event(event); + base = libinput_event_switch_get_base_event(s); + ck_assert(event == base); + + sw++; + + litest_disable_log_handler(li); + ck_assert(libinput_event_get_device_notify_event(event) == NULL); + ck_assert(libinput_event_get_keyboard_event(event) == NULL); + ck_assert(libinput_event_get_pointer_event(event) == NULL); + ck_assert(libinput_event_get_touch_event(event) == NULL); + ck_assert(libinput_event_get_gesture_event(event) == NULL); + ck_assert(libinput_event_get_tablet_tool_event(event) == NULL); + ck_assert(libinput_event_get_tablet_pad_event(event) == NULL); + litest_restore_log_handler(li); + } + libinput_event_destroy(event); + } + + ck_assert_int_gt(sw, 0); +} +END_TEST + START_TEST(bitfield_helpers) { /* This value has a bit set on all of the word boundaries we want to @@ -859,6 +908,45 @@ START_TEST(dimension_prop_parser) } END_TEST +struct parser_test_reliability { + char *tag; + bool success; + enum switch_reliability reliability; +}; + +START_TEST(reliability_prop_parser) +{ + struct parser_test_reliability tests[] = { + { "reliable", true, RELIABILITY_RELIABLE }, + { "unreliable", false, 0 }, + { "", false, 0 }, + { "0", false, 0 }, + { "1", false, 0 }, + { NULL, false, 0, } + }; + enum switch_reliability r; + bool success; + int i; + + for (i = 0; tests[i].tag != NULL; i++) { + r = 0xaf; + success = parse_switch_reliability_property(tests[i].tag, &r); + ck_assert(success == tests[i].success); + if (success) + ck_assert_int_eq(r, tests[i].reliability); + else + ck_assert_int_eq(r, 0xaf); + } + + success = parse_switch_reliability_property(NULL, &r); + ck_assert(success == true); + ck_assert_int_eq(r, RELIABILITY_UNKNOWN); + + success = parse_switch_reliability_property("foo", NULL); + ck_assert(success == false); +} +END_TEST + START_TEST(time_conversion) { ck_assert_int_eq(us(10), 10); @@ -1118,6 +1206,7 @@ litest_setup_tests_misc(void) litest_add_for_device("events:conversion", event_conversion_gesture, LITEST_BCM5974); litest_add_for_device("events:conversion", event_conversion_tablet, LITEST_WACOM_CINTIQ); litest_add_for_device("events:conversion", event_conversion_tablet_pad, LITEST_WACOM_INTUOS5_PAD); + litest_add_for_device("events:conversion", event_conversion_switch, LITEST_LID_SWITCH); litest_add_no_device("misc:bitfield_helpers", bitfield_helpers); litest_add_no_device("context:refcount", context_ref_counting); @@ -1130,6 +1219,7 @@ litest_setup_tests_misc(void) litest_add_no_device("misc:parser", wheel_click_count_parser); litest_add_no_device("misc:parser", trackpoint_accel_parser); litest_add_no_device("misc:parser", dimension_prop_parser); + litest_add_no_device("misc:parser", reliability_prop_parser); litest_add_no_device("misc:parser", safe_atoi_test); litest_add_no_device("misc:parser", safe_atod_test); litest_add_no_device("misc:parser", strsplit_test); diff --git a/tools/event-debug.c b/tools/event-debug.c index a3e460a..779b54a 100644 --- a/tools/event-debug.c +++ b/tools/event-debug.c @@ -133,6 +133,9 @@ print_event_header(struct libinput_event *ev) case LIBINPUT_EVENT_TABLET_PAD_STRIP: type = "TABLET_PAD_STRIP"; break; + case LIBINPUT_EVENT_SWITCH_TOGGLE: + type = "SWITCH_TOGGLE"; + break; } prefix = (last_device != dev) ? '-' : ' '; @@ -194,6 +197,9 @@ print_device_notify(struct libinput_event *ev) if (libinput_device_has_capability(dev, LIBINPUT_DEVICE_CAP_TABLET_PAD)) printf("P"); + if (libinput_device_has_capability(dev, + LIBINPUT_DEVICE_CAP_SWITCH)) + printf("S"); if (libinput_device_get_size(dev, &w, &h) == 0) printf(" size %.0fx%.0fmm", w, h); @@ -706,6 +712,28 @@ print_tablet_pad_strip_event(struct libinput_event *ev) mode); } +static void +print_switch_event(struct libinput_event *ev) +{ + struct libinput_event_switch *sw = libinput_event_get_switch_event(ev); + enum libinput_switch_state state; + const char *which; + + print_event_time(libinput_event_switch_get_time(sw)); + + switch (libinput_event_switch_get_switch(sw)) { + case LIBINPUT_SWITCH_LID: + which = "lid"; + break; + default: + abort(); + } + + state = libinput_event_switch_get_switch_state(sw); + + printf("switch %s state %d\n", which, state); +} + static int handle_and_print_events(struct libinput *li) { @@ -794,6 +822,9 @@ handle_and_print_events(struct libinput *li) case LIBINPUT_EVENT_TABLET_PAD_STRIP: print_tablet_pad_strip_event(ev); break; + case LIBINPUT_EVENT_SWITCH_TOGGLE: + print_switch_event(ev); + break; } libinput_event_destroy(ev); diff --git a/tools/event-gui.c b/tools/event-gui.c index e819901..155cc95 100644 --- a/tools/event-gui.c +++ b/tools/event-gui.c @@ -845,6 +845,8 @@ handle_event_libinput(GIOChannel *source, GIOCondition condition, gpointer data) case LIBINPUT_EVENT_TABLET_PAD_RING: case LIBINPUT_EVENT_TABLET_PAD_STRIP: break; + case LIBINPUT_EVENT_SWITCH_TOGGLE: + break; } libinput_event_destroy(ev); diff --git a/udev/90-libinput-model-quirks.hwdb b/udev/90-libinput-model-quirks.hwdb index 04bdf9a..86eea0d 100644 --- a/udev/90-libinput-model-quirks.hwdb +++ b/udev/90-libinput-model-quirks.hwdb @@ -17,6 +17,15 @@ # Sort by brand, model ########################################## +# Chassis types 9 (Laptop) and 10 +# (Notebook) are expected to have working +# lid switches +########################################## +libinput:name:*Lid Switch*:dmi:*:ct10:* +libinput:name:*Lid Switch*:dmi:*:ct9:* + LIBINPUT_ATTR_LID_SWITCH_RELIABILITY=reliable + +########################################## # ALPS ########################################## libinput:name:*AlpsPS/2 ALPS DualPoint TouchPad:dmi:* @@ -151,6 +160,13 @@ libinput:name:*Logitech M570*:dmi:* LIBINPUT_MODEL_TRACKBALL=1 ########################################## +# Microsoft +########################################## +# Surface3 needs us to write the open lid switch event +libinput:name:*Lid Switch*:dmi:*svnMicrosoftCorporation:pnSurface3:* + LIBINPUT_ATTR_LID_SWITCH_RELIABILITY=write_open + +########################################## # Synaptics ########################################## libinput:touchpad:input:b0011v0002p0007* @@ -187,3 +203,4 @@ libinput:touchpad:input:b0003v056Ap* ########################################## libinput:name:*Trackball*:dmi:* LIBINPUT_MODEL_TRACKBALL=1 + diff --git a/udev/parse_hwdb.py b/udev/parse_hwdb.py index d90c90f..d079be2 100755 --- a/udev/parse_hwdb.py +++ b/udev/parse_hwdb.py @@ -98,10 +98,16 @@ def property_grammar(): ('LIBINPUT_ATTR_SIZE_HINT', Group(dimension('SETTINGS*'))), ('LIBINPUT_ATTR_RESOLUTION_HINT', Group(dimension('SETTINGS*'))), ) + size_props = [Literal(name)('NAME') - Suppress('=') - val('VALUE') for name, val in sz_props] - grammar = Or(model_props + size_props); + reliability_tags = Or(('reliable', 'write_open')) + reliability = [Literal('LIBINPUT_ATTR_LID_SWITCH_RELIABILITY')('NAME') - + Suppress('=') - + reliability_tags('VALUE')] + + grammar = Or(model_props + size_props + reliability) return grammar |