enum libinput_button_state state)
{
/* If we've a trackpoint, send top buttons through the trackpoint */
- if (is_topbutton && tp->buttons.trackpoint) {
- struct evdev_dispatch *dispatch = tp->buttons.trackpoint->dispatch;
- struct input_event event;
- struct input_event syn_report = {{ 0, 0 }, EV_SYN, SYN_REPORT, 0 };
-
- event.time = us2tv(time);
- event.type = EV_KEY;
- event.code = button;
- event.value = (state == LIBINPUT_BUTTON_STATE_PRESSED) ? 1 : 0;
- syn_report.time = event.time;
- dispatch->interface->process(dispatch,
- tp->buttons.trackpoint,
- &event,
- time);
- dispatch->interface->process(dispatch,
- tp->buttons.trackpoint,
- &syn_report,
- time);
- return 1;
+ if (tp->buttons.trackpoint) {
+ if (is_topbutton) {
+ struct evdev_dispatch *dispatch = tp->buttons.trackpoint->dispatch;
+ struct input_event event;
+ struct input_event syn_report = {{ 0, 0 }, EV_SYN, SYN_REPORT, 0 };
+
+ event.time = us2tv(time);
+ event.type = EV_KEY;
+ event.code = button;
+ event.value = (state == LIBINPUT_BUTTON_STATE_PRESSED) ? 1 : 0;
+ syn_report.time = event.time;
+ dispatch->interface->process(dispatch,
+ tp->buttons.trackpoint,
+ &event,
+ time);
+ dispatch->interface->process(dispatch,
+ tp->buttons.trackpoint,
+ &syn_report,
+ time);
+ return 1;
+ }
+ /* Ignore button events not for the trackpoint while suspended */
+ if (tp->device->is_suspended)
+ return 0;
}
- /* Ignore button events not for the trackpoint while suspended */
- if (tp->device->is_suspended)
- return 0;
-
/* A button click always terminates edge scrolling, even if we
* don't end up sending a button event. */
tp_edge_scroll_stop_events(tp, time);
}
END_TEST
+START_TEST(device_button_down_remove)
+{
+ struct litest_device *lidev = litest_current_device();
+ struct litest_device *dev;
+ struct libinput *li;
+
+ for (int code = 0; code < KEY_MAX; code++) {
+ struct libinput_event *event;
+ struct libinput_event_pointer *p;
+ bool have_down = false,
+ have_up = false;
+ const char *keyname;
+ int button_down = 0, button_up = 0;
+
+ keyname = libevdev_event_code_get_name(EV_KEY, code);
+ if (!keyname ||
+ !strneq(keyname, "BTN_", 4) ||
+ strneq(keyname, "BTN_TOOL_", 9))
+ continue;
+
+ if (!libevdev_has_event_code(lidev->evdev, EV_KEY, code))
+ continue;
+
+ li = litest_create_context();
+ dev = litest_add_device(li, lidev->which);
+ litest_drain_events(li);
+
+ /* Clickpads require a touch down to trigger the button
+ * press */
+ if (libevdev_has_property(lidev->evdev, INPUT_PROP_BUTTONPAD)) {
+ litest_touch_down(dev, 0, 20, 90);
+ libinput_dispatch(li);
+ }
+
+ litest_event(dev, EV_KEY, code, 1);
+ litest_event(dev, EV_SYN, SYN_REPORT, 0);
+ libinput_dispatch(li);
+
+ litest_delete_device(dev);
+ libinput_dispatch(li);
+
+ while ((event = libinput_get_event(li))) {
+ if (libinput_event_get_type(event) !=
+ LIBINPUT_EVENT_POINTER_BUTTON) {
+ libinput_event_destroy(event);
+ continue;
+ }
+
+ p = libinput_event_get_pointer_event(event);
+ if (libinput_event_pointer_get_button_state(p)) {
+ ck_assert(button_down == 0);
+ button_down = libinput_event_pointer_get_button(p);
+ } else {
+ ck_assert(button_up == 0);
+ button_up = libinput_event_pointer_get_button(p);
+ ck_assert_int_eq(button_down, button_up);
+ }
+ libinput_event_destroy(event);
+ }
+
+ libinput_unref(li);
+ ck_assert_int_eq(have_down, have_up);
+ }
+}
+END_TEST
+
TEST_COLLECTION(device)
{
struct range abs_range = { 0, ABS_MISC };
litest_add("device:output", device_no_output, LITEST_KEYS, LITEST_ANY);
litest_add("device:seat", device_seat_phys_name, LITEST_ANY, LITEST_ANY);
+
+ litest_add("device:button", device_button_down_remove, LITEST_BUTTON, LITEST_ANY);
}