diff --git a/meson.build b/meson.build index 02c629e2..a20018ff 100644 --- a/meson.build +++ b/meson.build @@ -156,7 +156,6 @@ src_libinput = [ 'src/libinput-private.h', 'src/evdev.c', 'src/evdev.h', - 'src/evdev-lid.c', 'src/evdev-middle-button.c', 'src/evdev-mt-touchpad.c', 'src/evdev-mt-touchpad.h', diff --git a/src/evdev-lid.c b/src/evdev-lid.c deleted file mode 100644 index 6c438e00..00000000 --- a/src/evdev-lid.c +++ /dev/null @@ -1,321 +0,0 @@ -/* - * Copyright © 2017 James Ye - * Copyright © 2017 Red Hat, Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -#include "config.h" - -#include "libinput.h" -#include "evdev.h" -#include "libinput-private.h" - -struct lid_switch_dispatch { - struct evdev_dispatch base; - struct evdev_device *device; - enum switch_reliability reliability; - - bool lid_is_closed; - bool lid_is_closed_client_state; - - struct { - struct evdev_device *keyboard; - struct libinput_event_listener listener; - } keyboard; -}; - -static inline struct lid_switch_dispatch* -lid_dispatch(struct evdev_dispatch *dispatch) -{ - evdev_verify_dispatch_type(dispatch, DISPATCH_LID_SWITCH); - - return container_of(dispatch, struct lid_switch_dispatch, base); -} - -static void -lid_switch_notify_toggle(struct lid_switch_dispatch *dispatch, - struct evdev_device *device, - uint64_t time) -{ - if (dispatch->lid_is_closed ^ dispatch->lid_is_closed_client_state) { - switch_notify_toggle(&device->base, - time, - LIBINPUT_SWITCH_LID, - dispatch->lid_is_closed); - dispatch->lid_is_closed_client_state = dispatch->lid_is_closed; - } -} - -static void -lid_switch_keyboard_event(uint64_t time, - struct libinput_event *event, - void *data) -{ - struct lid_switch_dispatch *dispatch = lid_dispatch(data); - - if (!dispatch->lid_is_closed) - return; - - if (event->type != LIBINPUT_EVENT_KEYBOARD_KEY) - return; - - if (dispatch->reliability == RELIABILITY_WRITE_OPEN) { - int fd = libevdev_get_fd(dispatch->device->evdev); - struct input_event ev[2] = { - {{ 0, 0 }, EV_SW, SW_LID, 0 }, - {{ 0, 0 }, EV_SYN, SYN_REPORT, 0 }, - }; - - (void)write(fd, ev, sizeof(ev)); - /* In case write() fails, we sync the lid state manually - * regardless. */ - } - - /* Posting the event here means we preempt the keyboard events that - * caused us to wake up, so the lid event is always passed on before - * the key event. - */ - dispatch->lid_is_closed = false; - lid_switch_notify_toggle(dispatch, dispatch->device, time); -} - -static void -lid_switch_toggle_keyboard_listener(struct lid_switch_dispatch *dispatch, - bool is_closed) -{ - if (!dispatch->keyboard.keyboard) - return; - - if (is_closed) { - libinput_device_add_event_listener( - &dispatch->keyboard.keyboard->base, - &dispatch->keyboard.listener, - lid_switch_keyboard_event, - dispatch); - } else { - libinput_device_remove_event_listener( - &dispatch->keyboard.listener); - libinput_device_init_event_listener( - &dispatch->keyboard.listener); - } -} - -static void -lid_switch_process_switch(struct lid_switch_dispatch *dispatch, - struct evdev_device *device, - struct input_event *e, - uint64_t time) -{ - bool is_closed; - - switch (e->code) { - case SW_LID: - is_closed = !!e->value; - - if (dispatch->lid_is_closed == is_closed) - return; - lid_switch_toggle_keyboard_listener(dispatch, - is_closed); - - dispatch->lid_is_closed = is_closed; - - lid_switch_notify_toggle(dispatch, device, time); - break; - } -} - -static void -lid_switch_process(struct evdev_dispatch *evdev_dispatch, - struct evdev_device *device, - struct input_event *event, - uint64_t time) -{ - struct lid_switch_dispatch *dispatch = lid_dispatch(evdev_dispatch); - - switch (event->type) { - case EV_SW: - lid_switch_process_switch(dispatch, device, event, time); - break; - case EV_SYN: - break; - default: - assert(0 && "Unknown event type"); - break; - } -} - -static inline enum switch_reliability -evdev_read_switch_reliability_prop(struct evdev_device *device) -{ - const char *prop; - enum switch_reliability r; - - prop = udev_device_get_property_value(device->udev_device, - "LIBINPUT_ATTR_LID_SWITCH_RELIABILITY"); - if (!parse_switch_reliability_property(prop, &r)) { - evdev_log_error(device, - "%s: switch reliability set to unknown value '%s'\n", - device->devname, - prop); - r = RELIABILITY_UNKNOWN; - } else if (r == RELIABILITY_WRITE_OPEN) { - evdev_log_info(device, - "%s: will write switch open events\n", - device->devname); - } - - return r; -} - -static void -lid_switch_remove(struct evdev_dispatch *evdev_dispatch) -{ - struct lid_switch_dispatch *dispatch = lid_dispatch(evdev_dispatch); - - if (!dispatch->keyboard.keyboard) - return; - - libinput_device_remove_event_listener(&dispatch->keyboard.listener); -} - -static void -lid_switch_destroy(struct evdev_dispatch *evdev_dispatch) -{ - struct lid_switch_dispatch *dispatch = lid_dispatch(evdev_dispatch); - - free(dispatch); -} - -static void -lid_switch_pair_keyboard(struct evdev_device *lid_switch, - struct evdev_device *keyboard) -{ - struct lid_switch_dispatch *dispatch = - lid_dispatch(lid_switch->dispatch); - - if ((keyboard->tags & EVDEV_TAG_KEYBOARD) == 0) - return; - - if (dispatch->keyboard.keyboard) - return; - - if (keyboard->tags & EVDEV_TAG_INTERNAL_KEYBOARD) { - dispatch->keyboard.keyboard = keyboard; - evdev_log_debug(lid_switch, - "lid: keyboard paired with %s<->%s\n", - lid_switch->devname, - keyboard->devname); - - /* We need to init the event listener now only if the reported state - * is closed. */ - if (dispatch->lid_is_closed) - lid_switch_toggle_keyboard_listener(dispatch, - dispatch->lid_is_closed); - } -} - -static void -lid_switch_interface_device_added(struct evdev_device *device, - struct evdev_device *added_device) -{ - lid_switch_pair_keyboard(device, added_device); -} - -static void -lid_switch_interface_device_removed(struct evdev_device *device, - struct evdev_device *removed_device) -{ - struct lid_switch_dispatch *dispatch = lid_dispatch(device->dispatch); - - if (removed_device == dispatch->keyboard.keyboard) { - libinput_device_remove_event_listener( - &dispatch->keyboard.listener); - libinput_device_init_event_listener( - &dispatch->keyboard.listener); - dispatch->keyboard.keyboard = NULL; - } -} - -static void -lid_switch_sync_initial_state(struct evdev_device *device, - struct evdev_dispatch *evdev_dispatch) -{ - struct lid_switch_dispatch *dispatch = lid_dispatch(device->dispatch); - struct libevdev *evdev = device->evdev; - - dispatch->reliability = evdev_read_switch_reliability_prop(device); - - dispatch->lid_is_closed = libevdev_get_event_value(evdev, EV_SW, SW_LID); - dispatch->lid_is_closed_client_state = false; - - /* For the initial state sync, we depend on whether the lid switch - * is reliable. If we know it's reliable, we sync as expected. - * If we're not sure, we ignore the initial state and only sync on - * the first future lid close event. Laptops with a broken switch - * that always have the switch in 'on' state thus don't mess up our - * touchpad. - */ - if (dispatch->lid_is_closed && - dispatch->reliability == RELIABILITY_RELIABLE) { - uint64_t time; - time = libinput_now(evdev_libinput_context(device)); - lid_switch_notify_toggle(dispatch, device, time); - } -} - -struct evdev_dispatch_interface lid_switch_interface = { - lid_switch_process, - NULL, /* suspend */ - lid_switch_remove, - lid_switch_destroy, - lid_switch_interface_device_added, - lid_switch_interface_device_removed, - lid_switch_interface_device_removed, /* device_suspended, treat as remove */ - lid_switch_interface_device_added, /* device_resumed, treat as add */ - lid_switch_sync_initial_state, - NULL, /* toggle_touch */ -}; - -struct evdev_dispatch * -evdev_lid_switch_dispatch_create(struct evdev_device *lid_device) -{ - struct lid_switch_dispatch *dispatch; - int type; - - dispatch = zalloc(sizeof *dispatch); - dispatch->base.dispatch_type = DISPATCH_LID_SWITCH; - dispatch->base.interface = &lid_switch_interface; - dispatch->device = lid_device; - libinput_device_init_event_listener(&dispatch->keyboard.listener); - - evdev_init_sendevents(lid_device, &dispatch->base); - - dispatch->lid_is_closed = false; - - for (type = EV_KEY; type < EV_CNT; type++) { - if (type == EV_SW) - continue; - - libevdev_disable_event_type(lid_device->evdev, type); - } - - return &dispatch->base; -} diff --git a/src/evdev.c b/src/evdev.c index d08db900..9dec2c4f 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -1,7 +1,8 @@ /* * Copyright © 2010 Intel Corporation * Copyright © 2013 Jonas Ådahl - * Copyright © 2013-2015 Red Hat, Inc. + * Copyright © 2013-2017 Red Hat, Inc. + * Copyright © 2017 James Ye * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -170,6 +171,20 @@ fallback_keyboard_notify_key(struct fallback_dispatch *dispatch, keyboard_notify_key(&device->base, time, key, state); } +static void +fallback_lid_notify_toggle(struct fallback_dispatch *dispatch, + struct evdev_device *device, + uint64_t time) +{ + if (dispatch->lid.is_closed ^ dispatch->lid.is_closed_client_state) { + switch_notify_toggle(&device->base, + time, + LIBINPUT_SWITCH_LID, + dispatch->lid.is_closed); + dispatch->lid.is_closed_client_state = dispatch->lid.is_closed; + } +} + void evdev_pointer_notify_physical_button(struct evdev_device *device, uint64_t time, @@ -1086,6 +1101,82 @@ fallback_process_absolute_motion(struct fallback_dispatch *dispatch, } } +static void +fallback_lid_keyboard_event(uint64_t time, + struct libinput_event *event, + void *data) +{ + struct fallback_dispatch *dispatch = fallback_dispatch(data); + + if (!dispatch->lid.is_closed) + return; + + if (event->type != LIBINPUT_EVENT_KEYBOARD_KEY) + return; + + if (dispatch->lid.reliability == RELIABILITY_WRITE_OPEN) { + int fd = libevdev_get_fd(dispatch->device->evdev); + struct input_event ev[2] = { + {{ 0, 0 }, EV_SW, SW_LID, 0 }, + {{ 0, 0 }, EV_SYN, SYN_REPORT, 0 }, + }; + + (void)write(fd, ev, sizeof(ev)); + /* In case write() fails, we sync the lid state manually + * regardless. */ + } + + /* Posting the event here means we preempt the keyboard events that + * caused us to wake up, so the lid event is always passed on before + * the key event. + */ + dispatch->lid.is_closed = false; + fallback_lid_notify_toggle(dispatch, dispatch->device, time); +} + +static void +fallback_lid_toggle_keyboard_listener(struct fallback_dispatch *dispatch, + bool is_closed) +{ + if (!dispatch->lid.keyboard) + return; + + if (is_closed) { + libinput_device_add_event_listener( + &dispatch->lid.keyboard->base, + &dispatch->lid.listener, + fallback_lid_keyboard_event, + dispatch); + } else { + libinput_device_remove_event_listener( + &dispatch->lid.listener); + libinput_device_init_event_listener( + &dispatch->lid.listener); + } +} + +static inline void +fallback_process_switch(struct fallback_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; + fallback_lid_toggle_keyboard_listener(dispatch, is_closed); + + dispatch->lid.is_closed = is_closed; + fallback_lid_notify_toggle(dispatch, device, time); + break; + } +} + void evdev_notify_axis(struct evdev_device *device, uint64_t time, @@ -1317,6 +1408,9 @@ fallback_process(struct evdev_dispatch *evdev_dispatch, case EV_KEY: fallback_process_key(dispatch, device, event, time); break; + case EV_SW: + fallback_process_switch(dispatch, device, event, time); + break; case EV_SYN: sent = fallback_flush_pending_event(dispatch, device, time); switch (sent) { @@ -1434,6 +1528,46 @@ fallback_suspend(struct evdev_dispatch *evdev_dispatch, fallback_return_to_neutral_state(dispatch, device); } +static void +fallback_remove(struct evdev_dispatch *evdev_dispatch) +{ + struct fallback_dispatch *dispatch = fallback_dispatch(evdev_dispatch); + + if (!dispatch->lid.keyboard) + return; + + libinput_device_remove_event_listener(&dispatch->lid.listener); +} + +static void +fallback_sync_initial_state(struct evdev_device *device, + struct evdev_dispatch *evdev_dispatch) +{ + struct fallback_dispatch *dispatch = fallback_dispatch(evdev_dispatch); + uint64_t time = libinput_now(evdev_libinput_context(device)); + + if (device->tags & EVDEV_TAG_LID_SWITCH) { + struct libevdev *evdev = device->evdev; + + dispatch->lid.is_closed = libevdev_get_event_value(evdev, + EV_SW, + SW_LID); + dispatch->lid.is_closed_client_state = false; + + /* For the initial state sync, we depend on whether the lid switch + * is reliable. If we know it's reliable, we sync as expected. + * If we're not sure, we ignore the initial state and only sync on + * the first future lid close event. Laptops with a broken switch + * that always have the switch in 'on' state thus don't mess up our + * touchpad. + */ + if (dispatch->lid.is_closed && + dispatch->lid.reliability == RELIABILITY_RELIABLE) { + fallback_lid_notify_toggle(dispatch, device, time); + } + } +} + static void fallback_toggle_touch(struct evdev_dispatch *evdev_dispatch, struct evdev_device *device, @@ -1503,16 +1637,68 @@ evdev_calibration_get_default_matrix(struct libinput_device *libinput_device, return !matrix_is_identity(&device->abs.default_calibration); } +static void +fallback_lid_pair_keyboard(struct evdev_device *lid_switch, + struct evdev_device *keyboard) +{ + struct fallback_dispatch *dispatch = + fallback_dispatch(lid_switch->dispatch); + + if ((keyboard->tags & EVDEV_TAG_KEYBOARD) == 0 || + (lid_switch->tags & EVDEV_TAG_LID_SWITCH) == 0) + return; + + if (dispatch->lid.keyboard) + return; + + if (keyboard->tags & EVDEV_TAG_INTERNAL_KEYBOARD) { + dispatch->lid.keyboard = keyboard; + evdev_log_debug(lid_switch, + "lid: keyboard paired with %s<->%s\n", + lid_switch->devname, + keyboard->devname); + + /* We need to init the event listener now only if the reported state + * is closed. */ + if (dispatch->lid.is_closed) + fallback_lid_toggle_keyboard_listener(dispatch, + dispatch->lid.is_closed); + } +} + +static void +fallback_interface_device_added(struct evdev_device *device, + struct evdev_device *added_device) +{ + fallback_lid_pair_keyboard(device, added_device); +} + +static void +fallback_interface_device_removed(struct evdev_device *device, + struct evdev_device *removed_device) +{ + struct fallback_dispatch *dispatch = + fallback_dispatch(device->dispatch); + + if (removed_device == dispatch->lid.keyboard) { + libinput_device_remove_event_listener( + &dispatch->lid.listener); + libinput_device_init_event_listener( + &dispatch->lid.listener); + dispatch->lid.keyboard = NULL; + } +} + struct evdev_dispatch_interface fallback_interface = { fallback_process, fallback_suspend, - NULL, /* remove */ + fallback_remove, fallback_destroy, - NULL, /* device_added */ - NULL, /* device_removed */ - NULL, /* device_suspended */ - NULL, /* device_resumed */ - NULL, /* post_added */ + fallback_interface_device_added, + fallback_interface_device_removed, + fallback_interface_device_removed, /* device_suspended, treat as remove */ + fallback_interface_device_added, /* device_resumed, treat as add */ + fallback_sync_initial_state, /* post_added */ fallback_toggle_touch, /* toggle_touch */ }; @@ -1984,6 +2170,40 @@ fallback_dispatch_init_abs(struct fallback_dispatch *dispatch, evdev_device_init_abs_range_warnings(device); } +static inline enum switch_reliability +evdev_read_switch_reliability_prop(struct evdev_device *device) +{ + const char *prop; + enum switch_reliability r; + + prop = udev_device_get_property_value(device->udev_device, + "LIBINPUT_ATTR_LID_SWITCH_RELIABILITY"); + if (!parse_switch_reliability_property(prop, &r)) { + evdev_log_error(device, + "%s: switch reliability set to unknown value '%s'\n", + device->devname, + prop); + r = RELIABILITY_UNKNOWN; + } else if (r == RELIABILITY_WRITE_OPEN) { + evdev_log_info(device, + "%s: will write switch open events\n", + device->devname); + } + + return r; +} + +static inline void +fallback_dispatch_init_switch(struct fallback_dispatch *dispatch, + struct evdev_device *device) +{ + if (device->tags & EVDEV_TAG_LID_SWITCH) { + libinput_device_init_event_listener(&dispatch->lid.listener); + dispatch->lid.reliability = evdev_read_switch_reliability_prop(device); + dispatch->lid.is_closed = false; + } +} + static struct evdev_dispatch * fallback_dispatch_create(struct libinput_device *libinput_device) { @@ -1992,6 +2212,7 @@ fallback_dispatch_create(struct libinput_device *libinput_device) char timer_name[64]; dispatch = zalloc(sizeof *dispatch); + dispatch->device = evdev_device(libinput_device); dispatch->base.dispatch_type = DISPATCH_FALLBACK; dispatch->base.interface = &fallback_interface; dispatch->pending_event = EVDEV_NONE; @@ -2003,6 +2224,8 @@ fallback_dispatch_create(struct libinput_device *libinput_device) return NULL; } + fallback_dispatch_init_switch(dispatch, device); + if (device->left_handed.want_enabled) evdev_init_left_handed(device, evdev_change_to_left_handed); @@ -2894,11 +3117,9 @@ evdev_configure_device(struct evdev_device *device) 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); evdev_log_info(device, "device is a switch device\n"); - return dispatch; } if (device->seat_caps & EVDEV_DEVICE_POINTER && diff --git a/src/evdev.h b/src/evdev.h index 8df7ca25..0b8b2791 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -345,6 +345,7 @@ evdev_verify_dispatch_type(struct evdev_dispatch *dispatch, struct fallback_dispatch { struct evdev_dispatch base; + struct evdev_device *device; struct libinput_device_config_calibration calibration; @@ -391,6 +392,15 @@ struct fallback_dispatch { uint64_t button_up_time; struct libinput_timer timer; } debounce; + + struct { + enum switch_reliability reliability; + + bool is_closed; + bool is_closed_client_state; + struct evdev_device *keyboard; + struct libinput_event_listener listener; + } lid; }; static inline struct fallback_dispatch* diff --git a/test/test-switch.c b/test/test-switch.c index 45efeb02..045f226e 100644 --- a/test/test-switch.c +++ b/test/test-switch.c @@ -604,10 +604,8 @@ START_TEST(lid_key_press) litest_keyboard_key(sw, KEY_POWER, false); libinput_dispatch(li); - /* We should route the key events correctly, but for now we just - * ignore them. This test will fail once the key events are handled - * correctly. */ - litest_assert_empty_queue(li); + /* Check that we're routing key events from a lid device too */ + litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY); } END_TEST