--- /dev/null
+#include <assert.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include <gbm.h>
+#include <libds/log.h>
+#include "shared/pixel_format.h"
+#include "gbm_server.h"
+
+static const struct ds_buffer_resource_interface gbm_buffer_resource_iface;
+static const struct ds_buffer_interface gbm_client_buffer_iface;
+
+static struct ds_gbm_server *gbm_server;
+
+WL_EXPORT struct ds_gbm_server *
+ds_gbm_server_create(int fd)
+{
+ struct ds_gbm_server *gbm;
+
+ if (gbm_server) {
+ ds_inf("gbm server already created");
+ return gbm_server;
+ }
+
+ gbm = calloc(1, sizeof *gbm);
+ if (!gbm)
+ return NULL;
+
+ gbm->gbm_device = gbm_create_device(fd);
+ if (!gbm->gbm_device) {
+ free(gbm);
+ return NULL;
+ }
+
+ ds_buffer_register_resource_interface(&gbm_buffer_resource_iface);
+
+ gbm_server = gbm;
+
+ return gbm;
+}
+
+WL_EXPORT void
+ds_gbm_server_destroy(struct ds_gbm_server *gbm)
+{
+ gbm_device_destroy(gbm->gbm_device);
+
+ free(gbm);
+
+ gbm_server = NULL;
+}
+
+WL_EXPORT struct ds_gbm_client_buffer *
+ds_gbm_client_buffer_from_buffer(struct ds_buffer *ds_buffer)
+{
+ if (ds_buffer->iface != &gbm_client_buffer_iface)
+ return NULL;
+ return (struct ds_gbm_client_buffer *)ds_buffer;
+}
+
+WL_EXPORT struct gbm_bo *
+ds_gbm_client_buffer_get_gbm_bo(struct ds_gbm_client_buffer *buffer)
+{
+ if (buffer->base.iface != &gbm_client_buffer_iface)
+ return NULL;
+ return buffer->gbm_bo;
+}
+
+static void
+gbm_client_buffer_handle_release(struct wl_listener *listener, void *data)
+{
+ struct ds_gbm_client_buffer *buffer;
+
+ buffer = wl_container_of(listener, buffer, buffer_release);
+ if (buffer->resource)
+ wl_buffer_send_release(buffer->resource);
+}
+
+static void
+gbm_client_buffer_handle_resource_destroy(struct wl_listener *listener,
+ void *data)
+{
+ struct ds_gbm_client_buffer *buffer;
+
+ buffer = wl_container_of(listener, buffer, resource_destroy);
+
+ buffer->resource = NULL;
+
+ wl_list_remove(&buffer->resource_destroy.link);
+ wl_list_init(&buffer->resource_destroy.link);
+
+ ds_buffer_drop(&buffer->base);
+}
+
+static struct ds_gbm_client_buffer *
+gbm_client_buffer_from_buffer(struct ds_buffer *ds_buffer)
+{
+ assert(ds_buffer->iface == &gbm_client_buffer_iface);
+ return (struct ds_gbm_client_buffer *)ds_buffer;
+}
+
+static void
+gbm_client_buffer_iface_destroy(struct ds_buffer *ds_buffer)
+{
+ struct ds_gbm_client_buffer *buffer;
+
+ buffer = gbm_client_buffer_from_buffer(ds_buffer);
+
+ ds_dbg("Destroy gbm client buffer(%p)", buffer);
+
+ gbm_bo_destroy(buffer->gbm_bo);
+
+ wl_list_remove(&buffer->resource_destroy.link);
+ wl_list_remove(&buffer->buffer_release.link);
+ free(buffer);
+}
+
+static bool
+gbm_client_buffer_iface_begin_data_ptr_access(struct ds_buffer *ds_buffer,
+ enum ds_buffer_data_ptr_access_flag flags, void **data,
+ uint32_t *format, size_t *stride)
+{
+ struct ds_gbm_client_buffer *buffer;
+ uint32_t gbm_flags = 0, map_stride;
+ void *map, *map_data;
+
+ buffer = gbm_client_buffer_from_buffer(ds_buffer);
+
+ if (flags & DS_BUFFER_DATA_PTR_ACCESS_READ)
+ gbm_flags |= GBM_BO_TRANSFER_READ;
+
+ if (flags & DS_BUFFER_DATA_PTR_ACCESS_WRITE)
+ gbm_flags |= GBM_BO_TRANSFER_WRITE;
+
+ map = gbm_bo_map(buffer->gbm_bo, 0, 0, ds_buffer->width, ds_buffer->height,
+ gbm_flags, &map_stride, &map_data);
+ if (!map) {
+ ds_err("Failed gbm_surface_map()");
+ return false;
+ }
+
+ buffer->map_data = map_data;
+
+ *format = convert_tbm_format_to_drm(buffer->format);
+ *stride = map_stride;
+ *data = map;
+
+ return true;
+}
+
+static void
+gbm_client_buffer_iface_end_ptr_access(struct ds_buffer *ds_buffer)
+{
+ struct ds_gbm_client_buffer *buffer;
+
+ buffer = gbm_client_buffer_from_buffer(ds_buffer);
+
+ gbm_bo_unmap(buffer->gbm_bo, buffer->map_data);
+
+ buffer->map_data = NULL;
+}
+
+static struct wl_resource *
+gbm_client_buffer_iface_get_resource(struct ds_buffer *ds_buffer)
+{
+ struct ds_gbm_client_buffer *buffer;
+
+ buffer = gbm_client_buffer_from_buffer(ds_buffer);
+
+ return buffer->resource;
+}
+
+static const struct ds_buffer_interface gbm_client_buffer_iface = {
+ .destroy = gbm_client_buffer_iface_destroy,
+ .begin_data_ptr_access = gbm_client_buffer_iface_begin_data_ptr_access,
+ .end_data_ptr_access = gbm_client_buffer_iface_end_ptr_access,
+ .get_resource = gbm_client_buffer_iface_get_resource,
+};
+
+static struct ds_gbm_client_buffer *
+gbm_client_buffer_create(struct wl_resource *resource)
+{
+ struct ds_gbm_client_buffer *buffer;
+ struct gbm_bo *gbm_bo;
+ int32_t width, height;
+
+ gbm_bo = gbm_bo_import(gbm_server->gbm_device, GBM_BO_IMPORT_WL_BUFFER,
+ resource, GBM_BO_USE_RENDERING);
+ if (!gbm_bo) return NULL;
+
+ width = gbm_bo_get_width(gbm_bo);
+ height = gbm_bo_get_height(gbm_bo);
+
+ buffer = calloc(1, sizeof *buffer);
+ if (!buffer) {
+ gbm_bo_destroy(gbm_bo);
+ return NULL;
+ }
+
+ ds_buffer_init(&buffer->base, &gbm_client_buffer_iface, width, height);
+
+ buffer->resource = resource;
+ buffer->gbm_bo = gbm_bo;
+ buffer->format = gbm_bo_get_format(gbm_bo);
+ buffer->stride = gbm_bo_get_stride(gbm_bo);
+
+ buffer->buffer_release.notify = gbm_client_buffer_handle_release;
+ ds_buffer_add_release_listener(&buffer->base, &buffer->buffer_release);
+
+ buffer->resource_destroy.notify =
+ gbm_client_buffer_handle_resource_destroy;
+ wl_resource_add_destroy_listener(resource, &buffer->resource_destroy);
+
+ ds_dbg("gbm client buffer(%p) created", buffer);
+
+ return buffer;
+}
+
+static struct ds_gbm_client_buffer *
+gbm_client_buffer_get_or_create(struct wl_resource *resource)
+{
+ struct ds_gbm_client_buffer *buffer;
+ struct wl_listener *resource_destroy_listener;
+
+ resource_destroy_listener = wl_resource_get_destroy_listener(resource,
+ gbm_client_buffer_handle_resource_destroy);;
+ if (resource_destroy_listener) {
+ buffer = wl_container_of(resource_destroy_listener,
+ buffer, resource_destroy);
+ return buffer;
+ }
+
+ return gbm_client_buffer_create(resource);
+}
+
+static bool
+gbm_buffer_resource_iface_is_instance(struct wl_resource *resource)
+{
+ if (!gbm_server) return false;
+
+ if (!gbm_client_buffer_get_or_create(resource))
+ return false;
+
+ return true;
+}
+
+static struct ds_buffer *
+gbm_buffer_resource_iface_from_resource(struct wl_resource *resource)
+{
+ struct ds_gbm_client_buffer *buffer;
+
+ buffer = gbm_client_buffer_get_or_create(resource);
+ if (!buffer) {
+ ds_err("Could not get or create ds_gbm_client_buffer");
+ return NULL;
+ }
+
+ return &buffer->base;
+}
+
+static const struct ds_buffer_resource_interface gbm_buffer_resource_iface = {
+ .name = "gbm",
+ .is_instance = gbm_buffer_resource_iface_is_instance,
+ .from_resource = gbm_buffer_resource_iface_from_resource,
+};