Add ds_input_device, and ds_pointer. 64/278164/1
authorSeunghun Lee <shiin.lee@samsung.com>
Thu, 7 Apr 2022 01:46:48 +0000 (10:46 +0900)
committerSooChan Lim <sc1.lim@samsung.com>
Mon, 18 Jul 2022 05:58:25 +0000 (14:58 +0900)
This patch is just the beginning of ds_input_device.

Currently, a ds_input_device is created only on the wayland backend, and
it only supports a ds_pointer.
Other devices like keyboard and touch, and other backends like libinput
will be supported in future patches.

Change-Id: I9efce1bea5fd362d5bcee86d92a81076b0be48bc

17 files changed:
include/libds/backend.h
include/libds/input_device.h [new file with mode: 0644]
include/libds/interfaces/backend.h
include/libds/interfaces/input_device.h [new file with mode: 0644]
include/libds/interfaces/pointer.h [new file with mode: 0644]
include/libds/pointer.h [new file with mode: 0644]
src/examples/meson.build
src/examples/pointer-test.c [new file with mode: 0644]
src/libds/backend.c
src/libds/backend/wayland/backend.c
src/libds/backend/wayland/backend.h
src/libds/backend/wayland/meson.build
src/libds/backend/wayland/output.c
src/libds/backend/wayland/seat.c [new file with mode: 0644]
src/libds/input_device.c [new file with mode: 0644]
src/libds/meson.build
src/libds/pointer.c [new file with mode: 0644]

index 6b587d2..d6a266f 100644 (file)
@@ -31,6 +31,10 @@ void
 ds_backend_add_new_output_listener(struct ds_backend *backend,
         struct wl_listener *listener);
 
+void
+ds_backend_add_new_input_listener(struct ds_backend *backend,
+        struct wl_listener *listener);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/libds/input_device.h b/include/libds/input_device.h
new file mode 100644 (file)
index 0000000..45e125b
--- /dev/null
@@ -0,0 +1,31 @@
+#ifndef LIBDS_INPUT_DEVICE_H
+#define LIBDS_INPUT_DEVICE_H
+
+struct ds_input_device;
+
+struct ds_pointer;
+
+enum ds_button_state
+{
+    DS_BUTTON_RELEASED,
+    DS_BUTTON_PRESSED,
+};
+
+enum ds_input_device_type
+{
+    DS_INPUT_DEVICE_POINTER,
+    DS_INPUT_DEVICE_KEYBOARD,
+    DS_INPUT_DEVICE_TOUCH,
+};
+
+enum ds_input_device_type
+ds_input_device_get_type(struct ds_input_device *dev);
+
+struct ds_pointer *
+ds_input_device_get_pointer(struct ds_input_device *dev);
+
+void
+ds_input_device_add_destroy_listener(struct ds_input_device *dev,
+        struct wl_listener *listener);
+
+#endif
index e960ac4..bad37a0 100644 (file)
@@ -22,6 +22,7 @@ struct ds_backend
     {
         struct wl_signal destroy;
         struct wl_signal new_output;
+        struct wl_signal new_input;
     } events;
 
     bool started;
diff --git a/include/libds/interfaces/input_device.h b/include/libds/interfaces/input_device.h
new file mode 100644 (file)
index 0000000..f31594b
--- /dev/null
@@ -0,0 +1,40 @@
+#ifndef LIBDS_INTERFACES_INPUT_DEVICE_H
+#define LIBDS_INTERFACES_INPUT_DEVICE_H
+
+#include <wayland-server.h>
+#include <libds/input_device.h>
+#include <libds/pointer.h>
+
+struct ds_input_device_interface
+{
+    void (*destroy)(struct ds_input_device *dev);
+};
+
+struct ds_input_device
+{
+    const struct ds_input_device_interface *iface;
+
+    char *name;
+    double width_mm, height_mm;
+    char *output_name;
+
+    enum ds_input_device_type type;
+    union {
+        void *_device;
+        struct ds_pointer *pointer;
+    };
+
+    struct {
+        struct wl_signal destroy;
+    } events;
+
+    struct wl_list link;
+};
+
+void ds_input_device_init(struct ds_input_device *dev,
+        enum ds_input_device_type type,
+        const struct ds_input_device_interface *iface,
+        const char *name, int vendor, int product);
+void ds_input_device_destroy(struct ds_input_device *dev);
+
+#endif
diff --git a/include/libds/interfaces/pointer.h b/include/libds/interfaces/pointer.h
new file mode 100644 (file)
index 0000000..84b0e23
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef LIBDS_INTERFACES_POINTER_H
+#define LIBDS_INTERFACES_POINTER_H
+
+#include <wayland-server.h>
+
+struct ds_pointer;
+
+struct ds_pointer_interface
+{
+    void (*destroy)(struct ds_pointer *pointer);
+};
+
+struct ds_pointer
+{
+    const struct ds_pointer_interface *iface;
+
+    struct {
+        struct wl_signal motion;
+        struct wl_signal motion_absolute;
+        struct wl_signal button;
+        struct wl_signal frame;
+    } events;
+};
+
+void ds_pointer_init(struct ds_pointer *pointer,
+        const struct ds_pointer_interface *iface);
+
+void ds_pointer_destroy(struct ds_pointer *pointer);
+
+#endif
diff --git a/include/libds/pointer.h b/include/libds/pointer.h
new file mode 100644 (file)
index 0000000..30bfaca
--- /dev/null
@@ -0,0 +1,38 @@
+#ifndef LIBDS_POINTER_H
+#define LIBDS_POINTER_H
+
+#include <stdint.h>
+#include <wayland-server.h>
+#include <libds/input_device.h>
+
+struct ds_pointer;
+
+struct ds_event_pointer_motion_absolute
+{
+    struct ds_input_device *device;
+    uint32_t time_msec;
+    // From 0..1
+    double x, y;
+};
+
+struct ds_event_pointer_button
+{
+    struct ds_input_device *device;
+    uint32_t time_msec;
+    uint32_t button;
+    enum ds_button_state state;
+};
+
+void ds_pointer_add_motion_listener(struct ds_pointer *pointer,
+        struct wl_listener *listener);
+
+void ds_pointer_add_motion_absolute_listener(struct ds_pointer *pointer,
+        struct wl_listener *listener);
+
+void ds_pointer_add_button_listener(struct ds_pointer *pointer,
+        struct wl_listener *listener);
+
+void ds_pointer_add_frame_listener(struct ds_pointer *pointer,
+        struct wl_listener *listener);
+
+#endif
index ddb0792..f19f139 100644 (file)
@@ -23,6 +23,15 @@ executable('tinyds',
   install : true
 )
 
+executable('pointer-test',
+  [
+    'pointer-test.c',
+    'pixman-helper.c',
+  ],
+  dependencies: common_deps,
+  install_dir: libds_bindir,
+  install : true)
+
 if get_option('tizen')
   common_deps += dep_libds_tizen
 
diff --git a/src/examples/pointer-test.c b/src/examples/pointer-test.c
new file mode 100644 (file)
index 0000000..d669a03
--- /dev/null
@@ -0,0 +1,278 @@
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <drm_fourcc.h>
+#include <libds/log.h>
+#include <libds/backend/wayland.h>
+#include <libds/input_device.h>
+#include <libds/pointer.h>
+#include <libds/allocator/shm.h>
+#include <libds/swapchain.h>
+
+#include "pixman-helper.h"
+
+#define WIDTH 700
+#define HEIGHT 400
+
+struct pointer_device {
+    struct ds_pointer *ds_pointer;
+
+    struct wl_listener destroy;
+    struct wl_listener motion_absolute;
+    struct wl_listener button;
+    struct wl_listener frame;
+};
+
+struct output
+{
+    struct server *server;
+
+    struct ds_output *ds_output;
+    struct ds_allocator *allocator;
+    struct ds_swapchain *swapchain;
+
+    struct ds_buffer *front_buffer;
+
+    struct wl_listener destroy;
+};
+
+struct server
+{
+    struct wl_display *display;
+
+    struct output output;
+
+    struct ds_backend *backend;
+
+    struct wl_listener backend_destroy;
+    struct wl_listener new_input;
+};
+
+struct server _server;
+
+static struct ds_backend *create_backend_auto(struct wl_display *display);
+static void handle_backend_destroy(struct wl_listener *listener, void *data);
+static void handle_new_input(struct wl_listener *listener, void *data);
+static void output_init(struct output *output, struct server *sever);
+static void output_draw(struct output *output);
+
+int
+main(void)
+{
+    struct server *server = &_server;
+
+    ds_log_init(DS_DBG, NULL);
+
+    server->display = wl_display_create();
+    assert(server->display);
+
+    server->backend = create_backend_auto(server->display);
+    assert(server->backend);
+
+    server->backend_destroy.notify = handle_backend_destroy;
+    ds_backend_add_destroy_listener(server->backend, &server->backend_destroy);
+
+    server->new_input.notify = handle_new_input;
+    ds_backend_add_new_input_listener(server->backend, &server->new_input);
+
+    output_init(&server->output, server);
+
+    ds_backend_start(server->backend);
+
+    output_draw(&server->output);
+
+    wl_display_run(server->display);
+
+    wl_display_destroy(server->display);
+
+    return 0;
+}
+
+static struct ds_backend *
+create_backend_auto(struct wl_display *display)
+{
+    struct ds_backend *backend = NULL;
+    char name[512];
+    int i;
+
+    for (i = 0; i < 5; i++) {
+        snprintf(name, sizeof name, "wayland-%d", i);
+        backend = ds_wl_backend_create(display, name);
+        if (backend)
+            break;
+    }
+
+    return backend;
+}
+
+static void
+handle_backend_destroy(struct wl_listener *listener, void *data)
+{
+    struct server *server;
+
+    server = wl_container_of(listener, server, backend_destroy);
+
+    wl_list_remove(&server->backend_destroy.link);
+    wl_list_remove(&server->new_input.link);
+}
+
+static char *
+device_type_to_string(enum ds_input_device_type type)
+{
+    switch (type) {
+        case DS_INPUT_DEVICE_POINTER:
+            return "pointer";
+            break;
+        case DS_INPUT_DEVICE_KEYBOARD:
+            return "keyboard";
+            break;
+        case DS_INPUT_DEVICE_TOUCH:
+            return "touch";
+            break;
+        default:
+            return "Unknown";
+    }
+}
+
+static void
+pointer_handle_device_destroy(struct wl_listener *listener, void *data)
+{
+    struct pointer_device *pointer;
+
+    pointer = wl_container_of(listener, pointer, destroy);
+
+    wl_list_remove(&pointer->destroy.link);
+    wl_list_remove(&pointer->motion_absolute.link);
+    wl_list_remove(&pointer->button.link);
+    wl_list_remove(&pointer->frame.link);
+
+    free(pointer);
+}
+
+static void
+pointer_handle_motion_absolute(struct wl_listener *listener, void *data)
+{
+    struct ds_event_pointer_motion_absolute *event = data;
+
+    ds_inf("Pointer device(%p): motion absolute (%f, %f) time(%d ms)",
+            event->device, event->x, event->y, event->time_msec);
+}
+
+static void
+pointer_handle_button(struct wl_listener *listener, void *data)
+{
+    struct ds_event_pointer_button *event = data;
+
+    ds_inf("Pointer Device(%p): button(%d) state(%d) time(%d ms)",
+            event->device, event->button, event->state, event->time_msec);
+}
+
+static void
+pointer_handle_frame(struct wl_listener *listener, void *data)
+{
+    ds_inf("Pointer device(%p): frame", data);
+}
+
+static void
+add_pointer(struct ds_input_device *dev)
+{
+    struct pointer_device *pointer;
+
+    pointer = calloc(1, sizeof *pointer);
+    if (!pointer)
+        return;
+
+    pointer->ds_pointer = ds_input_device_get_pointer(dev);
+
+    pointer->destroy.notify = pointer_handle_device_destroy;
+    ds_input_device_add_destroy_listener(dev, &pointer->destroy);
+
+    pointer->motion_absolute.notify = pointer_handle_motion_absolute;
+    ds_pointer_add_motion_absolute_listener(pointer->ds_pointer,
+            &pointer->motion_absolute);
+
+    pointer->button.notify = pointer_handle_button;
+    ds_pointer_add_button_listener(pointer->ds_pointer,
+            &pointer->button);
+
+    pointer->frame.notify = pointer_handle_frame;
+    ds_pointer_add_frame_listener(pointer->ds_pointer,
+            &pointer->frame);
+}
+
+static void
+handle_new_input(struct wl_listener *listener, void *data)
+{
+    struct ds_input_device *dev = data;
+    enum ds_input_device_type type;
+
+    type = ds_input_device_get_type(dev);
+
+    if (type != DS_INPUT_DEVICE_POINTER)
+        return;
+
+    ds_inf("New pointer device(%p) type(%s)", dev,
+            device_type_to_string(type));
+
+    add_pointer(dev);
+}
+
+static void
+output_handle_destroy(struct wl_listener *listener, void *data)
+{
+    struct output *output;
+
+    output = wl_container_of(listener, output, destroy);
+
+    wl_list_remove(&output->destroy.link);
+
+    ds_swapchain_destroy(output->swapchain);
+    ds_allocator_destroy(output->allocator);
+}
+
+static void
+output_init(struct output *output, struct server *server)
+{
+    output->server = server;
+
+    output->ds_output = ds_wl_backend_create_output(server->backend);
+    assert(output->ds_output);
+
+    output->destroy.notify = output_handle_destroy;
+    ds_output_add_destroy_listener(output->ds_output, &output->destroy);
+
+    output->allocator = ds_shm_allocator_create();
+    assert(output->allocator);
+
+    output->swapchain = ds_swapchain_create(output->allocator,
+            WIDTH, HEIGHT, DRM_FORMAT_XRGB8888);
+    assert(output->swapchain);
+}
+
+static void
+output_draw(struct output *output)
+{
+    struct ds_buffer *buffer;
+    pixman_image_t *img;
+
+    ds_dbg("Redraw output");
+
+    buffer = ds_swapchain_acquire(output->swapchain, NULL);
+    assert(buffer);
+
+    img = pixman_image_from_buffer(buffer, DS_BUFFER_DATA_PTR_ACCESS_WRITE);
+    assert(img);
+
+    pixman_image_fill_color(img, 80, 80, 80);
+
+    pixman_image_unref(img);
+
+    ds_output_attach_buffer(output->ds_output, buffer);
+    ds_output_commit(output->ds_output);
+
+    if (output->front_buffer)
+        ds_buffer_unlock(output->front_buffer);
+
+    output->front_buffer = buffer;
+}
index ee2a912..641374a 100644 (file)
@@ -41,6 +41,13 @@ ds_backend_add_new_output_listener(struct ds_backend *backend,
     wl_signal_add(&backend->events.new_output, listener);
 }
 
+WL_EXPORT void
+ds_backend_add_new_input_listener(struct ds_backend *backend,
+        struct wl_listener *listener)
+{
+    wl_signal_add(&backend->events.new_input, listener);
+}
+
 void
 ds_backend_init(struct ds_backend *backend,
         const struct ds_backend_interface *iface)
@@ -48,6 +55,7 @@ ds_backend_init(struct ds_backend *backend,
     backend->iface = iface;
     wl_signal_init(&backend->events.destroy);
     wl_signal_init(&backend->events.new_output);
+    wl_signal_init(&backend->events.new_input);
 }
 
 void
index 778ca1c..d956aa0 100644 (file)
@@ -14,8 +14,8 @@
 static const struct ds_backend_interface wl_backend_interface;
 static void wl_backend_handle_display_destroy(struct wl_listener *listener,
         void *data);
-static bool wl_backend_server_init(struct ds_wl_backend_server *server,
-        const char *name);
+static bool wl_backend_server_init(struct ds_wl_backend *wl_backend,
+        struct ds_wl_backend_server *server, const char *name);
 static void wl_backend_server_finish(struct ds_wl_backend_server *server);
 static int wl_backend_handle_dispatch_events(int fd, uint32_t mask,
         void *data);
@@ -38,8 +38,9 @@ ds_wl_backend_create(struct wl_display *display, const char *server_name)
     wl_backend->display = display;
     wl_list_init(&wl_backend->buffers);
     wl_list_init(&wl_backend->outputs);
+    wl_list_init(&wl_backend->seats);
 
-    if (!wl_backend_server_init(&wl_backend->server, server_name)) {
+    if (!wl_backend_server_init(wl_backend, &wl_backend->server, server_name)) {
         ds_err("Failed to initialize Wayland Server");
         goto err_server;
     }
@@ -48,7 +49,7 @@ ds_wl_backend_create(struct wl_display *display, const char *server_name)
     fd = wl_display_get_fd(wl_backend->server.display);
 
     wl_backend->server_event_source =
-        wl_event_loop_add_fd(loop, fd, WL_EVENT_READABLE,
+        wl_event_loop_add_fd(loop, fd, WL_EVENT_WRITABLE | WL_EVENT_READABLE,
                 wl_backend_handle_dispatch_events, wl_backend);
     if (!wl_backend->server_event_source) {
         ds_err("Failed to create event source");
@@ -84,6 +85,7 @@ wl_backend_destroy(struct ds_wl_backend *backend)
 {
     struct ds_wl_output *output, *tmp_output;
     struct ds_wl_buffer *buffer, *tmp_buffer;
+    struct ds_wl_seat *seat, *tmp_seat;
 
     ds_dbg("Destroy wayland backend(%p)", backend);
 
@@ -93,6 +95,9 @@ wl_backend_destroy(struct ds_wl_backend *backend)
     wl_list_for_each_safe(buffer, tmp_buffer, &backend->buffers, link)
         destroy_wl_buffer(buffer);
 
+    wl_list_for_each_safe(seat, tmp_seat, &backend->seats, link)
+        destroy_wl_seat(seat);
+
     ds_backend_finish(&backend->base);
 
     wl_list_remove(&backend->display_destroy.link);
@@ -159,6 +164,7 @@ registry_handle_global(void *data, struct wl_registry *registry,
         uint32_t name, const char *iface, uint32_t version)
 {
     struct ds_wl_backend_server *server = data;
+    struct ds_wl_seat *seat;
 
     ds_log(DS_DBG, "Wayland global: %s v%d", iface, version);
 
@@ -176,6 +182,15 @@ registry_handle_global(void *data, struct wl_registry *registry,
         server->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
         wl_shm_add_listener(server->shm, &shm_listener, server);
     }
+    else if (strcmp(iface, wl_seat_interface.name) == 0) {
+        seat = create_wl_seat(server->backend, name, version);
+        if (!seat) {
+            ds_err("Could not create ds_wl_seat");
+            return;
+        }
+
+        wl_list_insert(&server->backend->seats, &seat->link);
+    }
 }
 
 static void
@@ -192,8 +207,10 @@ static const struct wl_registry_listener registry_listener =
 };
 
 static bool
-wl_backend_server_init(struct ds_wl_backend_server *server, const char *name)
+wl_backend_server_init(struct ds_wl_backend *wl_backend, struct ds_wl_backend_server *server, const char *name)
 {
+    server->backend = wl_backend;
+
     server->display = wl_display_connect(name);
     if (!server->display) {
         ds_log_errno(DS_ERR, "Could not connect to display: name \"%s\"", name);
index ab37ec0..12e594a 100644 (file)
@@ -3,9 +3,12 @@
 
 #include "libds/interfaces/backend.h"
 #include "libds/interfaces/output.h"
+#include "libds/interfaces/input_device.h"
+#include "libds/interfaces/pointer.h"
 
 struct ds_wl_backend_server
 {
+    struct ds_wl_backend *backend;
     struct wl_display *display;
     struct wl_registry *registry;
     struct wl_compositor *compositor;
@@ -23,6 +26,7 @@ struct ds_wl_backend
 
     struct wl_list outputs; // ds_wl_output.link
     struct wl_list buffers; // ds_wl_buffer.link
+    struct wl_list seats; // ds_wl_seat.link
 
     struct wl_event_source *server_event_source;
     struct ds_wl_backend_server server;
@@ -50,6 +54,53 @@ struct ds_wl_output
     struct wl_callback *frame_callback;
 
     struct wl_list link;
+
+    struct {
+        struct ds_wl_pointer *pointer;
+        struct wl_surface *surface;
+        int32_t hotspot_x, hotspot_y;
+        uint32_t enter_serial;
+    } cursor;
+};
+
+struct ds_wl_seat
+{
+    struct ds_wl_backend *backend;
+    struct ds_wl_output *output;
+
+    struct wl_seat *wl_seat;
+    char *name;
+
+    struct ds_input_device *pointer_dev;
+    struct ds_input_device *keyboard_dev;
+    struct ds_input_device *touch_dev;
+
+    struct wl_callback *initial_info_cb;
+
+    struct wl_list link; // ds_wl_backend.seats
+
+    enum wl_seat_capability caps;
+
+    int version;
+    uint32_t enter_serial;
+
+    bool initialized;
+};
+
+struct ds_wl_input_device
+{
+    struct ds_input_device base;
+
+    struct ds_wl_backend *backend;
+    struct ds_wl_seat *seat;
+};
+
+struct ds_wl_pointer
+{
+    struct ds_pointer base;
+
+    struct ds_wl_input_device *input_device;
+    struct wl_pointer *wl_pointer;
 };
 
 struct ds_wl_backend *
@@ -58,4 +109,14 @@ wl_backend_from_backend(struct ds_backend *backend);
 void
 destroy_wl_buffer(struct ds_wl_buffer *buffer);
 
+struct ds_wl_seat *create_wl_seat(struct ds_wl_backend *backend, uint32_t id,
+        uint32_t available_version);
+
+void destroy_wl_seat(struct ds_wl_seat *seat);
+
+void output_enter_pointer(struct ds_wl_output *output,
+        struct ds_wl_pointer *pointer, uint32_t serial);
+
+void output_leave_pointer(struct ds_wl_output *output);
+
 #endif
index db68fe9..88574a4 100644 (file)
@@ -1,6 +1,7 @@
 libds_files += files(
   'backend.c',
   'output.c',
+  'seat.c',
 )
 
 protocols = {
index 1966a97..36ea5f7 100644 (file)
@@ -13,6 +13,8 @@ const struct ds_output_interface wl_output_iface;
 static const struct xdg_surface_listener wl_output_xdg_surface_listener;
 static const struct xdg_toplevel_listener wl_output_xdg_toplevel_listener;
 
+static void output_update_cursor(struct ds_wl_output *output);
+
 struct ds_output *
 ds_wl_backend_create_output(struct ds_backend *ds_backend)
 {
@@ -90,6 +92,32 @@ destroy_wl_buffer(struct ds_wl_buffer *buffer)
     free(buffer);
 }
 
+void
+output_enter_pointer(struct ds_wl_output *output,
+        struct ds_wl_pointer *pointer, uint32_t serial)
+{
+    output->cursor.pointer = pointer;
+    output->cursor.enter_serial = serial;
+
+    output_update_cursor(output);
+}
+
+void
+output_leave_pointer(struct ds_wl_output *output)
+{
+    output->cursor.pointer = NULL;
+    output->cursor.enter_serial = 0;
+}
+
+static void output_update_cursor(struct ds_wl_output *output)
+{
+    struct ds_wl_pointer *pointer = output->cursor.pointer;
+
+    wl_pointer_set_cursor(pointer->wl_pointer, output->cursor.enter_serial,
+            output->cursor.surface, output->cursor.hotspot_x,
+            output->cursor.hotspot_y);
+}
+
 static struct ds_wl_output *
 wl_output_from_output(struct ds_output *ds_output)
 {
diff --git a/src/libds/backend/wayland/seat.c b/src/libds/backend/wayland/seat.c
new file mode 100644 (file)
index 0000000..b799e66
--- /dev/null
@@ -0,0 +1,406 @@
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wayland-client.h>
+
+#include "libds/log.h"
+#include "libds/pointer.h"
+
+#include "backend.h"
+
+#ifdef MIN
+# undef MIN
+#endif
+
+#define MIN(a, b)   ((a) < (b) ? (a) : (b))
+
+static const struct wl_seat_listener seat_listener;
+static const struct wl_callback_listener seat_callback_listener;
+
+static void seat_update_capabilities(struct ds_wl_seat *seat,
+        enum wl_seat_capability caps);
+static struct ds_input_device *
+create_wl_input_device(struct ds_wl_seat *seat,
+        enum ds_input_device_type type);
+static struct ds_pointer *create_wl_pointer(struct ds_wl_seat *seat);
+
+struct ds_wl_seat *
+create_wl_seat(struct ds_wl_backend *backend, uint32_t id,
+        uint32_t available_version)
+{
+    struct ds_wl_seat *seat;
+
+    seat = calloc(1, sizeof *seat);
+    if (!seat)
+        return NULL;
+
+    seat->backend = backend;
+    seat->version = MIN(available_version, 5);
+    seat->wl_seat = wl_registry_bind(backend->server.registry, id,
+            &wl_seat_interface, seat->version);
+
+    wl_seat_add_listener(seat->wl_seat, &seat_listener, seat);
+
+    seat->initial_info_cb = wl_display_sync(backend->server.display);
+    wl_callback_add_listener(seat->initial_info_cb, &seat_callback_listener,
+            seat);
+
+    ds_dbg("wl_backend: Seat(%p) created", seat);
+
+    return seat;
+}
+
+void
+destroy_wl_seat(struct ds_wl_seat *seat)
+{
+    ds_dbg("wl_backend: Seat(%p) destroy", seat);
+
+    if (seat->pointer_dev)
+        ds_input_device_destroy(seat->pointer_dev);
+
+    if (seat->keyboard_dev)
+        ds_input_device_destroy(seat->keyboard_dev);
+
+    if (seat->touch_dev)
+        ds_input_device_destroy(seat->touch_dev);
+
+    if (seat->version >= WL_SEAT_RELEASE_SINCE_VERSION)
+        wl_seat_release(seat->wl_seat);
+    else
+        wl_seat_destroy(seat->wl_seat);
+
+    wl_list_remove(&seat->link);
+
+    free(seat->name);
+    free(seat);
+}
+
+static void
+seat_handle_capabilities(void *data, struct wl_seat *wl_seat,
+        enum wl_seat_capability caps)
+{
+    struct ds_wl_seat *seat = data;
+
+    if (seat->initialized)
+        seat_update_capabilities(seat, caps);
+    else
+        seat->caps = caps;
+
+    ds_dbg("wl_backend: Seat(%p) capabilities(%d)", seat, caps);
+}
+
+static void
+seat_handle_name(void *data, struct wl_seat *wl_seat, const char *name)
+{
+    struct ds_wl_seat *seat = data;
+
+    if (seat->name)
+        free(seat->name);
+
+    ds_dbg("wl_backend: Seat(%p) name(%s)", seat, name);
+
+    seat->name = strdup(name);
+}
+
+static const struct wl_seat_listener seat_listener = {
+    .capabilities = seat_handle_capabilities,
+    .name = seat_handle_name,
+};
+
+static void
+seat_add_callback_handle_done(void *data, struct wl_callback *callback,
+        uint32_t callback_data)
+{
+    struct ds_wl_seat *seat = data;
+
+    wl_callback_destroy(seat->initial_info_cb);
+    seat->initial_info_cb = NULL;
+    seat->initialized = true;
+
+    seat_update_capabilities(seat, seat->caps);
+}
+
+static const struct wl_callback_listener seat_callback_listener = {
+    .done = seat_add_callback_handle_done,
+};
+
+static void
+seat_update_capabilities(struct ds_wl_seat *seat, enum wl_seat_capability caps)
+{
+    if ((caps & WL_SEAT_CAPABILITY_POINTER) && seat->pointer_dev == NULL) {
+        ds_dbg("wl_backend: Seat(%p) offered pointer", seat);
+
+        seat->pointer_dev = create_wl_input_device(seat,
+                DS_INPUT_DEVICE_POINTER);
+        seat->pointer_dev->pointer = create_wl_pointer(seat);
+
+        wl_signal_emit(&seat->backend->base.events.new_input,
+                seat->pointer_dev);
+    }
+    else if (!(caps & WL_SEAT_CAPABILITY_POINTER) &&
+            seat->pointer_dev != NULL) {
+        ds_dbg("wl_backend: Seat(%p) dropped pointer", seat);
+        ds_input_device_destroy(seat->pointer_dev);
+        seat->pointer_dev = NULL;
+    }
+
+    if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && seat->keyboard_dev == NULL) {
+        ds_dbg("wl_backend: Seat(%p) offered keyboard", seat);
+
+        seat->keyboard_dev = create_wl_input_device(seat,
+                DS_INPUT_DEVICE_KEYBOARD);
+    }
+    else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) &&
+            seat->keyboard_dev != NULL) {
+        ds_dbg("wl_backend: Seat(%p) dropped keyboard", seat);
+        ds_input_device_destroy(seat->keyboard_dev);
+        seat->keyboard_dev = NULL;
+    }
+
+    if ((caps & WL_SEAT_CAPABILITY_TOUCH) && seat->touch_dev == NULL) {
+        ds_dbg("wl_backend: Seat(%p) offered touch", seat);
+        seat->touch_dev = create_wl_input_device(seat,
+                DS_INPUT_DEVICE_TOUCH);
+    }
+    else if (!(caps & WL_SEAT_CAPABILITY_TOUCH) &&
+            seat->touch_dev != NULL) {
+        ds_dbg("wl_backend: Seat(%p) dropped touch", seat);
+        ds_input_device_destroy(seat->touch_dev);
+        seat->touch_dev = NULL;
+    }
+}
+
+static const struct ds_input_device_interface input_device_iface;
+
+static bool
+ds_input_device_is_wl(struct ds_input_device *ds_dev)
+{
+    return ds_dev->iface == &input_device_iface;
+}
+
+static struct ds_wl_input_device *
+get_wl_input_device_from_input_device(struct ds_input_device *ds_dev)
+{
+    assert(ds_input_device_is_wl(ds_dev));
+    return (struct ds_wl_input_device *)ds_dev;
+}
+
+static void
+input_device_iface_destroy(struct ds_input_device *ds_dev)
+{
+    struct ds_wl_input_device *dev;
+
+    dev = get_wl_input_device_from_input_device(ds_dev);
+
+    free(dev);
+}
+
+static const struct ds_input_device_interface input_device_iface =
+{
+    .destroy = input_device_iface_destroy,
+};
+
+static struct ds_input_device *
+create_wl_input_device(struct ds_wl_seat *seat,
+        enum ds_input_device_type type)
+{
+    struct ds_wl_input_device *dev;
+    unsigned int vendor = 0, product = 0;
+    size_t name_size;
+    char *name;
+
+    dev = calloc(1, sizeof *dev);
+    if (!dev)
+        return NULL;
+
+    dev->backend = seat->backend;
+    dev->seat = seat;
+
+    name_size = 8 + strlen(seat->name) + 1;
+    name = alloca(name_size);
+    (void) snprintf(name, name_size, "wayland-%s", seat->name);
+
+    ds_input_device_init(&dev->base, type, &input_device_iface, name, vendor,
+            product);
+
+    return &dev->base;
+}
+
+static const struct ds_pointer_interface pointer_iface;
+
+static struct ds_wl_pointer *
+get_wl_pointer_from_pointer(struct ds_pointer *ds_pointer)
+{
+    assert(ds_pointer->iface == &pointer_iface);
+    return (struct ds_wl_pointer *)ds_pointer;
+}
+
+static void
+pointer_iface_destroy(struct ds_pointer *ds_pointer)
+{
+    struct ds_wl_pointer *pointer;
+
+    pointer = get_wl_pointer_from_pointer(ds_pointer);
+
+    wl_pointer_release(pointer->wl_pointer);
+
+    free(pointer);
+}
+
+static const struct ds_pointer_interface pointer_iface = {
+    .destroy = pointer_iface_destroy,
+};
+
+static void
+pointer_handle_enter(void *data, struct wl_pointer *wl_pointer,
+        uint32_t serial, struct wl_surface *surface,
+        wl_fixed_t sx, wl_fixed_t sy)
+{
+    struct ds_wl_seat *seat = data;
+    struct ds_wl_pointer *pointer;
+
+    if (!surface)
+        return;
+
+    ds_dbg("Enter pointer");
+
+    seat->output = wl_surface_get_user_data(surface);
+    seat->enter_serial = serial;
+
+    pointer = get_wl_pointer_from_pointer(seat->pointer_dev->pointer);
+    output_enter_pointer(seat->output, pointer, seat->enter_serial);
+}
+
+static void
+pointer_handle_leave(void *data, struct wl_pointer *wl_pointer,
+        uint32_t serial, struct wl_surface *surface)
+{
+    struct ds_wl_seat *seat = data;
+    struct ds_wl_output *output;
+
+    if (!seat->output)
+        return;
+
+    output = wl_surface_get_user_data(surface);
+    if (seat->output != output)
+        return;
+
+    ds_dbg("Leave pointer");
+
+    output_leave_pointer(seat->output);
+
+    seat->output = NULL;
+    seat->enter_serial = 0;
+}
+
+static void
+pointer_handle_motion(void *data, struct wl_pointer *wl_pointer,
+        uint32_t time, wl_fixed_t sx, wl_fixed_t sy)
+{
+    struct ds_wl_seat *seat = data;
+
+    if (!seat->output)
+        return;
+
+    // FIXME take size size of a output into account
+    struct ds_event_pointer_motion_absolute event = {
+        .device = seat->pointer_dev,
+        .time_msec = time,
+        .x = wl_fixed_to_double(sx) / 700,
+        .y = wl_fixed_to_double(sy) / 400,
+    };
+
+    wl_signal_emit(&seat->pointer_dev->pointer->events.motion_absolute,
+            &event);
+}
+
+static void
+pointer_handle_button(void *data, struct wl_pointer *wl_pointer,
+        uint32_t serial, uint32_t time, uint32_t button, uint32_t state)
+{
+    struct ds_wl_seat *seat = data;
+
+    if (!seat->output)
+        return;
+
+    struct ds_event_pointer_button event = {
+        .device = seat->pointer_dev,
+        .button = button,
+        .state = state,
+        .time_msec = time,
+    };
+
+    wl_signal_emit(&seat->pointer_dev->pointer->events.button, &event);
+}
+
+static void
+pointer_handle_axis(void *data, struct wl_pointer *wl_pointer,
+        uint32_t time, uint32_t axis, wl_fixed_t value)
+{
+    // TODO
+}
+
+static void
+pointer_handle_frame(void *data, struct wl_pointer *wl_pointer)
+{
+    struct ds_wl_seat *seat = data;
+
+    if (!seat->output)
+        return;
+
+    wl_signal_emit(&seat->pointer_dev->pointer->events.frame,
+            seat->pointer_dev);
+}
+
+static void
+pointer_handle_axis_source(void *data, struct wl_pointer *wl_pointer,
+        uint32_t axis_source)
+{
+    // TODO
+}
+
+static void
+pointer_handle_axis_stop(void *data, struct wl_pointer *wl_pointer,
+        uint32_t time, uint32_t axis)
+{
+    // TODO
+}
+
+static void
+pointer_handle_axis_discrete(void *data, struct wl_pointer *wl_pointer,
+        uint32_t axis, int32_t discrete)
+{
+    // TODO
+}
+
+static const struct wl_pointer_listener wl_pointer_listener = {
+    .enter = pointer_handle_enter,
+    .leave = pointer_handle_leave,
+    .motion = pointer_handle_motion,
+    .button = pointer_handle_button,
+    .axis = pointer_handle_axis,
+    .frame = pointer_handle_frame,
+    .axis_source = pointer_handle_axis_source,
+    .axis_stop = pointer_handle_axis_stop,
+    .axis_discrete = pointer_handle_axis_discrete,
+};
+
+static struct ds_pointer *
+create_wl_pointer(struct ds_wl_seat *seat)
+{
+    struct ds_wl_pointer *pointer;
+
+    pointer = calloc(1, sizeof *pointer);
+    if (!pointer) {
+        ds_err("Could not allocate memory");
+        return NULL;
+    }
+
+    ds_pointer_init(&pointer->base, &pointer_iface);
+
+    pointer->wl_pointer = wl_seat_get_pointer(seat->wl_seat);
+    wl_pointer_add_listener(pointer->wl_pointer, &wl_pointer_listener, seat);
+
+    return &pointer->base;
+}
diff --git a/src/libds/input_device.c b/src/libds/input_device.c
new file mode 100644 (file)
index 0000000..db84100
--- /dev/null
@@ -0,0 +1,69 @@
+#define _POSIX_C_SOURCE 200809L
+#include <stdlib.h>
+#include <string.h>
+#include <wayland-server.h>
+
+#include "libds/log.h"
+#include "libds/interfaces/input_device.h"
+#include "libds/interfaces/pointer.h"
+
+WL_EXPORT enum ds_input_device_type
+ds_input_device_get_type(struct ds_input_device *dev)
+{
+    return dev->type;
+}
+
+WL_EXPORT struct ds_pointer *
+ds_input_device_get_pointer(struct ds_input_device *dev)
+{
+    if (dev->type != DS_INPUT_DEVICE_POINTER) {
+        ds_err("Given ds_input_device is not a pointer device");
+        return NULL;
+    }
+
+    return dev->pointer;
+}
+
+WL_EXPORT void
+ds_input_device_add_destroy_listener(struct ds_input_device *dev,
+        struct wl_listener *listener)
+{
+    wl_signal_add(&dev->events.destroy, listener);
+}
+
+void
+ds_input_device_init(struct ds_input_device *dev,
+        enum ds_input_device_type type,
+        const struct ds_input_device_interface *iface,
+        const char *name, int vendor, int product)
+{
+    dev->type = type;
+    dev->iface = iface;
+    dev->name = strdup(name);
+
+    wl_signal_init(&dev->events.destroy);
+}
+
+void
+ds_input_device_destroy(struct ds_input_device *dev)
+{
+    wl_signal_emit(&dev->events.destroy, dev);
+
+    if (dev->_device) {
+        switch (dev->type) {
+            case DS_INPUT_DEVICE_POINTER:
+                ds_pointer_destroy(dev->pointer);
+                break;
+            default:
+                ds_err("Warning: leaking memory %p %p %d",
+                        dev->_device, dev, dev->type);
+                break;
+        }
+    }
+
+    free(dev->name);
+    if (dev->iface && dev->iface->destroy)
+        dev->iface->destroy(dev);
+    else
+        free(dev);
+}
index 50f7962..a08dfdc 100644 (file)
@@ -18,6 +18,8 @@ libds_files = [
   'xdg_shell/xdg_toplevel.c',
   'pixel_format.c',
   'backend.c',
+  'input_device.c',
+  'pointer.c',
 ]
 
 protocols = {
diff --git a/src/libds/pointer.c b/src/libds/pointer.c
new file mode 100644 (file)
index 0000000..5c5cbda
--- /dev/null
@@ -0,0 +1,55 @@
+#include <stdlib.h>
+#include <wayland-server.h>
+#include "libds/interfaces/pointer.h"
+
+void
+ds_pointer_init(struct ds_pointer *pointer,
+        const struct ds_pointer_interface *iface)
+{
+    pointer->iface = iface;
+
+    wl_signal_init(&pointer->events.motion);
+    wl_signal_init(&pointer->events.motion_absolute);
+    wl_signal_init(&pointer->events.button);
+    wl_signal_init(&pointer->events.frame);
+}
+
+void
+ds_pointer_destroy(struct ds_pointer *pointer)
+{
+    if (!pointer)
+        return;
+
+    if (pointer->iface && pointer->iface->destroy)
+        pointer->iface->destroy(pointer);
+    else
+        free(pointer);
+}
+
+WL_EXPORT void
+ds_pointer_add_motion_listener(struct ds_pointer *pointer,
+        struct wl_listener *listener)
+{
+    wl_signal_add(&pointer->events.motion, listener);
+}
+
+WL_EXPORT void
+ds_pointer_add_motion_absolute_listener(struct ds_pointer *pointer,
+        struct wl_listener *listener)
+{
+    wl_signal_add(&pointer->events.motion_absolute, listener);
+}
+
+WL_EXPORT void
+ds_pointer_add_button_listener(struct ds_pointer *pointer,
+        struct wl_listener *listener)
+{
+    wl_signal_add(&pointer->events.button, listener);
+}
+
+WL_EXPORT void
+ds_pointer_add_frame_listener(struct ds_pointer *pointer,
+        struct wl_listener *listener)
+{
+    wl_signal_add(&pointer->events.frame, listener);
+}