diff --git a/include/wlr/types/meson.build b/include/wlr/types/meson.build index 288fd1c5e..688d125c1 100644 --- a/include/wlr/types/meson.build +++ b/include/wlr/types/meson.build @@ -10,26 +10,27 @@ install_headers( 'wlr_fullscreen_shell_v1.h', 'wlr_gamma_control_v1.h', 'wlr_gtk_primary_selection.h', - 'wlr_idle_inhibit_v1.h', 'wlr_idle.h', + 'wlr_idle_inhibit_v1.h', 'wlr_input_device.h', 'wlr_input_inhibitor.h', 'wlr_input_method_v2.h', + 'wlr_input_timestamps_v1.h', 'wlr_keyboard.h', 'wlr_layer_shell_v1.h', 'wlr_linux_dmabuf_v1.h', 'wlr_list.h', 'wlr_matrix.h', + 'wlr_output.h', 'wlr_output_damage.h', 'wlr_output_layout.h', 'wlr_output_management_v1.h', - 'wlr_output.h', + 'wlr_pointer.h', 'wlr_pointer_constraints_v1.h', 'wlr_pointer_gestures_v1.h', - 'wlr_pointer.h', 'wlr_presentation_time.h', - 'wlr_primary_selection_v1.h', 'wlr_primary_selection.h', + 'wlr_primary_selection_v1.h', 'wlr_region.h', 'wlr_relative_pointer_v1.h', 'wlr_screencopy_v1.h', @@ -46,7 +47,7 @@ install_headers( 'wlr_xcursor_manager.h', 'wlr_xdg_decoration_v1.h', 'wlr_xdg_output_v1.h', - 'wlr_xdg_shell_v6.h', 'wlr_xdg_shell.h', + 'wlr_xdg_shell_v6.h', subdir: 'wlr/types', ) diff --git a/include/wlr/types/wlr_input_timestamps_v1.h b/include/wlr/types/wlr_input_timestamps_v1.h new file mode 100644 index 000000000..91e0f39d9 --- /dev/null +++ b/include/wlr/types/wlr_input_timestamps_v1.h @@ -0,0 +1,110 @@ +/* + * This an unstable interface of wlroots. No guarantees are made regarding the + * future consistency of this API. + */ +#ifndef WLR_USE_UNSTABLE +#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" +#endif + +#ifndef WLR_TYPES_WLR_INPUT_TIMESTAMPS_V1_H +#define WLR_TYPES_WLR_INPUT_TIMESTAMPS_V1_H + +#include + +/** + * This protocol specifies a way for a client to request and receive + * high-resolution timestamps for input events. + */ + +/** + * A global interface used for requesting high-resolution timestamps for + * input events. + */ +struct wlr_input_timestamps_manager_v1 { + struct wl_global *global; + struct wl_list resources; // wl_resource_get_link() + struct wl_list input_timestamps; // wlr_input_timestamps_v1::link + + struct { + struct wl_signal destroy; + } events; + + struct wl_listener display_destroy_listener; + + void *data; +}; + +/** + * Enum used to specify the type of input device to provide event timestamps + * for. + */ +enum wlr_input_timestamps_type { + WLR_INPUT_TIMESTAMPS_KEYBOARD, + WLR_INPUT_TIMESTAMPS_POINTER, + WLR_INPUT_TIMESTAMPS_TOUCH, +}; + +/** + * Provides high-resolution timestamp events for a set of subscribed input + * events. The set of subscribed input events is determined by the + * zwp_input_timestamps_manager_v1 request used to create this object. + */ +struct wlr_input_timestamps_v1 { + struct wl_resource *resource; + struct wl_resource *input_resource; + struct wlr_seat *seat; + struct wl_list link; // wlr_input_timestamps_manager_v1::input_timestamps + + enum wlr_input_timestamps_type input_timestamps_type; + + struct { + struct wl_signal destroy; + } events; + + struct wl_listener seat_destroy; + struct wl_listener input_destroy; + + void *data; +}; + +/** + * Public creator and destructor functions. + */ +struct wlr_input_timestamps_manager_v1 *wlr_input_timestamps_manager_v1_create( + struct wl_display *display); + +void wlr_input_timestamps_manager_v1_destroy( + struct wlr_input_timestamps_manager_v1 *manager); + +/** + * Public functions to send timestamps for a particular set of subscribed + * events, depending on the type of input device. + * + * The timestamp event is associated with the first subsequent input event + * carrying a timestamp which belongs to the set of input events this object is + * subscribed to. + * + * The timestamp provided by this event is a high-resolution version of the + * timestamp argument of the associated input event. The provided timestamp is + * in the same clock domain and is at least as accurate as the associated input + * event timestamp. + * + * The timestamp is expressed as tv_sec_hi, tv_sec_lo, tv_nsec triples, each + * component being an unsigned 32-bit value. Whole seconds are in tv_sec which + * is a 64-bit value combined from tv_sec_hi and tv_sec_lo, and the additional + * fractional part in tv_nsec as nanoseconds. Hence, for valid timestamps + * tv_nsec must be in [0, 999999999]. + */ +void wlr_input_timestamps_manager_v1_send_keyboard_timestamp( + struct wlr_input_timestamps_manager_v1 *manager, struct wlr_seat *seat, + uint64_t tv_sec, uint32_t tv_nsec); + +void wlr_input_timestamps_manager_v1_send_pointer_timestamp( + struct wlr_input_timestamps_manager_v1 *manager, struct wlr_seat *seat, + uint64_t tv_sec, uint32_t tv_nsec); + +void wlr_input_timestamps_manager_v1_send_touch_timestamp( + struct wlr_input_timestamps_manager_v1 *manager, struct wlr_seat *seat, + uint64_t tv_sec, uint32_t tv_nsec); + +#endif diff --git a/protocol/meson.build b/protocol/meson.build index b7a0241e5..54394a04a 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -15,6 +15,7 @@ protocols = [ [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], [wl_protocol_dir, 'unstable/fullscreen-shell/fullscreen-shell-unstable-v1.xml'], [wl_protocol_dir, 'unstable/idle-inhibit/idle-inhibit-unstable-v1.xml'], + [wl_protocol_dir, 'unstable/input-timestamps/input-timestamps-unstable-v1.xml'], [wl_protocol_dir, 'unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml'], [wl_protocol_dir, 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml'], [wl_protocol_dir, 'unstable/pointer-gestures/pointer-gestures-unstable-v1.xml'], @@ -43,6 +44,7 @@ protocols = [ client_protocols = [ [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], [wl_protocol_dir, 'unstable/idle-inhibit/idle-inhibit-unstable-v1.xml'], + [wl_protocol_dir, 'unstable/input-timestamps/input-timestamps-unstable-v1.xml'], [wl_protocol_dir, 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml'], [wl_protocol_dir, 'unstable/pointer-gestures/pointer-gestures-unstable-v1.xml'], [wl_protocol_dir, 'unstable/relative-pointer/relative-pointer-unstable-v1.xml'], diff --git a/types/meson.build b/types/meson.build index 94d378738..24d725c08 100644 --- a/types/meson.build +++ b/types/meson.build @@ -5,24 +5,14 @@ lib_wlr_types = static_library( 'data_device/wlr_data_offer.c', 'data_device/wlr_data_source.c', 'data_device/wlr_drag.c', + 'seat/wlr_seat.c', 'seat/wlr_seat_keyboard.c', 'seat/wlr_seat_pointer.c', 'seat/wlr_seat_touch.c', - 'seat/wlr_seat.c', + 'tablet_v2/wlr_tablet_v2.c', 'tablet_v2/wlr_tablet_v2_pad.c', 'tablet_v2/wlr_tablet_v2_tablet.c', 'tablet_v2/wlr_tablet_v2_tool.c', - 'tablet_v2/wlr_tablet_v2.c', - 'xdg_shell_v6/wlr_xdg_popup_v6.c', - 'xdg_shell_v6/wlr_xdg_positioner_v6.c', - 'xdg_shell_v6/wlr_xdg_shell_v6.c', - 'xdg_shell_v6/wlr_xdg_surface_v6.c', - 'xdg_shell_v6/wlr_xdg_toplevel_v6.c', - 'xdg_shell/wlr_xdg_popup.c', - 'xdg_shell/wlr_xdg_positioner.c', - 'xdg_shell/wlr_xdg_shell.c', - 'xdg_shell/wlr_xdg_surface.c', - 'xdg_shell/wlr_xdg_toplevel.c', 'wlr_box.c', 'wlr_buffer.c', 'wlr_compositor.c', @@ -33,26 +23,27 @@ lib_wlr_types = static_library( 'wlr_fullscreen_shell_v1.c', 'wlr_gamma_control_v1.c', 'wlr_gtk_primary_selection.c', - 'wlr_idle_inhibit_v1.c', 'wlr_idle.c', + 'wlr_idle_inhibit_v1.c', 'wlr_input_device.c', 'wlr_input_inhibitor.c', 'wlr_input_method_v2.c', + 'wlr_input_timestamps_v1.c', 'wlr_keyboard.c', 'wlr_layer_shell_v1.c', 'wlr_linux_dmabuf_v1.c', 'wlr_list.c', 'wlr_matrix.c', + 'wlr_output.c', 'wlr_output_damage.c', 'wlr_output_layout.c', 'wlr_output_management_v1.c', - 'wlr_output.c', + 'wlr_pointer.c', 'wlr_pointer_constraints_v1.c', 'wlr_pointer_gestures_v1.c', - 'wlr_pointer.c', 'wlr_presentation_time.c', - 'wlr_primary_selection_v1.c', 'wlr_primary_selection.c', + 'wlr_primary_selection_v1.c', 'wlr_region.c', 'wlr_relative_pointer_v1.c', 'wlr_screencopy_v1.c', @@ -67,6 +58,16 @@ lib_wlr_types = static_library( 'wlr_xcursor_manager.c', 'wlr_xdg_decoration_v1.c', 'wlr_xdg_output_v1.c', + 'xdg_shell/wlr_xdg_popup.c', + 'xdg_shell/wlr_xdg_positioner.c', + 'xdg_shell/wlr_xdg_shell.c', + 'xdg_shell/wlr_xdg_surface.c', + 'xdg_shell/wlr_xdg_toplevel.c', + 'xdg_shell_v6/wlr_xdg_popup_v6.c', + 'xdg_shell_v6/wlr_xdg_positioner_v6.c', + 'xdg_shell_v6/wlr_xdg_shell_v6.c', + 'xdg_shell_v6/wlr_xdg_surface_v6.c', + 'xdg_shell_v6/wlr_xdg_toplevel_v6.c', ), include_directories: wlr_inc, dependencies: [ diff --git a/types/wlr_input_timestamps_v1.c b/types/wlr_input_timestamps_v1.c new file mode 100644 index 000000000..c92fa6ed8 --- /dev/null +++ b/types/wlr_input_timestamps_v1.c @@ -0,0 +1,364 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "input-timestamps-unstable-v1-protocol.h" + +#define INPUT_TIMESTAMPS_MANAGER_VERSION 1 + +static const struct zwp_input_timestamps_manager_v1_interface manager_impl; +static const struct zwp_input_timestamps_v1_interface impl; + +/** + * helper functions + */ + +static struct wlr_input_timestamps_v1 * +from_resource(struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, + &zwp_input_timestamps_v1_interface, + &impl)); + return wl_resource_get_user_data(resource); +} + +static struct wlr_input_timestamps_manager_v1 * +manager_from_resource(struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, + &zwp_input_timestamps_manager_v1_interface, + &manager_impl)); + return wl_resource_get_user_data(resource); +} + +/** + * input_timestamps handler functions + */ + +static void destroy(struct wlr_input_timestamps_v1 *input_timestamps) { + wlr_signal_emit_safe(&input_timestamps->events.destroy, input_timestamps); + + wl_list_remove(&input_timestamps->link); + wl_list_remove(&input_timestamps->seat_destroy.link); + wl_list_remove(&input_timestamps->input_destroy.link); + + wl_resource_set_user_data(input_timestamps->resource, NULL); + free(input_timestamps); +} + +static void handle_resource_destroy(struct wl_resource *resource) { + struct wlr_input_timestamps_v1 *input_timestamps = + from_resource(resource); + if (input_timestamps == NULL) { + return; + } + + destroy(input_timestamps); +} + +static void handle_destroy(struct wl_client *client, struct wl_resource *resource) { + struct wlr_input_timestamps_v1 *input_timestamps = + from_resource(resource); + wlr_log(WLR_DEBUG, "input_timestamps_v1 %p released by client %p", + input_timestamps, client); + + wl_resource_destroy(resource); +} + +static void handle_seat_destroy(struct wl_listener *listener, void *data) { + struct wlr_input_timestamps_v1 *input_timestamps = + wl_container_of(listener, input_timestamps, seat_destroy); + + destroy(input_timestamps); +} + +static void handle_input_destroy(struct wl_listener *listener, void *data) { + struct wlr_input_timestamps_v1 *input_timestamps = + wl_container_of(listener, input_timestamps, input_destroy); + + destroy(input_timestamps); +} + +/** + * input_timestamps_manager handler functions + */ + +static void manager_handle_resource_destroy(struct wl_resource *resource) { + wl_list_remove(wl_resource_get_link(resource)); +} + +static void manager_handle_destroy(struct wl_client *client, struct wl_resource *resource) { + wl_resource_destroy(resource); + + wlr_log(WLR_DEBUG, + "input_timestamps_v1 manager unbound from client %p", client); +} + +static void manager_handle_get_input_timestamps(struct wl_client *client, + struct wl_resource *resource, uint32_t id, struct wl_resource *input, + enum wlr_input_timestamps_type input_timestamps_type) { + struct wlr_input_timestamps_manager_v1 *manager = + manager_from_resource(resource); + assert(manager); + + struct wlr_seat_client *seat_client = NULL; + switch (input_timestamps_type) { + case WLR_INPUT_TIMESTAMPS_KEYBOARD: + seat_client = wlr_seat_client_from_keyboard_resource(input); + break; + case WLR_INPUT_TIMESTAMPS_POINTER: + seat_client = wlr_seat_client_from_pointer_resource(input); + break; + case WLR_INPUT_TIMESTAMPS_TOUCH: + seat_client = wlr_seat_client_from_touch_resource(input); + break; + } + + struct wlr_input_timestamps_v1 *input_timestamps = + calloc(1, sizeof(struct wlr_input_timestamps_v1)); + if (input_timestamps == NULL) { + wl_client_post_no_memory(client); + return; + } + + struct wl_resource *input_timestamps_resource = wl_resource_create(client, + &zwp_input_timestamps_v1_interface, wl_resource_get_version(resource), id); + if (input_timestamps_resource == NULL) { + free(input_timestamps); + wl_client_post_no_memory(client); + return; + } + + input_timestamps->resource = input_timestamps_resource; + input_timestamps->input_resource = input; + input_timestamps->seat = seat_client->seat; + + wl_list_insert(&manager->input_timestamps, &input_timestamps->link); + + input_timestamps->input_timestamps_type = input_timestamps_type; + + wl_signal_init(&input_timestamps->events.destroy); + + wl_signal_add(&input_timestamps->seat->events.destroy, + &input_timestamps->seat_destroy); + input_timestamps->seat_destroy.notify = handle_seat_destroy; + + wl_resource_add_destroy_listener(input_timestamps->input_resource, + &input_timestamps->input_destroy); + input_timestamps->input_destroy.notify = handle_input_destroy; + + wl_resource_set_implementation(input_timestamps_resource, &impl, + input_timestamps, handle_resource_destroy); + + switch (input_timestamps_type) { + case WLR_INPUT_TIMESTAMPS_KEYBOARD: + wlr_log(WLR_DEBUG, + "input_timestamps_v1 %p created for client %p for keyboard events", + input_timestamps, client); + break; + case WLR_INPUT_TIMESTAMPS_POINTER: + wlr_log(WLR_DEBUG, + "input_timestamps_v1 %p created for client %p for pointer events", + input_timestamps, client); + break; + case WLR_INPUT_TIMESTAMPS_TOUCH: + wlr_log(WLR_DEBUG, + "input_timestamps_v1 %p created for client %p for touch events", + input_timestamps, client); + break; + } +} + +static void manager_handle_get_keyboard_timestamps(struct wl_client *client, + struct wl_resource *resource, uint32_t id, struct wl_resource *keyboard) { + manager_handle_get_input_timestamps(client, resource, id, keyboard, + WLR_INPUT_TIMESTAMPS_KEYBOARD); +} + +static void manager_handle_get_pointer_timestamps(struct wl_client *client, + struct wl_resource *resource, uint32_t id, struct wl_resource *pointer) { + manager_handle_get_input_timestamps(client, resource, id, pointer, + WLR_INPUT_TIMESTAMPS_POINTER); +} + +static void manager_handle_get_touch_timestamps(struct wl_client *client, + struct wl_resource *resource, uint32_t id, struct wl_resource *touch) { + manager_handle_get_input_timestamps(client, resource, id, touch, + WLR_INPUT_TIMESTAMPS_TOUCH); +} + +static void manager_bind(struct wl_client *wl_client, void *data, + uint32_t version, uint32_t id) { + struct wlr_input_timestamps_manager_v1 *manager = data; + + struct wl_resource *manager_resource = wl_resource_create(wl_client, + &zwp_input_timestamps_manager_v1_interface, version, id); + + if (manager_resource == NULL) { + wl_client_post_no_memory(wl_client); + return; + } + + wl_list_insert(&manager->resources, wl_resource_get_link(manager_resource)); + + wl_resource_set_implementation(manager_resource, &manager_impl, + manager, manager_handle_resource_destroy); + + wlr_log(WLR_DEBUG, "input_timestamps_v1 manager bound to client %p", + wl_client); +} + +static void manager_handle_display_destroy(struct wl_listener *listener, void *data) { + struct wlr_input_timestamps_manager_v1 *manager = + wl_container_of(listener, manager, display_destroy_listener); + wlr_input_timestamps_manager_v1_destroy(manager); +} + +/** + * Implementations + */ + +static const struct zwp_input_timestamps_manager_v1_interface manager_impl = { + .destroy = manager_handle_destroy, + .get_keyboard_timestamps = manager_handle_get_keyboard_timestamps, + .get_pointer_timestamps = manager_handle_get_pointer_timestamps, + .get_touch_timestamps = manager_handle_get_touch_timestamps, +}; + +static const struct zwp_input_timestamps_v1_interface impl = { + .destroy = handle_destroy, +}; + +/** + * Public functions + */ + +struct wlr_input_timestamps_manager_v1 *wlr_input_timestamps_manager_v1_create(struct wl_display *display) { + struct wlr_input_timestamps_manager_v1 *manager = + calloc(1, sizeof(struct wlr_input_timestamps_manager_v1)); + + if (manager == NULL) { + return NULL; + } + + wl_list_init(&manager->resources); + wl_list_init(&manager->input_timestamps); + + manager->global = wl_global_create(display, + &zwp_input_timestamps_manager_v1_interface, INPUT_TIMESTAMPS_MANAGER_VERSION, + manager, manager_bind); + + if (manager->global == NULL) { + free(manager); + return NULL; + } + + wl_signal_init(&manager->events.destroy); + + manager->display_destroy_listener.notify = manager_handle_display_destroy; + wl_display_add_destroy_listener(display, &manager->display_destroy_listener); + + wlr_log(WLR_DEBUG, "input_timestamps_v1 manager created"); + + return manager; +} + +void wlr_input_timestamps_manager_v1_destroy(struct wlr_input_timestamps_manager_v1 *manager) { + if (manager == NULL) { + return; + } + + wlr_signal_emit_safe(&manager->events.destroy, manager); + wl_list_remove(&manager->display_destroy_listener.link); + + struct wlr_input_timestamps_v1 *object, *tmp_object; + wl_list_for_each_safe(object, tmp_object, &manager->input_timestamps, link) { + wl_resource_destroy(object->resource); + } + + struct wl_resource *resource, *tmp_resource; + wl_resource_for_each_safe(resource, tmp_resource, &manager->resources) { + wl_resource_destroy(resource); + } + + wl_global_destroy(manager->global); + free(manager); +} + +/** + * Sends input event timestamps in nanosecond accuracy depending on + * input_timestamps object type. + */ +static void manager_send_timestamp(struct wlr_input_timestamps_manager_v1 *manager, + struct wlr_seat *seat, uint64_t tv_sec, uint32_t tv_nsec, + enum wlr_input_timestamps_type input_timestamps_type) { + struct wlr_seat_client *focused_client = NULL; + switch (input_timestamps_type) { + case WLR_INPUT_TIMESTAMPS_KEYBOARD: + focused_client = seat->keyboard_state.focused_client; + break; + case WLR_INPUT_TIMESTAMPS_POINTER: + focused_client = seat->pointer_state.focused_client; + break; + case WLR_INPUT_TIMESTAMPS_TOUCH: + // TODO: add support for touch input events + focused_client = NULL; + break; + } + if (focused_client == NULL) { + return; + } + + struct wlr_seat_client *seat_client = NULL; + struct wlr_input_timestamps_v1 *input_timestamps; + wl_list_for_each(input_timestamps, &manager->input_timestamps, link) { + if (input_timestamps_type != input_timestamps->input_timestamps_type) { + continue; + } + + switch (input_timestamps_type) { + case WLR_INPUT_TIMESTAMPS_KEYBOARD: + seat_client = wlr_seat_client_from_keyboard_resource( + input_timestamps->input_resource); + break; + case WLR_INPUT_TIMESTAMPS_POINTER: + seat_client = wlr_seat_client_from_pointer_resource( + input_timestamps->input_resource); + break; + case WLR_INPUT_TIMESTAMPS_TOUCH: + seat_client = wlr_seat_client_from_touch_resource( + input_timestamps->input_resource); + break; + } + if (seat != input_timestamps->seat || focused_client != seat_client) { + continue; + } + + zwp_input_timestamps_v1_send_timestamp(input_timestamps->resource, + (uint32_t)(tv_sec >> 32), (uint32_t)tv_sec, (uint32_t)tv_nsec); + } +} + +void wlr_input_timestamps_manager_v1_send_keyboard_timestamp( + struct wlr_input_timestamps_manager_v1 *manager, struct wlr_seat *seat, + uint64_t tv_sec, uint32_t tv_nsec) { + manager_send_timestamp(manager, seat, tv_sec, tv_nsec, + WLR_INPUT_TIMESTAMPS_KEYBOARD); +} + +void wlr_input_timestamps_manager_v1_send_pointer_timestamp( + struct wlr_input_timestamps_manager_v1 *manager, struct wlr_seat *seat, + uint64_t tv_sec, uint32_t tv_nsec) { + manager_send_timestamp(manager, seat, tv_sec, tv_nsec, + WLR_INPUT_TIMESTAMPS_POINTER); +} + +void wlr_input_timestamps_manager_v1_send_touch_timestamp( + struct wlr_input_timestamps_manager_v1 *manager, struct wlr_seat *seat, + uint64_t tv_sec, uint32_t tv_nsec) { + manager_send_timestamp(manager, seat, tv_sec, tv_nsec, + WLR_INPUT_TIMESTAMPS_TOUCH); +}