diff --git a/libweston/compositor.h b/libweston/compositor.h index 1566677f..78d2668e 100644 --- a/libweston/compositor.h +++ b/libweston/compositor.h @@ -391,6 +391,8 @@ struct weston_pointer { uint32_t button_count; struct wl_listener output_destroy_listener; + + struct wl_list timestamps_list; }; diff --git a/libweston/input.c b/libweston/input.c index 8028ec20..632c9c3c 100644 --- a/libweston/input.c +++ b/libweston/input.c @@ -226,6 +226,8 @@ unbind_pointer_client_resource(struct wl_resource *resource) pointer_client = weston_pointer_get_pointer_client(pointer, client); assert(pointer_client); + remove_input_resource_from_timestamps(resource, + &pointer->timestamps_list); weston_pointer_cleanup_pointer_client(pointer, pointer_client); } } @@ -446,8 +448,12 @@ pointer_send_motion(struct weston_pointer *pointer, resource_list = &pointer->focus_client->pointer_resources; 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, + &pointer->timestamps_list, + time); wl_pointer_send_motion(resource, msecs, sx, sy); + } } WL_EXPORT void @@ -528,8 +534,12 @@ weston_pointer_send_button(struct weston_pointer *pointer, resource_list = &pointer->focus_client->pointer_resources; 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, + &pointer->timestamps_list, + time); wl_pointer_send_button(resource, serial, msecs, button, state); + } } static void @@ -586,14 +596,21 @@ weston_pointer_send_axis(struct weston_pointer *pointer, wl_pointer_send_axis_discrete(resource, event->axis, event->discrete); - if (event->value) + if (event->value) { + send_timestamps_for_input_resource(resource, + &pointer->timestamps_list, + time); wl_pointer_send_axis(resource, msecs, event->axis, wl_fixed_from_double(event->value)); - else if (wl_resource_get_version(resource) >= - WL_POINTER_AXIS_STOP_SINCE_VERSION) + } else if (wl_resource_get_version(resource) >= + WL_POINTER_AXIS_STOP_SINCE_VERSION) { + send_timestamps_for_input_resource(resource, + &pointer->timestamps_list, + time); wl_pointer_send_axis_stop(resource, msecs, event->axis); + } } } @@ -1128,6 +1145,7 @@ weston_pointer_create(struct weston_seat *seat) wl_signal_init(&pointer->focus_signal); wl_list_init(&pointer->focus_view_listener.link); wl_signal_init(&pointer->destroy_signal); + wl_list_init(&pointer->timestamps_list); pointer->sprite_destroy_listener.notify = pointer_handle_sprite_destroy; @@ -1165,6 +1183,7 @@ weston_pointer_destroy(struct weston_pointer *pointer) wl_list_remove(&pointer->focus_resource_listener.link); wl_list_remove(&pointer->focus_view_listener.link); wl_list_remove(&pointer->output_destroy_listener.link); + wl_list_remove(&pointer->timestamps_list); free(pointer); } @@ -4681,7 +4700,29 @@ input_timestamps_manager_get_pointer_timestamps(struct wl_client *client, uint32_t id, struct wl_resource *pointer_resource) { - wl_client_post_no_memory(client); + struct weston_pointer *pointer = + wl_resource_get_user_data(pointer_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 (pointer) { + wl_list_insert(&pointer->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, + pointer_resource, + unbind_resource); } static void diff --git a/tests/pointer-test.c b/tests/pointer-test.c index 4c438a22..eef52228 100644 --- a/tests/pointer-test.c +++ b/tests/pointer-test.c @@ -28,12 +28,14 @@ #include +#include "input-timestamps-helper.h" #include "shared/timespec-util.h" #include "weston-test-client-helper.h" static const struct timespec t0 = { .tv_sec = 0, .tv_nsec = 100000000 }; 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 void send_motion(struct client *client, const struct timespec *time, int x, int y) @@ -341,11 +343,16 @@ TEST(pointer_motion_events) struct client *client = create_client_with_pointer_focus(100, 100, 100, 100); struct pointer *pointer = client->input->pointer; + struct input_timestamps *input_ts = + input_timestamps_create_for_pointer(client); send_motion(client, &t1, 150, 150); assert(pointer->x == 50); assert(pointer->y == 50); assert(pointer->motion_time_msec == timespec_to_msec(&t1)); + assert(timespec_eq(&pointer->motion_time_timespec, &t1)); + + input_timestamps_destroy(input_ts); } TEST(pointer_button_events) @@ -353,6 +360,8 @@ TEST(pointer_button_events) struct client *client = create_client_with_pointer_focus(100, 100, 100, 100); struct pointer *pointer = client->input->pointer; + struct input_timestamps *input_ts = + input_timestamps_create_for_pointer(client); assert(pointer->button == 0); assert(pointer->state == 0); @@ -361,11 +370,15 @@ TEST(pointer_button_events) assert(pointer->button == BTN_LEFT); assert(pointer->state == WL_POINTER_BUTTON_STATE_PRESSED); assert(pointer->button_time_msec == timespec_to_msec(&t1)); + assert(timespec_eq(&pointer->button_time_timespec, &t1)); send_button(client, &t2, BTN_LEFT, WL_POINTER_BUTTON_STATE_RELEASED); assert(pointer->button == BTN_LEFT); assert(pointer->state == WL_POINTER_BUTTON_STATE_RELEASED); assert(pointer->button_time_msec == timespec_to_msec(&t2)); + assert(timespec_eq(&pointer->button_time_timespec, &t2)); + + input_timestamps_destroy(input_ts); } TEST(pointer_axis_events) @@ -373,13 +386,70 @@ TEST(pointer_axis_events) struct client *client = create_client_with_pointer_focus(100, 100, 100, 100); struct pointer *pointer = client->input->pointer; + struct input_timestamps *input_ts = + input_timestamps_create_for_pointer(client); send_axis(client, &t1, 1, 1.0); assert(pointer->axis == 1); assert(pointer->axis_value == 1.0); assert(pointer->axis_time_msec == timespec_to_msec(&t1)); + assert(timespec_eq(&pointer->axis_time_timespec, &t1)); send_axis(client, &t2, 2, 0.0); assert(pointer->axis == 2); assert(pointer->axis_stop_time_msec == timespec_to_msec(&t2)); + assert(timespec_eq(&pointer->axis_stop_time_timespec, &t2)); + + input_timestamps_destroy(input_ts); +} + +TEST(pointer_timestamps_stop_after_input_timestamps_object_is_destroyed) +{ + struct client *client = create_client_with_pointer_focus(100, 100, + 100, 100); + struct pointer *pointer = client->input->pointer; + struct input_timestamps *input_ts = + input_timestamps_create_for_pointer(client); + + send_button(client, &t1, BTN_LEFT, WL_POINTER_BUTTON_STATE_PRESSED); + assert(pointer->button == BTN_LEFT); + assert(pointer->state == WL_POINTER_BUTTON_STATE_PRESSED); + assert(pointer->button_time_msec == timespec_to_msec(&t1)); + assert(timespec_eq(&pointer->button_time_timespec, &t1)); + + input_timestamps_destroy(input_ts); + + send_button(client, &t2, BTN_LEFT, WL_POINTER_BUTTON_STATE_RELEASED); + assert(pointer->button == BTN_LEFT); + assert(pointer->state == WL_POINTER_BUTTON_STATE_RELEASED); + assert(pointer->button_time_msec == timespec_to_msec(&t2)); + assert(timespec_is_zero(&pointer->button_time_timespec)); +} + +TEST(pointer_timestamps_stop_after_client_releases_wl_pointer) +{ + struct client *client = create_client_with_pointer_focus(100, 100, + 100, 100); + struct pointer *pointer = client->input->pointer; + struct input_timestamps *input_ts = + input_timestamps_create_for_pointer(client); + + send_motion(client, &t1, 150, 150); + assert(pointer->x == 50); + assert(pointer->y == 50); + assert(pointer->motion_time_msec == timespec_to_msec(&t1)); + assert(timespec_eq(&pointer->motion_time_timespec, &t1)); + + wl_pointer_release(client->input->pointer->wl_pointer); + + /* 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. */ + pointer->input_timestamp = t_other; + send_motion(client, &t2, 175, 175); + assert(timespec_eq(&pointer->input_timestamp, &t_other)); + + input_timestamps_destroy(input_ts); }