Add ds_seat 75/278075/1
authorSeunghun Lee <shiin.lee@samsung.com>
Wed, 20 Apr 2022 01:11:12 +0000 (10:11 +0900)
committerSooChan Lim <sc1.lim@samsung.com>
Mon, 18 Jul 2022 05:08:28 +0000 (14:08 +0900)
ds_seat abstracts wl_seat protocol of server implementation.

Change-Id: Ie2a7a14c67f0369839a4e878942c9356ff2a53a5

12 files changed:
include/libds/interfaces/keyboard.h
include/libds/keyboard.h
include/libds/seat.h [new file with mode: 0644]
src/libds/meson.build
src/libds/seat.h [new file with mode: 0644]
src/libds/seat/seat.c [new file with mode: 0644]
src/libds/seat/seat_keyboard.c [new file with mode: 0644]
src/libds/seat/seat_pointer.c [new file with mode: 0644]
src/libds/seat/seat_private.h [new file with mode: 0644]
src/libds/seat/seat_touch.c [new file with mode: 0644]
src/libds/surface.h
src/libds/surface/surface.c

index 3602fe5..85a20a5 100644 (file)
@@ -23,14 +23,6 @@ struct ds_keyboard_interface
     void (*destroy)(struct ds_keyboard *keyboard);
 };
 
-struct ds_keyboard_modifiers
-{
-    xkb_mod_mask_t depressed;
-    xkb_mod_mask_t latched;
-    xkb_mod_mask_t locked;
-    xkb_mod_mask_t group;
-};
-
 struct ds_keyboard
 {
     const struct ds_keyboard_interface *iface;
index 2a515e3..80165e6 100644 (file)
@@ -22,6 +22,14 @@ enum ds_keyboard_modifier {
     DS_MODIFIER_MOD5 = 1 << 7,
 };
 
+struct ds_keyboard_modifiers
+{
+    xkb_mod_mask_t depressed;
+    xkb_mod_mask_t latched;
+    xkb_mod_mask_t locked;
+    xkb_mod_mask_t group;
+};
+
 struct ds_event_keyboard_key
 {
     uint32_t time_msec;
diff --git a/include/libds/seat.h b/include/libds/seat.h
new file mode 100644 (file)
index 0000000..dbae0ea
--- /dev/null
@@ -0,0 +1,117 @@
+#ifndef LIBDS_SEAT_H
+#define LIBDS_SEAT_H
+
+#include <stdint.h>
+#include <wayland-server.h>
+
+#include <libds/input_device.h>
+#include <libds/keyboard.h>
+#include <libds/surface.h>
+
+struct ds_seat;
+
+enum ds_axis_orientation
+{
+    DS_AXIS_ORIENTATION_VERTICAL,
+    DS_AXIS_ORIENTATION_HORIZONTAL,
+};
+
+enum ds_axis_source
+{
+    DS_AXIS_SOURCE_WHEEL,
+    DS_AXIS_SOURCE_FINGER,
+    DS_AXIS_SOURCE_CONTINUOUS,
+    DS_AXIS_SOURCE_WHEEL_TILT,
+};
+
+struct ds_event_seat_pointer_focus_change
+{
+    struct ds_seat *seat;
+    struct ds_surface *old_surface, *new_surface;
+    double sx, sy;
+};
+
+struct ds_event_seat_keyboard_focus_change
+{
+    struct ds_seat *seat;
+    struct ds_surface *old_surface, *new_surface;
+};
+
+struct ds_seat *ds_seat_create(struct wl_display *display, const char *name);
+
+void ds_seat_destroy(struct ds_seat *seat);
+
+void ds_seat_set_capabilities(struct ds_seat *seat,
+        enum wl_seat_capability capabilities);
+
+void ds_seat_set_name(struct ds_seat *seat, const char *name);
+
+void ds_seat_add_destroy_listener(struct ds_seat *seat,
+        struct wl_listener *listener);
+
+void ds_seat_pointer_notify_enter(struct ds_seat *seat,
+        struct ds_surface *surface, double sx, double sy);
+
+void ds_seat_pointer_notify_clear_focus(struct ds_seat *seat);
+
+void ds_seat_pointer_notify_motion(struct ds_seat *seat, uint32_t time_msec,
+        double sx, double sy);
+
+uint32_t ds_seat_pointer_notify_button(struct ds_seat *seat,
+        uint32_t time_msec, uint32_t button, enum ds_button_state state);
+
+void ds_seat_pointer_notify_axis(struct ds_seat *seat, uint32_t time_msec,
+        enum ds_axis_orientation orientation, double value,
+        int32_t value_discrete, enum ds_axis_source source);
+
+void ds_seat_pointer_notify_frame(struct ds_seat *seat);
+
+void ds_seat_pointer_add_grab_begin_listener(struct ds_seat *seat,
+        struct wl_listener *listener);
+
+void ds_seat_pointer_add_grab_end_listener(struct ds_seat *seat,
+        struct wl_listener *listener);
+
+void ds_seat_pointer_add_focus_change_listener(struct ds_seat *seat,
+        struct wl_listener *listener);
+
+void ds_seat_keyboard_notify_enter(struct ds_seat *seat,
+        struct ds_surface *surface, uint32_t keycodes[], size_t num_keycodes,
+        struct ds_keyboard_modifiers *modifiers);
+
+void ds_seat_keyboard_notify_clear_focus(struct ds_seat *seat);
+
+void ds_seat_keyboard_notify_modifiers(struct ds_seat *seat,
+        struct ds_keyboard_modifiers *modifiers);
+
+void ds_seat_keyboard_notify_key(struct ds_seat *seat, uint32_t time_msec,
+        uint32_t key, uint32_t state);
+
+void ds_seat_keyboard_add_grab_begin_listener(struct ds_seat *seat,
+        struct wl_listener *listener);
+
+void ds_seat_keyboard_add_grab_end_listener(struct ds_seat *seat,
+        struct wl_listener *listener);
+
+void ds_seat_keyboard_add_focus_change_listener(struct ds_seat *seat,
+        struct wl_listener *listener);
+
+void ds_seat_touch_end_grab_start_listener(struct ds_seat *seat,
+        struct wl_listener *listener);
+
+uint32_t ds_seat_touch_notify_down(struct ds_seat *seat,
+        struct ds_surface *surface, uint32_t time_msec, int32_t touch_id,
+        double sx, double sy);
+
+void ds_seat_touch_notify_up(struct ds_seat *seat, uint32_t time_msec,
+        int32_t touch_id);
+
+void ds_seat_touch_notify_motion(struct ds_seat *seat, uint32_t time_msec,
+        int32_t touch_id, double sx, double sy);
+
+void ds_seat_touch_notify_frame(struct ds_seat *seat);
+
+void ds_seat_touch_add_grab_start_listener(struct ds_seat *seat,
+        struct wl_listener *listener);
+
+#endif
index be07132..43bdeae 100644 (file)
@@ -22,6 +22,10 @@ libds_files = [
   'pointer.c',
   'keyboard.c',
   'touch.c',
+  'seat/seat.c',
+  'seat/seat_pointer.c',
+  'seat/seat_keyboard.c',
+  'seat/seat_touch.c',
 ]
 
 protocols = {
@@ -57,6 +61,10 @@ libdrm = dependency('libdrm', required: true)
 xkbcommon = dependency('xkbcommon', required: true)
 rt = meson.get_compiler('c').find_library('rt')
 
+if wayland_server.version().version_compare('>= 1.19')
+  cdata.set('HAVE_WL_SEAT_ERROR_MISSING_CAPABILITY', '1')
+endif
+
 libds_deps = [
   math,
   wayland_server,
diff --git a/src/libds/seat.h b/src/libds/seat.h
new file mode 100644 (file)
index 0000000..6295647
--- /dev/null
@@ -0,0 +1,135 @@
+#ifndef DS_SEAT_H
+#define DS_SEAT_H
+
+#include "libds/keyboard.h"
+#include "libds/seat.h"
+
+struct ds_seat_pointer_grab;
+
+struct ds_pointer_grab_interface
+{
+    void (*enter)(struct ds_seat_pointer_grab *grab,
+            struct ds_surface *surface, double sx, double sy);
+    void (*clear_focus)(struct ds_seat_pointer_grab *grab);
+    void (*motion)(struct ds_seat_pointer_grab *grab, uint32_t time_msec,
+            double sx, double sy);
+    uint32_t (*button)(struct ds_seat_pointer_grab *grab, uint32_t time_msec,
+            uint32_t button, enum ds_button_state state);
+    void (*axis)(struct ds_seat_pointer_grab *grab, uint32_t time_msec,
+            enum ds_axis_orientation orientation, double value,
+            int32_t value_discrete, enum ds_axis_source source);
+    void (*frame)(struct ds_seat_pointer_grab *grab);
+    void (*cancel)(struct ds_seat_pointer_grab *grab);
+};
+
+struct ds_seat_keyboard_grab;
+
+struct ds_keyboard_grab_interface
+{
+    void (*enter)(struct ds_seat_keyboard_grab *grab,
+            struct ds_surface *surface, uint32_t keycodes[],
+            size_t num_keycodes, struct ds_keyboard_modifiers *modifiers);
+    void (*clear_focus)(struct ds_seat_keyboard_grab *grab);
+    void (*key)(struct ds_seat_keyboard_grab *grab, uint32_t time_msec,
+            uint32_t key, uint32_t state);
+    void (*modifiers)(struct ds_seat_keyboard_grab *grab,
+            struct ds_keyboard_modifiers *modifiers);
+    void (*cancel)(struct ds_seat_keyboard_grab *grab);
+};
+
+struct ds_touch_point;
+
+struct ds_seat_touch_grab;
+
+struct ds_touch_grab_interface
+{
+    uint32_t (*down)(struct ds_seat_touch_grab *grab, uint32_t time_msec,
+            struct ds_touch_point *point);
+    void (*up)(struct ds_seat_touch_grab *grab, uint32_t time_msec,
+            struct ds_touch_point *point);
+    void (*motion)(struct ds_seat_touch_grab *grab, uint32_t time_msec,
+            struct ds_touch_point *point);
+    void (*enter)(struct ds_seat_touch_grab *grab, uint32_t time_msec,
+            struct ds_touch_point *point);
+    void (*frame)(struct ds_seat_touch_grab *grab);
+    void (*cancel)(struct ds_seat_touch_grab *grab);
+};
+
+struct ds_seat_pointer_grab
+{
+    const struct ds_pointer_grab_interface *iface;
+    struct ds_seat *seat;
+    void *data;
+};
+
+struct ds_seat_keyboard_grab
+{
+    const struct ds_keyboard_grab_interface *iface;
+    struct ds_seat *seat;
+    void *data;
+};
+
+struct ds_seat_touch_grab
+{
+    const struct ds_touch_grab_interface *iface;
+    struct ds_seat *seat;
+    void *data;
+};
+
+void ds_seat_pointer_start_grab(struct ds_seat *seat,
+        struct ds_seat_pointer_grab *grab);
+
+void ds_seat_pointer_end_grab(struct ds_seat *seat);
+
+void ds_seat_pointer_enter(struct ds_seat *seat, struct ds_surface *surface,
+        double sx, double sy);
+
+void ds_seat_pointer_clear_focus(struct ds_seat *seat);
+
+void ds_seat_pointer_send_motion(struct ds_seat *seat, uint32_t time_msec,
+        double sx, double sy);
+
+uint32_t ds_seat_pointer_send_button(struct ds_seat *seat, uint32_t time_msec,
+        uint32_t button, enum ds_button_state state);
+
+void ds_seat_pointer_send_axis(struct ds_seat *seat, uint32_t time_msec,
+        enum ds_axis_orientation orientation, double value,
+        int32_t value_discrete, enum ds_axis_source source);
+
+void ds_seat_pointer_send_frame(struct ds_seat *seat);
+
+void ds_seat_keyboard_start_grab(struct ds_seat *seat,
+        struct ds_seat_keyboard_grab *grab);
+
+void ds_seat_keyboard_end_grab(struct ds_seat *seat);
+
+void ds_seat_keyboard_enter(struct ds_seat *seat, struct ds_surface *surface,
+        uint32_t keycodes[], size_t num_keycodes,
+        struct ds_keyboard_modifiers *modifiers);
+
+void ds_seat_keyboard_clear_focus(struct ds_seat *seat);
+
+void ds_seat_keyboard_send_key(struct ds_seat *seat, uint32_t time_msec,
+        uint32_t key, uint32_t state);
+
+void ds_seat_keyboard_send_modifiers(struct ds_seat *seat,
+        struct ds_keyboard_modifiers *modifiers);
+
+void ds_seat_touch_start_grab(struct ds_seat *seat,
+        struct ds_seat_touch_grab *grab);
+
+void ds_seat_touch_end_grab(struct ds_seat *seat);
+
+uint32_t ds_seat_touch_send_down(struct ds_seat *seat,
+        struct ds_surface *surface, uint32_t time_msec, int32_t touch_id,
+        double sx, double sy);
+
+void ds_seat_touch_send_up(struct ds_seat *seat, uint32_t time_msec,
+        int32_t touch_id);
+
+void ds_seat_touch_send_motion(struct ds_seat *seat, uint32_t time_msec,
+        int32_t touch_id, double sx, double sy);
+
+void ds_seat_touch_send_frame(struct ds_seat *seat);
+
+#endif
diff --git a/src/libds/seat/seat.c b/src/libds/seat/seat.c
new file mode 100644 (file)
index 0000000..542186e
--- /dev/null
@@ -0,0 +1,390 @@
+#include "config.h"
+
+#define _POSIX_C_SOURCE 200809L
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libds/log.h"
+#include "seat_private.h"
+
+#define SEAT_VERSION 7
+
+static void seat_handle_bind(struct wl_client *wl_client, void *data,
+        uint32_t version, uint32_t id);
+static void seat_handle_display_destroy(struct wl_listener *listener,
+        void *data);
+static void seat_destroy(struct ds_seat *seat);
+static struct ds_seat_client *seat_client_create(struct ds_seat *seat,
+        struct wl_client *wl_client);
+static void seat_client_destroy(struct ds_seat_client *seat_client);
+static void
+seat_client_send_capabilities(struct ds_seat_client *seat_client);
+static void seat_client_send_name(struct ds_seat_client *seat_client);
+
+WL_EXPORT struct ds_seat *
+ds_seat_create(struct wl_display *display, const char *name)
+{
+    struct ds_seat *seat;
+
+    seat = calloc(1, sizeof *seat);
+    if (!seat)
+        return NULL;
+
+    if (!seat_pointer_init(seat)) {
+        ds_err("Failed to initialize pointer for seat(%s)", name);
+        goto err_ptr;
+    }
+
+    if (!seat_keyboard_init(seat)) {
+        ds_err("Failed to initialize keyboard for seat(%s)", name);
+        goto err_kbd;
+    }
+
+    if (!seat_touch_init(seat)) {
+        ds_err("Failed to initialize touch for seat(%s)", name);
+        goto err_touch;
+    }
+
+    seat->global = wl_global_create(display, &wl_seat_interface,
+            SEAT_VERSION, seat, seat_handle_bind);
+    if (!seat->global) {
+        ds_err("Failed to create wl_global for seat(%s)", name);
+        goto err_global;
+    }
+
+    seat->display = display;
+    seat->name = strdup(name);
+
+    wl_list_init(&seat->clients);
+
+    wl_signal_init(&seat->events.destroy);
+    wl_signal_init(&seat->events.pointer_grab_begin);
+    wl_signal_init(&seat->events.pointer_grab_end);
+    wl_signal_init(&seat->events.keyboard_grab_begin);
+    wl_signal_init(&seat->events.keyboard_grab_end);
+    wl_signal_init(&seat->events.touch_grab_begin);
+    wl_signal_init(&seat->events.touch_grab_end);
+
+    seat->display_destroy.notify = seat_handle_display_destroy;
+    wl_display_add_destroy_listener(display, &seat->display_destroy);
+
+    return seat;
+
+err_global:
+    seat_touch_finish(seat);
+err_touch:
+    seat_keyboard_finish(seat);
+err_kbd:
+    seat_pointer_finish(seat);
+err_ptr:
+    free(seat);
+
+    return NULL;
+}
+
+WL_EXPORT void
+ds_seat_destroy(struct ds_seat *seat)
+{
+    seat_destroy(seat);
+}
+
+WL_EXPORT void
+ds_seat_set_capabilities(struct ds_seat *seat,
+        enum wl_seat_capability capabilities)
+{
+    struct ds_seat_client *seat_client;
+
+    if (capabilities == seat->capabilities)
+        return;
+
+    seat->capabilities = capabilities;
+    seat->accumulated_capabilities |= capabilities;
+
+    wl_list_for_each(seat_client, &seat->clients, link) {
+        if (!(capabilities & WL_SEAT_CAPABILITY_POINTER)) {
+            seat_client_remove_all_pointer_resources(seat_client);
+        }
+        if (!(capabilities & WL_SEAT_CAPABILITY_KEYBOARD)) {
+            seat_client_remove_all_keyboard_resources(seat_client);
+        }
+        if (!(capabilities & WL_SEAT_CAPABILITY_TOUCH)) {
+            seat_client_remove_all_touch_resources(seat_client);
+        }
+
+        seat_client_send_capabilities(seat_client);
+    }
+}
+
+WL_EXPORT void
+ds_seat_set_name(struct ds_seat *seat, const char *name)
+{
+    struct ds_seat_client *seat_client;
+
+    free(seat->name);
+    seat->name = strdup(name);
+
+    wl_list_for_each(seat_client, &seat->clients, link) {
+        seat_client_send_name(seat_client);
+    }
+}
+
+WL_EXPORT void
+ds_seat_add_destroy_listener(struct ds_seat *seat,
+        struct wl_listener *listener)
+{
+    wl_signal_add(&seat->events.destroy, listener);
+}
+
+struct ds_seat_client *
+seat_client_for_wl_client(struct ds_seat *seat, struct wl_client *wl_client)
+{
+    struct ds_seat_client *seat_client;
+
+    wl_list_for_each(seat_client, &seat->clients, link) {
+        if (seat_client->wl_client == wl_client)
+            return seat_client;
+    }
+
+    return NULL;
+}
+
+static struct ds_seat_client *
+ds_seat_client_get_or_create(struct ds_seat *seat, struct wl_client *wl_client)
+{
+    struct ds_seat_client *seat_client;
+
+    seat_client = seat_client_for_wl_client(seat, wl_client);
+    if (!seat_client) {
+        seat_client = seat_client_create(seat, wl_client);
+        if (!seat_client)
+            return NULL;
+
+        wl_list_insert(&seat->clients, &seat_client->link);
+    }
+
+    return seat_client;
+}
+
+static void
+seat_handle_get_pointer(struct wl_client *wl_client,
+        struct wl_resource *resource, uint32_t id)
+{
+    struct ds_seat_client *seat_client;
+
+    seat_client = wl_resource_get_user_data(resource);
+    if (!seat_client)
+        return;
+
+    if (!(seat_client->seat->accumulated_capabilities &
+                WL_SEAT_CAPABILITY_POINTER)) {
+#ifdef HAVE_WL_SEAT_ERROR_MISSING_CAPABILITY
+        wl_resource_post_error(resource, WL_SEAT_ERROR_MISSING_CAPABILITY,
+                "wl_seat.get_pointer called when no "
+                "pointer capability has existed");
+#endif
+        return;
+    }
+
+    seat_client_add_pointer_resource(seat_client,
+            wl_resource_get_version(resource), id);
+}
+
+static void
+seat_handle_get_keyboard(struct wl_client *wl_client,
+        struct wl_resource *resource, uint32_t id)
+{
+    struct ds_seat_client *seat_client;
+
+    seat_client = wl_resource_get_user_data(resource);
+    if (!seat_client)
+        return;
+
+    if (!(seat_client->seat->accumulated_capabilities &
+                WL_SEAT_CAPABILITY_POINTER)) {
+#ifdef HAVE_WL_SEAT_ERROR_MISSING_CAPABILITY
+        wl_resource_post_error(resource, WL_SEAT_ERROR_MISSING_CAPABILITY,
+                "wl_seat.get_pointer called when no "
+                "keyboard capability has existed");
+#endif
+        return;
+    }
+
+    seat_client_add_keyboard_resource(seat_client,
+            wl_resource_get_version(resource), id);
+}
+
+static void
+seat_handle_get_touch(struct wl_client *wl_client,
+        struct wl_resource *resource, uint32_t id)
+{
+    struct ds_seat_client *seat_client;
+
+    seat_client = wl_resource_get_user_data(resource);
+    if (!seat_client)
+        return;
+
+    if (!(seat_client->seat->accumulated_capabilities &
+                WL_SEAT_CAPABILITY_POINTER)) {
+#ifdef HAVE_WL_SEAT_ERROR_MISSING_CAPABILITY
+        wl_resource_post_error(resource, WL_SEAT_ERROR_MISSING_CAPABILITY,
+                "wl_seat.get_pointer called when no "
+                "touch capability has existed");
+#endif
+        return;
+    }
+
+    seat_client_add_touch_resource(seat_client,
+            wl_resource_get_version(resource), id);
+}
+
+static void
+seat_handle_release(struct wl_client *wl_client, struct wl_resource *resource)
+{
+    wl_resource_destroy(resource);
+}
+
+static const struct wl_seat_interface seat_impl =
+{
+    .get_pointer = seat_handle_get_pointer,
+    .get_keyboard = seat_handle_get_keyboard,
+    .get_touch = seat_handle_get_touch,
+    .release = seat_handle_release,
+};
+
+static void
+seat_client_handle_resource_destroy(struct wl_resource *resource)
+{
+    struct ds_seat_client *seat_client;
+
+    seat_client = wl_resource_get_user_data(resource);
+    if (!seat_client)
+        return;
+
+    wl_list_remove(wl_resource_get_link(resource));
+    if (!wl_list_empty(&seat_client->resources))
+        return;
+
+    seat_client_destroy(seat_client);
+}
+
+static void
+seat_handle_bind(struct wl_client *wl_client, void *data, uint32_t version,
+        uint32_t id)
+{
+    struct ds_seat *seat = data;
+    struct ds_seat_client *seat_client;
+    struct wl_resource *resource;
+
+    resource = wl_resource_create(wl_client, &wl_seat_interface, version, id);
+    if (!resource) {
+        wl_client_post_no_memory(wl_client);
+        return;
+    }
+
+    seat_client = ds_seat_client_get_or_create(seat, wl_client);
+    if (!seat_client) {
+        wl_resource_destroy(resource);
+        wl_client_post_no_memory(wl_client);
+        return;
+    }
+
+    wl_resource_set_implementation(resource, &seat_impl,
+            seat_client, seat_client_handle_resource_destroy);
+
+    wl_list_insert(&seat_client->resources, wl_resource_get_link(resource));
+
+    wl_seat_send_capabilities(resource, seat->capabilities);
+
+    if (version >= WL_SEAT_NAME_SINCE_VERSION)
+        wl_seat_send_name(resource, seat->name);
+}
+
+static void
+seat_handle_display_destroy(struct wl_listener *listener, void *data)
+{
+    struct ds_seat *seat;
+
+    seat = wl_container_of(listener, seat, display_destroy);
+    seat_destroy(seat);
+}
+
+static void
+seat_destroy(struct ds_seat *seat)
+{
+    struct ds_seat_client *seat_client, *tmp;
+    struct wl_resource *resource, *next;
+
+    wl_signal_emit(&seat->events.destroy, seat);
+
+    wl_list_remove(&seat->display_destroy.link);
+
+    wl_list_for_each_safe(seat_client, tmp, &seat->clients, link) {
+        wl_resource_for_each_safe(resource, next, &seat_client->resources) {
+            wl_list_remove(wl_resource_get_link(resource));
+            wl_resource_set_user_data(resource, NULL);
+        }
+        seat_client_destroy(seat_client);
+    }
+
+    seat_pointer_finish(seat);
+    seat_keyboard_finish(seat);
+    seat_touch_finish(seat);
+
+    wl_global_destroy(seat->global);
+    free(seat->name);
+    free(seat);
+}
+
+static struct ds_seat_client *
+seat_client_create(struct ds_seat *seat, struct wl_client *wl_client)
+{
+    struct ds_seat_client *seat_client;
+
+    seat_client = calloc(1, sizeof *seat_client);
+    seat_client->seat = seat;
+    seat_client->wl_client = wl_client;
+
+    wl_list_init(&seat_client->resources);
+    wl_list_init(&seat_client->pointers);
+    wl_list_init(&seat_client->keyboards);
+    wl_list_init(&seat_client->touches);
+
+    wl_signal_init(&seat_client->events.destroy);
+
+    return seat_client;
+}
+
+static void
+seat_client_destroy(struct ds_seat_client *seat_client)
+{
+    wl_signal_emit(&seat_client->events.destroy, seat_client);
+
+    seat_client_remove_all_pointer_resources(seat_client);
+    seat_client_remove_all_keyboard_resources(seat_client);
+    seat_client_remove_all_touch_resources(seat_client);
+
+    wl_list_remove(&seat_client->link);
+
+    free(seat_client);
+}
+
+static void
+seat_client_send_capabilities(struct ds_seat_client *seat_client)
+{
+    struct wl_resource *resource;
+
+    wl_resource_for_each(resource, &seat_client->resources) {
+        wl_seat_send_capabilities(resource, seat_client->seat->capabilities);
+    }
+}
+
+static void
+seat_client_send_name(struct ds_seat_client *seat_client)
+{
+    struct wl_resource *resource;
+
+    wl_resource_for_each(resource, &seat_client->resources) {
+        wl_seat_send_name(resource, seat_client->seat->name);
+    }
+}
diff --git a/src/libds/seat/seat_keyboard.c b/src/libds/seat/seat_keyboard.c
new file mode 100644 (file)
index 0000000..381e2d6
--- /dev/null
@@ -0,0 +1,383 @@
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include "libds/log.h"
+#include "seat_private.h"
+
+static const struct ds_keyboard_grab_interface default_keyboard_grab_iface;
+static const struct wl_keyboard_interface keyboard_impl;
+
+static void
+seat_client_send_keyboard_leave_raw(struct ds_seat_client *seat_client,
+        struct ds_surface *surface);
+static void
+seat_keyboard_handle_surface_destroy(struct wl_listener *listener,
+        void *data);
+static void keyboard_handle_resource_destroy(struct wl_resource *resource);
+
+WL_EXPORT void
+ds_seat_keyboard_notify_enter(struct ds_seat *seat,
+        struct ds_surface *surface, uint32_t keycodes[], size_t num_keycodes,
+        struct ds_keyboard_modifiers *modifiers)
+{
+    struct ds_seat_keyboard_grab *grab = seat->keyboard.grab;
+
+    grab->iface->enter(grab, surface, keycodes, num_keycodes, modifiers);
+}
+
+WL_EXPORT void
+ds_seat_keyboard_notify_clear_focus(struct ds_seat *seat)
+{
+    struct ds_seat_keyboard_grab *grab = seat->keyboard.grab;
+
+    grab->iface->clear_focus(grab);
+}
+
+WL_EXPORT void
+ds_seat_keyboard_notify_modifiers(struct ds_seat *seat,
+        struct ds_keyboard_modifiers *modifiers)
+{
+    struct ds_seat_keyboard_grab *grab = seat->keyboard.grab;
+
+    clock_gettime(CLOCK_MONOTONIC, &seat->last_event);
+    grab->iface->modifiers(grab, modifiers);
+}
+
+WL_EXPORT void
+ds_seat_keyboard_notify_key(struct ds_seat *seat, uint32_t time_msec,
+        uint32_t key, uint32_t state)
+{
+    struct ds_seat_keyboard_grab *grab = seat->keyboard.grab;
+
+    clock_gettime(CLOCK_MONOTONIC, &seat->last_event);
+    grab->iface->key(grab, time_msec, key, state);
+}
+
+WL_EXPORT void
+ds_seat_keyboard_add_grab_begin_listener(struct ds_seat *seat,
+        struct wl_listener *listener)
+{
+    wl_signal_add(&seat->events.keyboard_grab_begin, listener);
+}
+
+WL_EXPORT void
+ds_seat_keyboard_add_grab_end_listener(struct ds_seat *seat,
+        struct wl_listener *listener)
+{
+    wl_signal_add(&seat->events.keyboard_grab_end, listener);
+}
+
+WL_EXPORT void
+ds_seat_keyboard_add_focus_change_listener(struct ds_seat *seat,
+        struct wl_listener *listener)
+{
+    wl_signal_add(&seat->keyboard.events.focus_change, listener);
+}
+
+void
+ds_seat_keyboard_start_grab(struct ds_seat *seat,
+        struct ds_seat_keyboard_grab *grab)
+{
+    grab->seat = seat;
+    seat->keyboard.grab = grab;
+
+    wl_signal_emit(&seat->events.keyboard_grab_begin, grab);
+}
+
+void
+ds_seat_keyboard_end_grab(struct ds_seat *seat)
+{
+    struct ds_seat_keyboard *keyboard = &seat->keyboard;
+    struct ds_seat_keyboard_grab *grab = keyboard->grab;
+
+    if (grab != keyboard->default_grab) {
+        keyboard->grab = keyboard->default_grab;
+        wl_signal_emit(&seat->events.keyboard_grab_end, grab);
+        if (grab->iface->cancel)
+            grab->iface->cancel(grab);
+    }
+}
+
+void
+ds_seat_keyboard_enter(struct ds_seat *seat, struct ds_surface *surface,
+        uint32_t keycodes[], size_t num_keycodes,
+        struct ds_keyboard_modifiers *modifiers)
+{
+    struct ds_seat_keyboard *keyboard = &seat->keyboard;
+    struct ds_seat_client *seat_client = NULL, *focused_client;
+    struct ds_surface *focused_surface;
+    struct wl_client *wl_client;
+    struct wl_array keys;
+    struct wl_resource *resource;
+    uint32_t *p;
+    uint32_t serial;
+
+    if (keyboard->focused_surface == surface)
+        return;
+
+    if (surface) {
+        wl_client =
+            wl_resource_get_client(ds_surface_get_wl_resource(surface));
+        seat_client = seat_client_for_wl_client(seat, wl_client);
+    }
+
+    focused_client = keyboard->focused_client;
+    focused_surface = keyboard->focused_surface;
+
+    if (focused_client != NULL && focused_surface != NULL)
+        seat_client_send_keyboard_leave_raw(focused_client, focused_surface);
+
+    if (seat_client) {
+        wl_array_init(&keys);
+
+        for (size_t i = 0; i < num_keycodes; i++) {
+            p = wl_array_add(&keys, sizeof(uint32_t));
+            if (!p) {
+                ds_err("Cannot allocate memory, skipping keycode: %" PRIu32
+                        "\n", keycodes[i]);
+                continue;
+            }
+            *p = keycodes[i];
+        }
+
+        serial = wl_display_next_serial(seat->display);
+
+        wl_resource_for_each(resource, &seat_client->keyboards) {
+            wl_keyboard_send_enter(resource, serial,
+                    ds_surface_get_wl_resource(surface), &keys);
+        }
+        wl_array_release(&keys);
+    }
+
+    wl_list_remove(&keyboard->surface_destroy.link);
+    wl_list_init(&keyboard->surface_destroy.link);
+
+    if (surface) {
+        keyboard->surface_destroy.notify =
+            seat_keyboard_handle_surface_destroy;
+        ds_surface_add_destroy_listener(surface, &keyboard->surface_destroy);
+    }
+
+    keyboard->focused_client = seat_client;
+    keyboard->focused_surface = surface;
+
+    if (seat_client) {
+        ds_seat_keyboard_send_modifiers(seat, modifiers);
+
+        // TODO handle selection
+    }
+
+    struct ds_event_seat_keyboard_focus_change event = {
+        .seat = seat,
+        .old_surface = focused_surface,
+        .new_surface = surface,
+    };
+    wl_signal_emit(&keyboard->events.focus_change, &event);
+}
+
+void
+ds_seat_keyboard_clear_focus(struct ds_seat *seat)
+{
+    ds_seat_keyboard_enter(seat, NULL, NULL, 0, NULL);
+}
+
+void
+ds_seat_keyboard_send_key(struct ds_seat *seat, uint32_t time_msec,
+        uint32_t key, uint32_t state)
+{
+    struct ds_seat_client *seat_client;
+    struct wl_resource *resource;
+    uint32_t serial;
+
+    seat_client = seat->keyboard.focused_client;
+    if (!seat_client)
+        return;
+
+    serial = wl_display_next_serial(seat->display);
+    wl_resource_for_each(resource, &seat_client->keyboards)
+        wl_keyboard_send_key(resource, serial, time_msec, key, state);
+}
+
+void
+ds_seat_keyboard_send_modifiers(struct ds_seat *seat,
+        struct ds_keyboard_modifiers *modifiers)
+{
+    struct ds_seat_keyboard *keyboard = &seat->keyboard;
+    struct ds_seat_client *seat_client = keyboard->focused_client;
+    struct wl_resource *resource;
+    uint32_t serial;
+
+    if (!seat_client)
+        return;
+
+    serial = wl_display_next_serial(seat->display);
+    wl_resource_for_each(resource, &seat_client->keyboards) {
+        if (!modifiers) {
+            wl_keyboard_send_modifiers(resource, serial, 0, 0, 0, 0);
+        }
+        else {
+            wl_keyboard_send_modifiers(resource, serial,
+                    modifiers->depressed, modifiers->latched,
+                    modifiers->locked, modifiers->group);
+        }
+    }
+}
+
+bool
+seat_keyboard_init(struct ds_seat *seat)
+{
+    struct ds_seat_keyboard *keyboard = &seat->keyboard;
+    struct ds_seat_keyboard_grab *grab;
+
+    grab = calloc(1, sizeof *grab);
+    if (!grab)
+        return false;
+
+    grab->iface = &default_keyboard_grab_iface;
+    grab->seat = seat;
+
+    keyboard->default_grab = grab;
+    keyboard->grab = grab;
+    keyboard->seat = seat;
+
+    wl_list_init(&keyboard->surface_destroy.link);
+
+    wl_signal_init(&keyboard->events.focus_change);
+
+    return true;
+}
+
+void seat_keyboard_finish(struct ds_seat *seat)
+{
+    struct ds_seat_keyboard *keyboard = &seat->keyboard;
+
+    wl_list_remove(&keyboard->surface_destroy.link);
+    free(keyboard->default_grab);
+}
+
+void
+seat_client_add_keyboard_resource(struct ds_seat_client *seat_client,
+        uint32_t version, uint32_t id)
+{
+    struct wl_resource *resource;
+
+    resource = wl_resource_create(seat_client->wl_client,
+            &wl_keyboard_interface, version, id);
+    if (!resource) {
+        wl_client_post_no_memory(seat_client->wl_client);
+        return;
+    }
+
+    wl_resource_set_implementation(resource, &keyboard_impl, seat_client,
+            keyboard_handle_resource_destroy);
+
+    wl_list_insert(&seat_client->keyboards, wl_resource_get_link(resource));
+
+    if (!(seat_client->seat->capabilities & WL_SEAT_CAPABILITY_KEYBOARD)) {
+        wl_resource_set_user_data(resource, NULL);
+        return;
+    }
+}
+
+void
+seat_client_remove_all_keyboard_resources(struct ds_seat_client *seat_client)
+{
+    struct wl_resource *resource, *tmp;
+
+    wl_resource_for_each_safe(resource, tmp, &seat_client->keyboards) {
+        wl_list_remove(wl_resource_get_link(resource));
+        wl_resource_set_user_data(resource, NULL);
+    }
+}
+
+static void
+keyboard_handle_release(struct wl_client *client, struct wl_resource *resource)
+{
+    wl_resource_destroy(resource);
+}
+
+static const struct wl_keyboard_interface keyboard_impl =
+{
+    .release = keyboard_handle_release,
+};
+
+static void
+keyboard_handle_resource_destroy(struct wl_resource *resource)
+{
+    if (!wl_resource_get_user_data(resource))
+        return;
+
+    wl_list_remove(wl_resource_get_link(resource));
+}
+
+static void
+default_keyboard_grab_iface_enter(struct ds_seat_keyboard_grab *grab,
+            struct ds_surface *surface, uint32_t keycodes[],
+            size_t num_keycodes, struct ds_keyboard_modifiers *modifiers)
+{
+    ds_seat_keyboard_enter(grab->seat, surface, keycodes, num_keycodes,
+            modifiers);
+}
+
+static void
+default_keyboard_grab_iface_clear_focus(struct ds_seat_keyboard_grab *grab)
+{
+    ds_seat_keyboard_clear_focus(grab->seat);
+}
+
+static void
+default_keyboard_grab_iface_key(struct ds_seat_keyboard_grab *grab,
+        uint32_t time_msec, uint32_t key, uint32_t state)
+{
+    ds_seat_keyboard_send_key(grab->seat, time_msec, key, state);
+}
+
+static void
+default_modifiers_grab_iface_key(struct ds_seat_keyboard_grab *grab,
+        struct ds_keyboard_modifiers *modifiers)
+{
+    ds_seat_keyboard_send_modifiers(grab->seat, modifiers);
+}
+
+static void
+default_cancel_grab_iface_key(struct ds_seat_keyboard_grab *grab)
+{
+    // cannot be cancelled
+}
+
+static const struct ds_keyboard_grab_interface default_keyboard_grab_iface = {
+    .enter = default_keyboard_grab_iface_enter,
+    .clear_focus = default_keyboard_grab_iface_clear_focus,
+    .key = default_keyboard_grab_iface_key,
+    .modifiers = default_modifiers_grab_iface_key,
+    .cancel = default_cancel_grab_iface_key,
+};
+
+static void
+seat_client_send_keyboard_leave_raw(struct ds_seat_client *seat_client,
+        struct ds_surface *surface)
+{
+    struct wl_resource *resource;
+    uint32_t serial;
+
+    serial = wl_display_next_serial(seat_client->seat->display);
+    wl_resource_for_each(resource, &seat_client->keyboards) {
+        wl_keyboard_send_leave(resource, serial,
+                ds_surface_get_wl_resource(surface));
+    }
+}
+
+static void
+seat_keyboard_handle_surface_destroy(struct wl_listener *listener,
+        void *data)
+{
+    struct ds_seat_keyboard *keyboard;
+
+    keyboard = wl_container_of(listener, keyboard, surface_destroy);
+
+    wl_list_remove(&keyboard->surface_destroy.link);
+    wl_list_init(&keyboard->surface_destroy.link);
+    ds_seat_keyboard_clear_focus(keyboard->seat);
+}
diff --git a/src/libds/seat/seat_pointer.c b/src/libds/seat/seat_pointer.c
new file mode 100644 (file)
index 0000000..2616756
--- /dev/null
@@ -0,0 +1,512 @@
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include "seat_private.h"
+
+static const struct ds_pointer_grab_interface default_pointer_grab_iface;
+static const struct wl_pointer_interface pointer_impl;
+
+static void seat_pointer_warp(struct ds_seat *seat, double sx, double sy);
+static void
+seat_client_send_pointer_leave_raw(struct ds_seat_client *seat_client,
+        struct ds_surface *surface);
+static void
+seat_pointer_handle_surface_destroy(struct wl_listener *listener,
+        void *data);
+static void pointer_handle_resource_destroy(struct wl_resource *resource);
+static void pointer_send_frame(struct wl_resource *resource);
+
+WL_EXPORT void
+ds_seat_pointer_notify_enter(struct ds_seat *seat, struct ds_surface *surface,
+        double sx, double sy)
+{
+    struct ds_seat_pointer_grab *grab = seat->pointer.grab;
+
+    assert(surface);
+    grab->iface->enter(grab, surface, sx, sy);
+}
+
+WL_EXPORT void
+ds_seat_pointer_notify_clear_focus(struct ds_seat *seat)
+{
+    struct ds_seat_pointer_grab *grab = seat->pointer.grab;
+
+    grab->iface->clear_focus(grab);
+}
+
+WL_EXPORT void
+ds_seat_pointer_notify_motion(struct ds_seat *seat, uint32_t time_msec,
+        double sx, double sy)
+{
+    struct ds_seat_pointer_grab *grab = seat->pointer.grab;
+
+    grab->iface->motion(grab, time_msec, sx, sy);
+}
+
+WL_EXPORT uint32_t
+ds_seat_pointer_notify_button(struct ds_seat *seat, uint32_t time_msec,
+        uint32_t button, enum ds_button_state state)
+{
+    struct ds_seat_pointer *pointer = &seat->pointer;
+    struct ds_seat_pointer_grab *grab = pointer->grab;
+    uint32_t serial;
+
+    if (state == DS_BUTTON_PRESSED) {
+        if (pointer->button_count == 0) {
+            pointer->grab_button = button;
+            pointer->grab_time = time_msec;
+        }
+        // TODO need a set struct for assigning pointer->buttons
+    }
+    else {
+        // TODO
+    }
+
+    serial = grab->iface->button(grab, time_msec, button, state);
+    if (serial && pointer->button_count == 1 &&
+            state == DS_BUTTON_PRESSED)
+        pointer->grab_serial = serial;
+
+    return serial;
+}
+
+WL_EXPORT void
+ds_seat_pointer_notify_axis(struct ds_seat *seat, uint32_t time_msec,
+        enum ds_axis_orientation orientation, double value,
+        int32_t value_discrete, enum ds_axis_source source)
+{
+    struct ds_seat_pointer_grab *grab = seat->pointer.grab;
+
+    clock_gettime(CLOCK_MONOTONIC, &seat->last_event);
+    grab->iface->axis(grab, time_msec, orientation, value, value_discrete, source);
+}
+
+WL_EXPORT void
+ds_seat_pointer_notify_frame(struct ds_seat *seat)
+{
+    struct ds_seat_pointer_grab *grab = seat->pointer.grab;
+
+    clock_gettime(CLOCK_MONOTONIC, &seat->last_event);
+    if (grab->iface->frame)
+        grab->iface->frame(grab);
+}
+
+WL_EXPORT void
+ds_seat_pointer_add_grab_begin_listener(struct ds_seat *seat,
+        struct wl_listener *listener)
+{
+    wl_signal_add(&seat->events.pointer_grab_begin, listener);
+}
+
+WL_EXPORT void
+ds_seat_pointer_add_grab_end_listener(struct ds_seat *seat,
+        struct wl_listener *listener)
+{
+    wl_signal_add(&seat->events.pointer_grab_end, listener);
+}
+
+WL_EXPORT void
+ds_seat_pointer_add_focus_change_listener(struct ds_seat *seat,
+        struct wl_listener *listener)
+{
+    wl_signal_add(&seat->pointer.events.focus_change, listener);
+}
+
+void
+ds_seat_pointer_start_grab(struct ds_seat *seat,
+        struct ds_seat_pointer_grab *grab)
+{
+    grab->seat = seat;
+    seat->pointer.grab = grab;
+    wl_signal_emit(&seat->events.pointer_grab_begin, grab);
+}
+
+void
+ds_seat_pointer_end_grab(struct ds_seat *seat)
+{
+    struct ds_seat_pointer *pointer = &seat->pointer;
+    struct ds_seat_pointer_grab *grab = pointer->grab;
+
+    if (grab != pointer->default_grab) {
+        pointer->grab = pointer->default_grab;
+        wl_signal_emit(&seat->events.pointer_grab_end, grab);
+        if (grab->iface->cancel)
+            grab->iface->cancel(grab);
+    }
+}
+
+void
+ds_seat_pointer_enter(struct ds_seat *seat, struct ds_surface *surface,
+        double sx, double sy)
+{
+    struct ds_seat_pointer *pointer = &seat->pointer;
+    struct ds_seat_client *seat_client = NULL, *focused_client;
+    struct ds_surface *focused_surface;
+    struct wl_client *wl_client;
+    struct wl_resource *resource;
+    uint32_t serial;
+
+    if (pointer->focused_surface == surface) {
+        // this surface already got an enter notify
+        return;
+    }
+
+    focused_client = pointer->focused_client;
+    focused_surface = pointer->focused_surface;
+
+    if (focused_client != NULL && focused_surface != NULL)
+        seat_client_send_pointer_leave_raw(focused_client, focused_surface);
+
+    if (surface) {
+        wl_client =
+            wl_resource_get_client(ds_surface_get_wl_resource(surface));
+        seat_client = seat_client_for_wl_client(seat, wl_client);
+    }
+
+    if (seat_client) {
+        serial = wl_display_next_serial(seat->display);
+        wl_resource_for_each(resource, &seat_client->pointers) {
+            wl_pointer_send_enter(resource, serial,
+                    ds_surface_get_wl_resource(surface),
+                    wl_fixed_from_double(sx), wl_fixed_from_double(sy));
+            pointer_send_frame(resource);
+        }
+    }
+
+    wl_list_remove(&pointer->surface_destroy.link);
+    wl_list_init(&pointer->surface_destroy.link);
+
+    if (surface) {
+        pointer->surface_destroy.notify =
+            seat_pointer_handle_surface_destroy;
+        ds_surface_add_destroy_listener(surface, &pointer->surface_destroy);
+    }
+
+    pointer->focused_client = seat_client;
+    pointer->focused_surface = surface;
+    if (surface)
+        seat_pointer_warp(seat, sx, sy);
+    else
+        seat_pointer_warp(seat, NAN, NAN);
+
+    struct ds_event_seat_pointer_focus_change event = {
+        .seat = seat,
+        .new_surface = surface,
+        .old_surface = focused_surface,
+        .sx = sx,
+        .sy = sy,
+    };
+    wl_signal_emit(&pointer->events.focus_change, &event);
+}
+
+void
+ds_seat_pointer_clear_focus(struct ds_seat *seat)
+{
+    ds_seat_pointer_enter(seat, NULL, 0, 0);
+}
+
+void
+ds_seat_pointer_send_motion(struct ds_seat *seat, uint32_t time_msec,
+        double sx, double sy)
+{
+    struct ds_seat_pointer *pointer = &seat->pointer;
+    struct ds_seat_client *seat_client = pointer->focused_client;
+    struct wl_resource *resource;
+    wl_fixed_t sx_fixed, sy_fixed;
+
+    if (!seat_client)
+        return;
+
+    sx_fixed = wl_fixed_from_double(sx);
+    sy_fixed = wl_fixed_from_double(sy);
+    if (wl_fixed_from_double(pointer->sx) != sx_fixed ||
+            wl_fixed_from_double(pointer->sy) != sy_fixed) {
+        wl_resource_for_each(resource, &seat_client->pointers)
+            wl_pointer_send_motion(resource, time_msec, sx_fixed, sy_fixed);
+    }
+
+    seat_pointer_warp(seat, sx, sy);
+}
+
+uint32_t
+ds_seat_pointer_send_button(struct ds_seat *seat, uint32_t time_msec,
+        uint32_t button, enum ds_button_state state)
+{
+    struct ds_seat_client *seat_client = seat->pointer.focused_client;
+    struct wl_resource *resource;
+    uint32_t serial;
+
+    if (!seat_client)
+        return 0;
+
+    serial = wl_display_next_serial(seat->display);
+    wl_resource_for_each(resource, &seat_client->pointers)
+        wl_pointer_send_button(resource, serial, time_msec, button, state);
+
+    return serial;
+}
+
+void
+ds_seat_pointer_send_axis(struct ds_seat *seat, uint32_t time_msec,
+        enum ds_axis_orientation orientation, double value,
+        int32_t value_discrete, enum ds_axis_source source)
+{
+    struct ds_seat_pointer *pointer = &seat->pointer;
+    struct ds_seat_client *seat_client = pointer->focused_client;
+    struct wl_resource *resource;
+    uint32_t version;
+    bool send_source = false;
+
+    if (!seat_client)
+        return;
+
+    if (pointer->sent_axis_source) {
+        assert(pointer->cached_axis_source == source);
+    }
+    else {
+        pointer->sent_axis_source = true;
+        pointer->cached_axis_source = source;
+        send_source = true;
+    }
+
+    wl_resource_for_each(resource, &seat_client->pointers) {
+        version = wl_resource_get_version(resource);
+
+        if (send_source && version >= WL_POINTER_AXIS_SOURCE_SINCE_VERSION)
+            wl_pointer_send_axis_source(resource, source);
+
+        if (value) {
+            if (value_discrete &&
+                    version >= WL_POINTER_AXIS_DISCRETE_SINCE_VERSION) {
+                wl_pointer_send_axis_discrete(resource, orientation,
+                        value_discrete);
+            }
+
+            wl_pointer_send_axis(resource, time_msec, orientation,
+                    wl_fixed_from_double(value));
+        }
+        else if (version >= WL_POINTER_AXIS_STOP_SINCE_VERSION) {
+            wl_pointer_send_axis_stop(resource, time_msec, orientation);
+        }
+    }
+}
+
+void
+ds_seat_pointer_send_frame(struct ds_seat *seat)
+{
+    struct ds_seat_pointer *pointer = &seat->pointer;
+    struct ds_seat_client *seat_client = pointer->focused_client;
+    struct wl_resource *resource;
+
+    if (!seat_client)
+        return;
+
+    pointer->sent_axis_source = false;
+
+    wl_resource_for_each(resource, &seat_client->pointers)
+        pointer_send_frame(resource);
+}
+
+bool
+seat_pointer_init(struct ds_seat *seat)
+{
+    struct ds_seat_pointer *pointer = &seat->pointer;
+    struct ds_seat_pointer_grab *grab;
+
+    grab = calloc(1, sizeof *grab);
+    if (!grab)
+        return false;
+
+    grab->iface = &default_pointer_grab_iface;
+    grab->seat = seat;
+
+    pointer->default_grab = grab;
+    pointer->grab = grab;
+    pointer->seat = seat;
+
+    wl_list_init(&pointer->surface_destroy.link);
+
+    wl_signal_init(&pointer->events.focus_change);
+
+    return true;
+}
+
+void
+seat_pointer_finish(struct ds_seat *seat)
+{
+    struct ds_seat_pointer *pointer = &seat->pointer;
+
+    wl_list_remove(&pointer->surface_destroy.link);
+    free(pointer->default_grab);
+}
+
+void
+seat_client_add_pointer_resource(struct ds_seat_client *seat_client,
+        uint32_t version, uint32_t id)
+{
+    struct wl_resource *resource;
+
+    resource = wl_resource_create(seat_client->wl_client,
+            &wl_pointer_interface, version, id);
+    if (!resource) {
+        wl_client_post_no_memory(seat_client->wl_client);
+        return;
+    }
+
+    wl_resource_set_implementation(resource, &pointer_impl, seat_client,
+            &pointer_handle_resource_destroy);
+
+    if (!(seat_client->seat->capabilities & WL_SEAT_CAPABILITY_POINTER)) {
+        wl_resource_set_user_data(resource, NULL);
+        return;
+    }
+
+    wl_list_insert(&seat_client->pointers, wl_resource_get_link(resource));
+}
+
+void
+seat_client_remove_all_pointer_resources(struct ds_seat_client *seat_client)
+{
+    struct wl_resource *resource, *tmp;
+
+    wl_resource_for_each_safe(resource, tmp, &seat_client->pointers) {
+        wl_list_remove(wl_resource_get_link(resource));
+        wl_resource_set_user_data(resource, NULL);
+    }
+}
+
+static void
+seat_pointer_warp(struct ds_seat *seat, double sx, double sy)
+{
+    seat->pointer.sx = sx;
+    seat->pointer.sy = sy;
+}
+
+static void
+seat_client_send_pointer_leave_raw(struct ds_seat_client *seat_client,
+        struct ds_surface *surface)
+{
+    struct wl_resource *resource;
+    uint32_t serial;
+
+    serial = wl_display_next_serial(seat_client->seat->display);
+    wl_resource_for_each(resource, &seat_client->pointers) {
+        wl_pointer_send_leave(resource, serial,
+                ds_surface_get_wl_resource(surface));
+        pointer_send_frame(resource);
+    }
+}
+
+static void
+seat_pointer_handle_surface_destroy(struct wl_listener *listener, void *data)
+{
+    struct ds_seat_pointer *pointer;
+
+    pointer = wl_container_of(listener, pointer, surface_destroy);
+    wl_list_remove(&pointer->surface_destroy.link);
+    wl_list_init(&pointer->surface_destroy.link);
+    ds_seat_pointer_clear_focus(pointer->seat);
+}
+
+static void
+default_pointer_grab_iface_enter(struct ds_seat_pointer_grab *grab,
+        struct ds_surface *surface, double sx, double sy)
+{
+    ds_seat_pointer_enter(grab->seat, surface, sx, sy);
+}
+
+static void
+default_pointer_grab_iface_clear_focus(struct ds_seat_pointer_grab *grab)
+{
+    ds_seat_pointer_clear_focus(grab->seat);
+}
+
+static void
+default_pointer_grab_iface_motion(struct ds_seat_pointer_grab *grab,
+        uint32_t time_msec, double sx, double sy)
+{
+    ds_seat_pointer_send_motion(grab->seat, time_msec, sx, sy);
+}
+
+static uint32_t
+default_pointer_grab_iface_button(struct ds_seat_pointer_grab *grab,
+        uint32_t time_msec, uint32_t button, enum ds_button_state state)
+{
+    return ds_seat_pointer_send_button(grab->seat, time_msec, button, state);
+}
+
+static void
+default_pointer_grab_iface_axis(struct ds_seat_pointer_grab *grab,
+        uint32_t time_msec, enum ds_axis_orientation orientation, double value,
+        int32_t value_discrete, enum ds_axis_source source)
+{
+    ds_seat_pointer_send_axis(grab->seat, time_msec, orientation, value,
+            value_discrete, source);
+}
+
+static void
+default_pointer_grab_iface_frame(struct ds_seat_pointer_grab *grab)
+{
+    ds_seat_pointer_send_frame(grab->seat);
+}
+
+static void
+default_pointer_grab_iface_cancel(struct ds_seat_pointer_grab *grab)
+{
+    // cannot be cancelled
+}
+
+static const struct ds_pointer_grab_interface default_pointer_grab_iface = {
+    .enter = default_pointer_grab_iface_enter,
+    .clear_focus = default_pointer_grab_iface_clear_focus,
+    .motion = default_pointer_grab_iface_motion,
+    .button = default_pointer_grab_iface_button,
+    .axis = default_pointer_grab_iface_axis,
+    .frame = default_pointer_grab_iface_frame,
+    .cancel = default_pointer_grab_iface_cancel,
+};
+
+static void
+pointer_handle_set_cursor(struct wl_client *client,
+        struct wl_resource *pointer_resource, uint32_t serial,
+        struct wl_resource *surface_resource,
+        int32_t hotspot_x, int32_t hotspot_y)
+{
+    struct ds_seat_client *seat_client;
+
+    seat_client = wl_resource_get_user_data(pointer_resource);
+    if (!seat_client)
+        return;
+
+    // TODO
+}
+
+static void
+pointer_handle_release(struct wl_client *client, struct wl_resource *resource)
+{
+    wl_resource_destroy(resource);
+}
+
+static const struct wl_pointer_interface pointer_impl =
+{
+    .set_cursor = pointer_handle_set_cursor,
+    .release = pointer_handle_release,
+};
+
+static void pointer_handle_resource_destroy(struct wl_resource *resource)
+{
+    if (!wl_resource_get_user_data(resource))
+        return;
+
+    wl_list_remove(wl_resource_get_link(resource));
+}
+
+static void
+pointer_send_frame(struct wl_resource *resource)
+{
+    if (wl_resource_get_version(resource) >=
+            WL_POINTER_FRAME_SINCE_VERSION)
+        wl_pointer_send_frame(resource);
+}
diff --git a/src/libds/seat/seat_private.h b/src/libds/seat/seat_private.h
new file mode 100644 (file)
index 0000000..7a566bb
--- /dev/null
@@ -0,0 +1,172 @@
+#ifndef DS_SEAT_PRIVATE_H
+#define DS_SEAT_PRIVATE_H
+
+#include <time.h>
+#include <wayland-server.h>
+
+#include "libds/seat.h"
+#include "seat.h"
+#include "surface.h"
+
+struct ds_seat_client
+{
+    struct ds_seat *seat;
+    struct wl_client *wl_client;
+    struct wl_list link;
+
+    struct wl_list resources; // wl_seat
+    struct wl_list pointers; // wl_pointer
+    struct wl_list keyboards; // wl_keyboard
+    struct wl_list touches; // wl_touch
+
+    struct {
+        struct wl_signal destroy;
+    } events;
+
+    bool needs_touch_frame;
+};
+
+#define DS_POINTER_BUTTONS_CAP 16
+
+struct ds_seat_pointer
+{
+    struct ds_seat *seat;
+    struct ds_seat_client *focused_client;
+    struct ds_surface *focused_surface;
+    double sx, sy;
+
+    struct ds_seat_pointer_grab *grab;
+    struct ds_seat_pointer_grab *default_grab;
+
+    bool sent_axis_source;
+    enum ds_axis_source cached_axis_source;
+
+    uint32_t buttons[DS_POINTER_BUTTONS_CAP];
+    size_t button_count;
+    uint32_t grab_button;
+    uint32_t grab_serial;
+    uint32_t grab_time;
+
+    struct wl_listener surface_destroy;
+
+    struct {
+        struct wl_signal focus_change;
+    } events;
+};
+
+struct ds_seat_keyboard
+{
+    struct ds_seat *seat;
+
+    struct ds_seat_client *focused_client;
+    struct ds_surface *focused_surface;
+
+    struct wl_listener surface_destroy;
+
+    struct ds_seat_keyboard_grab *grab;
+    struct ds_seat_keyboard_grab *default_grab;
+
+    struct {
+        struct wl_signal focus_change;
+    } events;
+};
+
+struct ds_touch_point
+{
+    int32_t touch_id;
+    struct ds_surface *surface;
+    struct ds_seat_client *seat_client;
+
+    struct ds_seat_client *focused_client;
+    struct ds_surface *focused_surface;
+    double sx, sy;
+
+    struct wl_listener surface_destroy;
+    struct wl_listener focused_surface_destroy;
+    struct wl_listener client_destroy;
+
+    struct {
+        struct wl_signal destroy;
+    } events;
+
+    struct wl_list link;
+};
+
+struct ds_seat_touch
+{
+    struct ds_seat *seat;
+    struct wl_list touch_points; // ds_touch_point::link
+
+    uint32_t grab_serial;
+    uint32_t grab_id;
+
+    struct ds_seat_touch_grab *grab;
+    struct ds_seat_touch_grab *default_grab;
+};
+
+struct ds_seat
+{
+    char *name;
+    enum wl_seat_capability capabilities;
+    enum wl_seat_capability accumulated_capabilities;
+    struct timespec last_event;
+
+    struct wl_display *display;
+    struct wl_global *global;
+
+    struct wl_list clients; // ds_seat_client::link
+
+    struct ds_seat_pointer pointer;
+    struct ds_seat_keyboard keyboard;
+    struct ds_seat_touch touch;
+
+    struct wl_listener display_destroy;
+
+    struct {
+        struct wl_signal destroy;
+
+        struct wl_signal pointer_grab_begin;
+        struct wl_signal pointer_grab_end;
+
+        struct wl_signal keyboard_grab_begin;
+        struct wl_signal keyboard_grab_end;
+
+        struct wl_signal touch_grab_begin;
+        struct wl_signal touch_grab_end;
+    } events;
+};
+
+struct ds_seat_client *
+seat_client_for_wl_client(struct ds_seat *seat, struct wl_client *wl_client);
+
+bool seat_pointer_init(struct ds_seat *seat);
+
+void seat_pointer_finish(struct ds_seat *seat);
+
+void seat_client_add_pointer_resource(struct ds_seat_client *seat_client,
+        uint32_t version, uint32_t id);
+
+void
+seat_client_remove_all_pointer_resources(struct ds_seat_client *seat_client);
+
+bool seat_keyboard_init(struct ds_seat *seat);
+
+void seat_keyboard_finish(struct ds_seat *seat);
+
+void seat_client_add_keyboard_resource(struct ds_seat_client *seat_client,
+        uint32_t version, uint32_t id);
+
+void
+seat_client_remove_all_keyboard_resources(struct ds_seat_client *seat_client);
+
+bool seat_touch_init(struct ds_seat *seat);
+
+void seat_touch_finish(struct ds_seat *seat);
+
+void seat_client_add_touch_resource(struct ds_seat_client *seat_client,
+        uint32_t version, uint32_t id);
+
+void
+seat_client_remove_all_touch_resources(struct ds_seat_client *seat_client);
+
+#endif
diff --git a/src/libds/seat/seat_touch.c b/src/libds/seat/seat_touch.c
new file mode 100644 (file)
index 0000000..8a02e94
--- /dev/null
@@ -0,0 +1,432 @@
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "libds/log.h"
+#include "seat_private.h"
+
+static const struct ds_touch_grab_interface default_touch_grab_iface;
+static const struct wl_touch_interface touch_impl;
+
+static void touch_handle_resource_destroy(struct wl_resource *resource);
+static struct ds_touch_point *touch_point_create(struct ds_seat *seat,
+        int32_t touch_id, struct ds_surface *surface, double sx, double sy);
+static void touch_point_destroy(struct ds_touch_point *point);
+static void touch_point_clear_focus(struct ds_touch_point *point);
+static struct ds_touch_point *seat_find_touch_point(struct ds_seat *seat,
+        int32_t touch_id);
+static int seat_touch_num_points(struct ds_seat *seat);
+
+WL_EXPORT uint32_t
+ds_seat_touch_notify_down(struct ds_seat *seat, struct ds_surface *surface,
+        uint32_t time_msec, int32_t touch_id, double sx, double sy)
+{
+    struct ds_seat_touch_grab *grab = seat->touch.grab;
+    struct ds_touch_point *point;
+    uint32_t serial;
+
+    // FIXME
+    // What if ds_touch_point is already exist associated with given touch_id?
+    point = touch_point_create(seat, touch_id, surface, sx, sy);
+    if (!point) {
+        ds_err("Could not create touch point");
+        return 0;
+    }
+
+    serial = grab->iface->down(grab, time_msec, point);
+    if (!serial) {
+        touch_point_destroy(point);
+        return 0;
+    }
+
+    if (serial && seat_touch_num_points(seat) == 1) {
+        seat->touch.grab_serial = serial;
+        seat->touch.grab_id = touch_id;
+    }
+
+    return serial;
+}
+
+WL_EXPORT void
+ds_seat_touch_notify_up(struct ds_seat *seat, uint32_t time_msec,
+        int32_t touch_id)
+{
+    struct ds_seat_touch_grab *grab = seat->touch.grab;
+    struct ds_touch_point *point;
+
+    clock_gettime(CLOCK_MONOTONIC, &seat->last_event);
+
+    point = seat_find_touch_point(seat, touch_id);
+    if (!point)
+        return;
+
+    grab->iface->up(grab, time_msec, point);
+
+    touch_point_destroy(point);
+}
+
+WL_EXPORT void
+ds_seat_touch_notify_motion(struct ds_seat *seat, uint32_t time_msec,
+        int32_t touch_id, double sx, double sy)
+{
+    struct ds_seat_touch_grab *grab = seat->touch.grab;
+    struct ds_touch_point *point;
+
+    clock_gettime(CLOCK_MONOTONIC, &seat->last_event);
+
+    point = seat_find_touch_point(seat, touch_id);
+    if (!point)
+        return;
+
+    point->sx = sx;
+    point->sy = sy;
+
+    grab->iface->motion(grab, time_msec, point);
+}
+
+WL_EXPORT void
+ds_seat_touch_notify_frame(struct ds_seat *seat)
+{
+    struct ds_seat_touch_grab *grab = seat->touch.grab;
+
+    if (grab->iface->frame)
+        grab->iface->frame(grab);
+}
+
+WL_EXPORT void
+ds_seat_touch_add_grab_start_listener(struct ds_seat *seat,
+        struct wl_listener *listener)
+{
+    wl_signal_add(&seat->events.touch_grab_begin, listener);
+}
+
+WL_EXPORT void
+ds_seat_touch_end_grab_start_listener(struct ds_seat *seat,
+        struct wl_listener *listener)
+{
+    wl_signal_add(&seat->events.touch_grab_end, listener);
+}
+
+uint32_t
+ds_seat_touch_send_down(struct ds_seat *seat, struct ds_surface *surface,
+        uint32_t time_msec, int32_t touch_id, double sx, double sy)
+{
+    struct ds_touch_point *point;
+    struct wl_resource *resource;
+    uint32_t serial;
+
+    point = seat_find_touch_point(seat, touch_id);
+    if (!point) {
+        ds_err("Got touch down for unknown touch point");
+        return 0;
+    }
+
+    serial = wl_display_next_serial(seat->display);
+    wl_resource_for_each(resource, &point->seat_client->touches) {
+        wl_touch_send_down(resource, serial, time_msec,
+                ds_surface_get_wl_resource(surface), touch_id,
+                wl_fixed_from_double(sx), wl_fixed_from_double(sy));
+    }
+
+    point->seat_client->needs_touch_frame = true;
+
+    return serial;
+}
+
+void
+ds_seat_touch_send_up(struct ds_seat *seat, uint32_t time_msec,
+        int32_t touch_id)
+{
+    struct ds_touch_point *point;
+    struct wl_resource *resource;
+    uint32_t serial;
+
+    point = seat_find_touch_point(seat, touch_id);
+    if (!point) {
+        ds_err("Got touch up for unknown touch point");
+        return;
+    }
+
+    serial = wl_display_next_serial(seat->display);
+    wl_resource_for_each(resource, &point->seat_client->touches)
+        wl_touch_send_up(resource, serial, time_msec, touch_id);
+
+    point->seat_client->needs_touch_frame = true;
+}
+
+void
+ds_seat_touch_send_motion(struct ds_seat *seat, uint32_t time_msec,
+        int32_t touch_id, double sx, double sy)
+{
+    struct ds_touch_point *point;
+    struct wl_resource *resource;
+
+    point = seat_find_touch_point(seat, touch_id);
+    if (!point) {
+        ds_err("Got touch motion for unknown touch point");
+        return;
+    }
+
+    wl_resource_for_each(resource, &point->seat_client->touches) {
+        wl_touch_send_motion(resource, time_msec, touch_id,
+                wl_fixed_from_double(sx), wl_fixed_from_double(sy));
+    }
+
+    point->seat_client->needs_touch_frame = true;
+}
+
+void
+ds_seat_touch_send_frame(struct ds_seat *seat)
+{
+    struct ds_seat_client *seat_client;
+    struct wl_resource *resource;
+
+    wl_list_for_each(seat_client, &seat->clients, link) {
+        if (!seat_client->needs_touch_frame)
+            continue;
+
+        wl_resource_for_each(resource, &seat_client->touches)
+            wl_touch_send_frame(resource);
+
+        seat_client->needs_touch_frame = false;
+    }
+}
+
+bool
+seat_touch_init(struct ds_seat *seat)
+{
+    struct ds_seat_touch *touch = &seat->touch;
+    struct ds_seat_touch_grab *grab;
+
+    grab = calloc(1, sizeof *grab);
+    if (!grab)
+        return false;
+
+    grab->iface = &default_touch_grab_iface;
+    grab->seat = seat;
+
+    touch->default_grab = grab;
+    touch->grab = grab;
+    touch->seat = seat;
+
+    wl_list_init(&touch->touch_points);
+
+    return true;
+}
+
+void
+seat_touch_finish(struct ds_seat *seat)
+{
+    struct ds_seat_touch *touch = &seat->touch;
+    struct ds_touch_point *point;
+
+    wl_list_for_each(point, &touch->touch_points, link)
+        touch_point_clear_focus(point);
+
+    free(touch->default_grab);
+}
+
+void
+seat_client_add_touch_resource(struct ds_seat_client *seat_client,
+        uint32_t version, uint32_t id)
+{
+    struct wl_resource *resource;
+
+    resource = wl_resource_create(seat_client->wl_client,
+            &wl_keyboard_interface, version, id);
+    if (!resource) {
+        wl_client_post_no_memory(seat_client->wl_client);
+        return;
+    }
+
+    wl_resource_set_implementation(resource, &touch_impl, seat_client,
+            touch_handle_resource_destroy);
+
+    wl_list_insert(&seat_client->touches, wl_resource_get_link(resource));
+
+    if (!(seat_client->seat->capabilities & WL_SEAT_CAPABILITY_TOUCH))
+        wl_resource_set_user_data(resource, NULL);
+}
+
+void
+seat_client_remove_all_touch_resources(struct ds_seat_client *seat_client)
+{
+    struct wl_resource *resource, *tmp;
+
+    wl_resource_for_each_safe(resource, tmp, &seat_client->touches) {
+        wl_list_remove(wl_resource_get_link(resource));
+        wl_resource_set_user_data(resource, NULL);
+    }
+}
+
+static uint32_t
+default_touch_grab_iface_down(struct ds_seat_touch_grab *grab,
+        uint32_t time_msec, struct ds_touch_point *point)
+{
+    return ds_seat_touch_send_down(grab->seat, point->surface, time_msec,
+            point->touch_id, point->sx, point->sy);
+}
+
+static void
+default_touch_grab_iface_up(struct ds_seat_touch_grab *grab,
+        uint32_t time_msec, struct ds_touch_point *point)
+{
+    ds_seat_touch_send_up(grab->seat, time_msec, point->touch_id);
+}
+
+static void
+default_touch_grab_iface_motion(struct ds_seat_touch_grab *grab,
+        uint32_t time_msec, struct ds_touch_point *point)
+{
+    if (!point->focused_surface || point->focused_surface == point->surface) {
+        ds_seat_touch_send_motion(grab->seat, time_msec, point->touch_id,
+                point->sx, point->sy);
+    }
+}
+
+static void
+default_touch_grab_iface_enter(struct ds_seat_touch_grab *grab,
+        uint32_t time_msec, struct ds_touch_point *point)
+{
+    // not handled by default
+}
+
+static void
+default_touch_grab_iface_frame(struct ds_seat_touch_grab *grab)
+{
+    ds_seat_touch_send_frame(grab->seat);
+}
+
+static void
+default_touch_grab_iface_cancel(struct ds_seat_touch_grab *grab)
+{
+    // cannot be cancelled
+}
+
+static const struct ds_touch_grab_interface default_touch_grab_iface = {
+    .down = default_touch_grab_iface_down,
+    .up = default_touch_grab_iface_up,
+    .motion = default_touch_grab_iface_motion,
+    .enter = default_touch_grab_iface_enter,
+    .frame = default_touch_grab_iface_frame,
+    .cancel = default_touch_grab_iface_cancel,
+};
+
+static void
+touch_handle_release(struct wl_client *client, struct wl_resource *resource)
+{
+    wl_resource_destroy(resource);
+}
+
+static const struct wl_touch_interface touch_impl =
+{
+    .release = touch_handle_release,
+};
+
+static void
+touch_handle_resource_destroy(struct wl_resource *resource)
+{
+    if (!wl_resource_get_user_data(resource))
+        return;
+
+    wl_list_remove(wl_resource_get_link(resource));
+}
+
+static void
+touch_point_handle_surface_destroy(struct wl_listener *listener, void *data)
+{
+    struct ds_touch_point *point;
+
+    point = wl_container_of(listener, point, surface_destroy);
+    point->surface = NULL;
+    wl_list_remove(&point->surface_destroy.link);
+    wl_list_init(&point->surface_destroy.link);
+}
+
+static void
+touch_point_handle_client_destroy(struct wl_listener *listener, void *data)
+{
+    struct ds_touch_point *point;
+
+    point = wl_container_of(listener, point, surface_destroy);
+    touch_point_destroy(point);
+}
+
+static struct ds_touch_point *
+touch_point_create(struct ds_seat *seat, int32_t touch_id,
+        struct ds_surface *surface, double sx, double sy)
+{
+    struct ds_touch_point *point;
+    struct ds_seat_client *seat_client;
+    struct wl_client *wl_client;
+
+    wl_client = wl_resource_get_client(ds_surface_get_wl_resource(surface));
+    seat_client = seat_client_for_wl_client(seat, wl_client);
+    if (!seat_client || wl_list_empty(&seat_client->touches))
+        return NULL;
+
+    point = calloc(1, sizeof *point);
+    if (!point)
+        return NULL;
+
+    point->touch_id = touch_id;
+    point->surface = surface;
+    point->seat_client = seat_client;
+    point->sx = sx;
+    point->sy = sy;
+
+    wl_signal_init(&point->events.destroy);
+
+    point->surface_destroy.notify = touch_point_handle_surface_destroy;
+    ds_surface_add_destroy_listener(surface, &point->surface_destroy);
+
+    point->client_destroy.notify = touch_point_handle_client_destroy;
+    wl_signal_add(&seat_client->events.destroy, &point->client_destroy);
+
+    wl_list_insert(&seat->touch.touch_points, &point->link);
+
+    return point;
+}
+
+static void
+touch_point_destroy(struct ds_touch_point *point)
+{
+    wl_signal_emit(&point->events.destroy, point);
+
+    touch_point_clear_focus(point);
+
+    wl_list_remove(&point->surface_destroy.link);
+    wl_list_remove(&point->client_destroy.link);
+    wl_list_remove(&point->link);
+    free(point);
+}
+
+static void
+touch_point_clear_focus(struct ds_touch_point *point)
+{
+    if (!point->focused_surface)
+        return;
+
+    wl_list_remove(&point->focused_surface_destroy.link);
+    point->focused_client = NULL;
+    point->focused_surface = NULL;
+}
+
+static struct ds_touch_point *seat_find_touch_point(struct ds_seat *seat,
+        int32_t touch_id)
+{
+    struct ds_touch_point *point;
+
+    wl_list_for_each(point, &seat->touch.touch_points, link) {
+        if (point->touch_id == touch_id)
+            return point;
+    }
+
+    return NULL;
+}
+
+static int
+seat_touch_num_points(struct ds_seat *seat)
+{
+    return wl_list_length(&seat->touch.touch_points);
+}
index 8f3ceac..008fb9c 100644 (file)
@@ -46,4 +46,7 @@ ds_subsurface_create(struct wl_resource *subcomp_resource,
         struct ds_surface *surface, struct ds_surface *parent,
         uint32_t version, uint32_t id);
 
+struct wl_resource *
+ds_surface_get_wl_resource(struct ds_surface *surface);
+
 #endif
index 0333561..78dc41c 100644 (file)
@@ -220,6 +220,12 @@ ds_surface_has_buffer(struct ds_surface *surface)
     return !!surface->buffer;
 }
 
+struct wl_resource *
+ds_surface_get_wl_resource(struct ds_surface *surface)
+{
+    return surface->resource;
+}
+
 static void
 surface_handle_destroy(struct wl_client *client, struct wl_resource *resource)
 {