diff --git a/protocol/meson.build b/protocol/meson.build index 5ab82d84..b7a6e80a 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -33,6 +33,7 @@ protocols = [ [wl_protocol_dir, 'unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml'], [wl_protocol_dir, 'unstable/xdg-shell/xdg-shell-unstable-v6.xml'], [wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'], + [wl_protocol_dir, 'unstable/tablet/tablet-unstable-v2.xml'], 'gamma-control.xml', 'gtk-primary-selection.xml', 'idle.xml', diff --git a/types/meson.build b/types/meson.build index 49f87bf8..a571dffd 100644 --- a/types/meson.build +++ b/types/meson.build @@ -43,6 +43,7 @@ lib_wlr_types = static_library( 'wlr_screenshooter.c', 'wlr_server_decoration.c', 'wlr_surface.c', + 'wlr_tablet_v2.c', 'wlr_tablet_pad.c', 'wlr_tablet_tool.c', 'wlr_touch.c', diff --git a/types/wlr_tablet_v2.c b/types/wlr_tablet_v2.c new file mode 100644 index 00000000..09e0ef35 --- /dev/null +++ b/types/wlr_tablet_v2.c @@ -0,0 +1,218 @@ +#ifndef _POSIX_C_SOURCE +#define _POSIX_C_SOURCE 200809L +#endif + +#include +#include +#include +#include +#include + +#include "tablet-unstable-v2-protocol.h" + + +struct wlr_tablet_manager_v2 { + struct wl_global *wl_global; + struct wl_list clients; // wlr_tablet_client_v2::link + + struct wl_listener display_destroy; + + void *data; +}; + +struct wlr_tablet_client_v2 { + struct wl_list link; + struct wl_client *client; + struct wl_resource *resource; + + struct wlr_tablet_manager_v2 *manager; + + struct wl_list tablet_seats; // wlr_tablet_seat_v2::link +}; + +struct wlr_tablet_seat_v2 { + struct wl_list link; + struct wl_resource *resource; + + struct wlr_tablet_client_v2 *client; + struct wlr_seat_client *seat; + + struct wl_listener seat_destroy; + + struct wl_list tools; +}; + +struct wlr_tablet_v2 { + struct wl_list link; + struct wl_resource *resource; +}; + +void wlr_tablet_v2_destroy(struct wlr_tablet_manager_v2 *manager); +static struct wlr_tablet_client_v2 *tablet_client_from_resource(struct wl_resource *resource); + +static void tablet_seat_destroy(struct wl_client *client, + struct wl_resource *resource) { + wl_resource_destroy(resource); +} + +static struct zwp_tablet_seat_v2_interface seat_impl = { + .destroy = tablet_seat_destroy, +}; + +static struct wlr_tablet_seat_v2 *tablet_seat_from_resource ( + struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, &zwp_tablet_seat_v2_interface, + &seat_impl)); + return wl_resource_get_user_data(resource); +} + +static void wlr_tablet_seat_v2_destroy(struct wl_resource *resource) { + struct wlr_tablet_seat_v2 *seat = tablet_seat_from_resource(resource); + + seat->resource = NULL; + /* We can't just destroy the struct, because we may need to iterate it + * on display->destroy/manager_destroy + */ + // TODO: Implement the free() check +} + +static void handle_seat_destroy(struct wl_listener *listener, void *data) { + struct wlr_tablet_seat_v2 *seat = + wl_container_of(listener, seat, seat_destroy); + + seat->seat = NULL; + wl_list_remove(&seat->seat_destroy.link); + /* Remove leaves it in a defunct state, we will remove again in the + * actual destroy sequence + */ + wl_list_init(&seat->seat_destroy.link); +} + +static void tablet_manager_destroy(struct wl_client *client, + struct wl_resource *resource) { + wl_resource_destroy(resource); +} + +static void get_tablet_seat(struct wl_client *wl_client, struct wl_resource *resource, + uint32_t id, struct wl_resource *seat_resource) +{ + struct wlr_tablet_client_v2 *manager = tablet_client_from_resource(resource); + struct wlr_seat_client *seat = wlr_seat_client_from_resource(seat_resource); + + struct wlr_tablet_seat_v2 *tablet_seat = + calloc(1, sizeof(struct wlr_tablet_seat_v2)); + if (tablet_seat == NULL) { + wl_client_post_no_memory(wl_client); + return; + } + + tablet_seat->resource = + wl_resource_create(wl_client, &zwp_tablet_seat_v2_interface, 1, id); + if (tablet_seat->resource == NULL) { + free(tablet_seat); + wl_client_post_no_memory(wl_client); + return; + } + + + tablet_seat->seat = seat; + tablet_seat->client = manager; + + tablet_seat->seat_destroy.notify = handle_seat_destroy; + wl_signal_add(&seat->events.destroy, &tablet_seat->seat_destroy); + + wl_resource_set_implementation(tablet_seat->resource, &seat_impl, tablet_seat, + wlr_tablet_seat_v2_destroy); + wl_list_insert(&manager->tablet_seats, &tablet_seat->link); +} + +static struct zwp_tablet_manager_v2_interface manager_impl = { + .get_tablet_seat = get_tablet_seat, + .destroy = tablet_manager_destroy, +}; + +static struct wlr_tablet_client_v2 *tablet_client_from_resource ( + struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, &zwp_tablet_manager_v2_interface, + &manager_impl)); + return wl_resource_get_user_data(resource); +} + +static void wlr_tablet_manager_v2_destroy(struct wl_resource *resource) { + struct wlr_tablet_client_v2 *client = tablet_client_from_resource(resource); + + client->resource = NULL; + /* We can't just destroy the struct, because we may need to iterate it + * on display->destroy/manager_destroy + */ + // TODO: Implement the free() check +} + +static void tablet_v2_bind(struct wl_client *wl_client, void *data, + uint32_t version, uint32_t id) { + struct wlr_tablet_manager_v2 *manager = data; + assert(wl_client && manager); + + struct wlr_tablet_client_v2 *client = + calloc(1, sizeof(struct wlr_tablet_client_v2)); + if (client == NULL) { + wl_client_post_no_memory(wl_client); + return; + } + + wl_list_init(&client->tablet_seats); + + client->resource = + wl_resource_create(wl_client, &zwp_tablet_manager_v2_interface, version, id); + if (client->resource == NULL) { + free(client); + wl_client_post_no_memory(wl_client); + return; + } + client->client = wl_client; + client->manager = manager; + + wl_resource_set_implementation(client->resource, &manager_impl, client, + wlr_tablet_manager_v2_destroy); + wl_list_insert(&manager->clients, &client->link); +} + +static void handle_display_destroy(struct wl_listener *listener, void *data) { + struct wlr_tablet_manager_v2 *tablet = + wl_container_of(listener, tablet, display_destroy); + wlr_tablet_v2_destroy(tablet); +} + +void wlr_tablet_v2_destroy(struct wlr_tablet_manager_v2 *manager) { + struct wlr_tablet_client_v2 *tmp; + struct wlr_tablet_client_v2 *pos; + + wl_list_for_each_safe(pos, tmp, &manager->clients, link) { + wl_resource_destroy(pos->resource); + } + + wl_global_destroy(manager->wl_global); + free(manager); +} + +struct wlr_tablet_manager_v2 *wlr_tablet_v2_create(struct wl_display *display) { + struct wlr_tablet_manager_v2 *tablet = + calloc(1, sizeof(struct wlr_tablet_manager_v2)); + if (!tablet) { + return NULL; + } + + wl_list_init(&tablet->clients); + + tablet->display_destroy.notify = handle_display_destroy; + wl_display_add_destroy_listener(display, &tablet->display_destroy); + + tablet->wl_global = wl_global_create(display, + &zwp_tablet_manager_v2_interface, 1, tablet, tablet_v2_bind); + if (tablet->wl_global == NULL) { + free(tablet); + return NULL; + } + + return tablet; +}