diff --git a/libweston/compositor.h b/libweston/compositor.h index ca1acc60..1566677f 100644 --- a/libweston/compositor.h +++ b/libweston/compositor.h @@ -605,6 +605,8 @@ struct weston_keyboard { enum weston_led leds; } xkb_state; struct xkb_keymap *pending_keymap; + + struct wl_list timestamps_list; }; struct weston_seat { diff --git a/libweston/input.c b/libweston/input.c index 2e8bd088..8028ec20 100644 --- a/libweston/input.c +++ b/libweston/input.c @@ -87,6 +87,42 @@ region_init_infinite(pixman_region32_t *region) UINT32_MAX, UINT32_MAX); } +static void +send_timestamp(struct wl_resource *resource, + const struct timespec *time) +{ + uint32_t tv_sec_hi, tv_sec_lo, tv_nsec; + + timespec_to_proto(time, &tv_sec_hi, &tv_sec_lo, &tv_nsec); + zwp_input_timestamps_v1_send_timestamp(resource, tv_sec_hi, tv_sec_lo, + tv_nsec); +} + +static void +send_timestamps_for_input_resource(struct wl_resource *input_resource, + struct wl_list *list, + const struct timespec *time) +{ + struct wl_resource *resource; + + wl_resource_for_each(resource, list) { + if (wl_resource_get_user_data(resource) == input_resource) + send_timestamp(resource, time); + } +} + +static void +remove_input_resource_from_timestamps(struct wl_resource *input_resource, + struct wl_list *list) +{ + struct wl_resource *resource; + + wl_resource_for_each(resource, list) { + if (wl_resource_get_user_data(resource) == input_resource) + wl_resource_set_user_data(resource, NULL); + } +} + static struct weston_pointer_client * weston_pointer_client_create(struct wl_client *client) { @@ -884,8 +920,12 @@ weston_keyboard_send_key(struct weston_keyboard *keyboard, resource_list = &keyboard->focus_resource_list; serial = wl_display_next_serial(display); msecs = timespec_to_msec(time); - wl_resource_for_each(resource, resource_list) + wl_resource_for_each(resource, resource_list) { + send_timestamps_for_input_resource(resource, + &keyboard->timestamps_list, + time); wl_keyboard_send_key(resource, serial, msecs, key, state); + } }; static void @@ -1157,6 +1197,7 @@ weston_keyboard_create(void) keyboard->default_grab.keyboard = keyboard; keyboard->grab = &keyboard->default_grab; wl_signal_init(&keyboard->focus_signal); + wl_list_init(&keyboard->timestamps_list); return keyboard; } @@ -1187,6 +1228,7 @@ weston_keyboard_destroy(struct weston_keyboard *keyboard) wl_array_release(&keyboard->keys); wl_list_remove(&keyboard->focus_resource_listener.link); + wl_list_remove(&keyboard->timestamps_list); free(keyboard); } @@ -2467,6 +2509,19 @@ seat_get_pointer(struct wl_client *client, struct wl_resource *resource, } } +static void +destroy_keyboard_resource(struct wl_resource *resource) +{ + struct weston_keyboard *keyboard = wl_resource_get_user_data(resource); + + wl_list_remove(wl_resource_get_link(resource)); + + if (keyboard) { + remove_input_resource_from_timestamps(resource, + &keyboard->timestamps_list); + } +} + static void keyboard_release(struct wl_client *client, struct wl_resource *resource) { @@ -2524,7 +2579,7 @@ seat_get_keyboard(struct wl_client *client, struct wl_resource *resource, wl_list_init(wl_resource_get_link(cr)); wl_resource_set_implementation(cr, &keyboard_interface, - keyboard, unbind_resource); + keyboard, destroy_keyboard_resource); /* If we don't have a keyboard_state, the resource is inert, so there * is nothing more to set up */ @@ -4570,6 +4625,18 @@ bind_pointer_constraints(struct wl_client *client, void *data, NULL, NULL); } +static void +input_timestamps_destroy(struct wl_client *client, + struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +static const struct zwp_input_timestamps_v1_interface + input_timestamps_interface = { + input_timestamps_destroy, +}; + static void input_timestamps_manager_destroy(struct wl_client *client, struct wl_resource *resource) @@ -4583,7 +4650,29 @@ input_timestamps_manager_get_keyboard_timestamps(struct wl_client *client, uint32_t id, struct wl_resource *keyboard_resource) { - wl_client_post_no_memory(client); + struct weston_keyboard *keyboard = + wl_resource_get_user_data(keyboard_resource); + struct wl_resource *input_ts; + + input_ts = wl_resource_create(client, + &zwp_input_timestamps_v1_interface, + 1, id); + if (!input_ts) { + wl_client_post_no_memory(client); + return; + } + + if (keyboard) { + wl_list_insert(&keyboard->timestamps_list, + wl_resource_get_link(input_ts)); + } else { + wl_list_init(wl_resource_get_link(input_ts)); + } + + wl_resource_set_implementation(input_ts, + &input_timestamps_interface, + keyboard_resource, + unbind_resource); } static void diff --git a/tests/keyboard-test.c b/tests/keyboard-test.c index 722bfd32..37ef83d0 100644 --- a/tests/keyboard-test.c +++ b/tests/keyboard-test.c @@ -27,11 +27,13 @@ #include +#include "input-timestamps-helper.h" #include "shared/timespec-util.h" #include "weston-test-client-helper.h" static const struct timespec t1 = { .tv_sec = 1, .tv_nsec = 1000001 }; static const struct timespec t2 = { .tv_sec = 2, .tv_nsec = 2000001 }; +static const struct timespec t_other = { .tv_sec = 123, .tv_nsec = 456 }; static struct client * create_client_with_keyboard_focus(void) @@ -97,10 +99,59 @@ TEST(keyboard_key_event_time) { struct client *client = create_client_with_keyboard_focus(); struct keyboard *keyboard = client->input->keyboard; + struct input_timestamps *input_ts = + input_timestamps_create_for_keyboard(client); send_key(client, &t1, 1, WL_KEYBOARD_KEY_STATE_PRESSED); assert(keyboard->key_time_msec == timespec_to_msec(&t1)); + assert(timespec_eq(&keyboard->key_time_timespec, &t1)); send_key(client, &t2, 1, WL_KEYBOARD_KEY_STATE_RELEASED); assert(keyboard->key_time_msec == timespec_to_msec(&t2)); + assert(timespec_eq(&keyboard->key_time_timespec, &t2)); + + input_timestamps_destroy(input_ts); +} + +TEST(keyboard_timestamps_stop_after_input_timestamps_object_is_destroyed) +{ + struct client *client = create_client_with_keyboard_focus(); + struct keyboard *keyboard = client->input->keyboard; + struct input_timestamps *input_ts = + input_timestamps_create_for_keyboard(client); + + send_key(client, &t1, 1, WL_KEYBOARD_KEY_STATE_PRESSED); + assert(keyboard->key_time_msec == timespec_to_msec(&t1)); + assert(timespec_eq(&keyboard->key_time_timespec, &t1)); + + input_timestamps_destroy(input_ts); + + send_key(client, &t2, 1, WL_KEYBOARD_KEY_STATE_RELEASED); + assert(keyboard->key_time_msec == timespec_to_msec(&t2)); + assert(timespec_is_zero(&keyboard->key_time_timespec)); +} + +TEST(keyboard_timestamps_stop_after_client_releases_wl_keyboard) +{ + struct client *client = create_client_with_keyboard_focus(); + struct keyboard *keyboard = client->input->keyboard; + struct input_timestamps *input_ts = + input_timestamps_create_for_keyboard(client); + + send_key(client, &t1, 1, WL_KEYBOARD_KEY_STATE_PRESSED); + assert(keyboard->key_time_msec == timespec_to_msec(&t1)); + assert(timespec_eq(&keyboard->key_time_timespec, &t1)); + + wl_keyboard_release(client->input->keyboard->wl_keyboard); + + /* Set input_timestamp to an arbitrary value (different from t1, t2 + * and 0) and check that it is not changed by sending the event. + * This is preferred over just checking for 0, since 0 is used + * internally for resetting the timestamp after handling an input + * event and checking for it here may lead to false negatives. */ + keyboard->input_timestamp = t_other; + send_key(client, &t2, 1, WL_KEYBOARD_KEY_STATE_RELEASED); + assert(timespec_eq(&keyboard->input_timestamp, &t_other)); + + input_timestamps_destroy(input_ts); }