--- /dev/null
+Copyright (c) 2022 Samsung Electronics co., Ltd. All Rights Reserved.
+Copyright (c) 2017, 2018 Drew DeVault
+Copyright (c) 2014 Jari Vetoniemi
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
--- /dev/null
+[] Implement a xdg shell
+[] Implement a KMS/DRM backend
+[] Implement a libinput backend
+[] Implement an input
+[] Implement a viewporter
+[] Make up a ds output interface
+[] Make up tests
+[] Document APIs
--- /dev/null
+project('libds-samples', 'c',
+ version : '0.1',
+ default_options : ['warning_level=3'])
+
+common_deps = [
+ dependency('wayland-server', required: true),
+ dependency('libds', required: true),
+]
+
+executable('wl-backend',
+ 'wl-backend.c',
+ dependencies: common_deps,
+ install : true)
+
+executable('tinyds',
+ 'tinyds.c',
+ dependencies: [
+ common_deps,
+ dependency('pixman-1', required: true),
+ dependency('libdrm', required: true),
+ ],
+ install : true)
--- /dev/null
+#include <assert.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <time.h>
+
+#include <drm_fourcc.h>
+#include <pixman.h>
+#include <wayland-server.h>
+#include <libds/log.h>
+#include <libds/backend.h>
+#include <libds/allocator/shm.h>
+#include <libds/backend/wayland.h>
+#include <libds/swapchain.h>
+#include <libds/compositor.h>
+#include <libds/xdg_shell.h>
+
+#define TINYDS_UNUSED __attribute__((unused))
+
+#define OUTPUT_WIDTH 1280
+#define OUTPUT_HEIGHT 720
+
+struct tinyds_output
+{
+ struct tinyds_server *server;
+ struct ds_output *ds_output;
+ struct ds_allocator *allocator;
+ struct ds_swapchain *swapchain;
+ struct ds_buffer *front_buffer;
+
+ struct wl_listener output_destroy;
+ struct wl_listener output_frame;
+ struct wl_list link; // tinyds_server::outputs
+
+ int width, height;
+
+ bool drawable;
+ bool damaged;
+};
+
+struct tinyds_server
+{
+ struct wl_display *display;
+
+ struct ds_backend *backend;
+ struct ds_compositor *compositor;
+ struct ds_xdg_shell *xdg_shell;
+
+ struct tinyds_output primary_output;
+
+ struct wl_list views;
+ struct wl_list outputs;
+
+ struct wl_listener new_xdg_surface;
+};
+
+struct tinyds_view
+{
+ struct tinyds_server *server;
+
+ struct ds_xdg_surface *xdg_surface;
+
+ struct wl_listener xdg_surface_map;
+ struct wl_listener xdg_surface_unmap;
+ struct wl_listener xdg_surface_destroy;
+ struct wl_listener surface_commit;
+ struct wl_list link; // tinyds_server::views
+
+ int x, y;
+ bool mapped;
+};
+
+struct tinyds_server _tinyds;
+
+static bool init_server(struct tinyds_server *server, struct wl_display *display);
+static void fini_server(struct tinyds_server *server);
+static bool init_output(struct tinyds_output *output, struct tinyds_server *server,
+ int width, int height);
+static void fini_output(struct tinyds_output *output);
+static void output_handle_destroy(struct wl_listener *listener, void *data);
+static void output_handle_frame(struct wl_listener *listener, void *data);
+static void draw_server(struct tinyds_server *server);
+static void draw_server_with_damage(struct tinyds_server *server);
+static void draw_output(struct tinyds_output *output);
+static void draw_view(struct tinyds_view *view, pixman_image_t *dst_image);
+
+int
+main(void)
+{
+ struct tinyds_server *server = &_tinyds;
+ struct wl_display *display;
+ const char *socket;
+
+ ds_log_init(DS_DBG, NULL);
+
+ display = wl_display_create();
+ assert(display);
+
+ assert(init_server(server, display) == true);
+
+ assert(init_output(&server->primary_output, server,
+ OUTPUT_WIDTH, OUTPUT_HEIGHT) == true);
+
+ socket = wl_display_add_socket_auto(display);
+ assert(socket);
+
+ ds_backend_start(server->backend);
+
+ draw_server(server);
+
+ setenv("WAYLAND_DISPLAY", socket, true);
+
+ ds_inf("Running Wayland compositor on WAYLAND_DISPLAY=%s", socket);
+
+ wl_display_run(server->display);
+
+ fini_output(&server->primary_output);
+ fini_server(server);
+ wl_display_destroy(display);
+
+ return 0;
+}
+
+static struct ds_backend *
+tinyds_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
+view_handle_xdg_surface_map(struct wl_listener *listener,
+ void *data TINYDS_UNUSED)
+{
+ struct tinyds_view *view;
+
+ view = wl_container_of(listener, view, xdg_surface_map);
+ view->mapped = true;
+}
+
+static void
+view_handle_xdg_surface_unmap(struct wl_listener *listener,
+ void *data TINYDS_UNUSED)
+{
+ struct tinyds_view *view;
+
+ view = wl_container_of(listener, view, xdg_surface_unmap);
+ view->mapped = false;
+}
+
+static void
+view_handle_xdg_surface_destroy(struct wl_listener *listener,
+ void *data TINYDS_UNUSED)
+{
+ struct tinyds_view *view;
+
+ view = wl_container_of(listener, view, xdg_surface_destroy);
+
+ draw_server_with_damage(view->server);
+
+ wl_list_remove(&view->xdg_surface_destroy.link);
+ wl_list_remove(&view->xdg_surface_map.link);
+ wl_list_remove(&view->xdg_surface_unmap.link);
+ wl_list_remove(&view->surface_commit.link);
+ wl_list_remove(&view->link);
+ free(view);
+}
+
+static void
+view_handle_surface_commit(struct wl_listener *listener,
+ void *data TINYDS_UNUSED)
+{
+ struct tinyds_view *view;
+
+ view = wl_container_of(listener, view, surface_commit);
+ draw_server_with_damage(view->server);
+}
+
+static void
+server_new_xdg_surface(struct wl_listener *listener, void *data)
+{
+ struct tinyds_server *server;
+ struct tinyds_view *view;
+ struct ds_xdg_surface *xdg_surface;
+
+ server = wl_container_of(listener, server, new_xdg_surface);
+ xdg_surface = data;
+
+ ds_inf("New xdg_surface(%p)", (void *)xdg_surface);
+
+ view = calloc(1, sizeof *view);
+ view->server = server;
+ view->xdg_surface = xdg_surface;
+
+ view->xdg_surface_map.notify = view_handle_xdg_surface_map;
+ ds_xdg_surface_add_map_listener(xdg_surface,
+ &view->xdg_surface_map);
+
+ view->xdg_surface_unmap.notify = view_handle_xdg_surface_unmap;
+ ds_xdg_surface_add_unmap_listener(xdg_surface,
+ &view->xdg_surface_unmap);
+
+ view->xdg_surface_destroy.notify = view_handle_xdg_surface_destroy;
+ ds_xdg_surface_add_destroy_listener(xdg_surface,
+ &view->xdg_surface_destroy);
+
+ view->surface_commit.notify = view_handle_surface_commit;
+ ds_surface_add_commit_listener(
+ ds_xdg_surface_get_surface(xdg_surface),
+ &view->surface_commit);
+
+ wl_list_insert(server->views.prev, &view->link);
+}
+
+static bool
+init_server(struct tinyds_server *server, struct wl_display *display)
+{
+ server->display = display;
+
+ wl_list_init(&server->outputs);
+ wl_list_init(&server->views);
+
+ if (wl_display_init_shm(display) != 0)
+ return false;
+
+ server->backend = tinyds_create_backend_auto(display);
+ if (!server->backend)
+ return false;
+
+ server->compositor = ds_compositor_create(display);
+ if (!server->compositor) {
+ ds_backend_destroy(server->backend);
+ return false;
+ }
+
+ server->xdg_shell = ds_xdg_shell_create(display);
+ if (!server->xdg_shell) {
+ ds_backend_destroy(server->backend);
+ return false;
+ }
+
+ server->new_xdg_surface.notify = server_new_xdg_surface;
+ ds_xdg_shell_add_new_surface_listener(server->xdg_shell,
+ &server->new_xdg_surface);
+
+ return true;
+}
+
+static void
+fini_server(struct tinyds_server *server)
+{
+ wl_list_remove(&server->new_xdg_surface.link);
+}
+
+static void
+output_handle_destroy(struct wl_listener *listener, void *data TINYDS_UNUSED)
+{
+ struct tinyds_output *output =
+ wl_container_of(listener, output, output_destroy);
+
+ wl_list_remove(&output->output_destroy.link);
+ wl_list_remove(&output->output_frame.link);
+ output->ds_output = NULL;
+
+ wl_display_terminate(output->server->display);
+}
+
+static void
+output_handle_frame(struct wl_listener *listener, void *data TINYDS_UNUSED)
+{
+ struct tinyds_output *output =
+ wl_container_of(listener, output, output_frame);
+
+ output->drawable = true;
+ draw_output(output);
+}
+
+static bool
+init_output(struct tinyds_output *output, struct tinyds_server *server,
+ int width, int height)
+{
+ output->server = server;
+ output->width = width;
+ output->height = height;
+ output->front_buffer = NULL;
+ output->drawable = true;
+ output->damaged = true;
+
+ output->allocator = ds_shm_allocator_create();
+ if (!output->allocator)
+ return false;
+
+ output->swapchain = ds_swapchain_create(output->allocator,
+ width, height, DRM_FORMAT_XRGB8888);
+ if (!output->swapchain)
+ goto err_swapchain;
+
+ output->ds_output =
+ ds_wl_backend_create_output(server->backend);
+ if (!output->ds_output)
+ goto err_output;
+
+ output->output_destroy.notify = output_handle_destroy;
+ ds_output_add_destroy_listener(output->ds_output, &output->output_destroy);
+
+ output->output_frame.notify = output_handle_frame;
+ ds_output_add_frame_listener(output->ds_output, &output->output_frame);
+
+ wl_list_insert(&server->outputs, &output->link);
+
+ return true;
+
+err_output:
+ ds_swapchain_destroy(output->swapchain);
+err_swapchain:
+ ds_allocator_destroy(output->allocator);
+
+ return false;
+}
+
+static void
+fini_output(struct tinyds_output *output)
+{
+ wl_list_remove(&output->link);
+ if (output->front_buffer)
+ ds_buffer_unlock(output->front_buffer);
+ if (output->ds_output)
+ ds_output_destroy(output->ds_output);
+ ds_swapchain_destroy(output->swapchain);
+ ds_allocator_destroy(output->allocator);
+}
+
+static void
+draw_server(struct tinyds_server *server)
+{
+ struct tinyds_output *output;
+
+ wl_list_for_each(output, &server->outputs, link)
+ draw_output(output);
+}
+
+static void
+draw_server_with_damage(struct tinyds_server *server)
+{
+ struct tinyds_output *output;
+
+ wl_list_for_each(output, &server->outputs, link) {
+ output->damaged = true;
+ draw_output(output);
+ }
+}
+
+static void image_fill_color(pixman_image_t *image,
+ uint8_t r, uint8_t g, uint8_t b);
+static pixman_image_t *image_from_buffer(struct ds_buffer *buffer,
+ enum ds_buffer_data_ptr_access_flag access_flag);
+static void view_send_frame_done(struct tinyds_view *view);
+
+static void
+draw_output(struct tinyds_output *output)
+{
+ struct ds_buffer *output_buffer;
+ pixman_image_t *output_image;
+ struct tinyds_view *view;
+
+ if (!output->drawable || !output->damaged)
+ return;
+
+ output_buffer = ds_swapchain_acquire(output->swapchain, NULL);
+ if (!output_buffer)
+ return;
+
+ output_image = image_from_buffer(output_buffer,
+ DS_BUFFER_DATA_PTR_ACCESS_WRITE);
+ if (!output_image) {
+ ds_buffer_unlock(output_buffer);
+ return;
+ }
+
+ image_fill_color(output_image, 80, 80, 80);
+
+ wl_list_for_each(view, &output->server->views, link) {
+ if (!view->mapped)
+ continue;
+ draw_view(view, output_image);
+ }
+ pixman_image_unref(output_image);
+
+ ds_output_attach_buffer(output->ds_output, output_buffer);
+ ds_output_commit(output->ds_output);
+
+ if (output->front_buffer)
+ ds_buffer_unlock(output->front_buffer);
+ output->front_buffer = output_buffer;
+
+ output->drawable = false;
+ output->damaged = false;
+}
+
+static void
+draw_view(struct tinyds_view *view, pixman_image_t *dst_image)
+{
+ struct ds_buffer *buffer;
+ pixman_image_t *src_image;
+
+ buffer = ds_surface_get_buffer(
+ ds_xdg_surface_get_surface(view->xdg_surface));
+ if (!buffer)
+ return;
+
+ src_image = image_from_buffer(buffer,
+ DS_BUFFER_DATA_PTR_ACCESS_READ);
+ pixman_image_composite32(PIXMAN_OP_OVER,
+ src_image,
+ NULL,
+ dst_image,
+ 0, 0, 0, 0, 0, 0,
+ pixman_image_get_width(src_image),
+ pixman_image_get_height(src_image));
+ pixman_image_unref(src_image);
+
+ view_send_frame_done(view);
+}
+
+static pixman_color_t *
+color_rgb888(pixman_color_t *tmp, uint8_t r, uint8_t g, uint8_t b)
+{
+ tmp->alpha = 65535;
+ tmp->red = (r << 8) + r;
+ tmp->green = (g << 8) + g;
+ tmp->blue = (b << 8) +b;
+
+ return tmp;
+}
+
+static void
+image_fill_color(pixman_image_t *image, uint8_t r, uint8_t g, uint8_t b)
+{
+ pixman_image_t *color_image;
+ pixman_color_t color;
+
+ color_rgb888(&color, r, g, b);
+ color_image = pixman_image_create_solid_fill(&color);
+ pixman_image_composite32(PIXMAN_OP_SRC,
+ color_image,
+ NULL,
+ image,
+ 0, 0, 0, 0, 0, 0,
+ pixman_image_get_width(image),
+ pixman_image_get_height(image));
+ pixman_image_unref(color_image);
+}
+
+static void
+destroy_pixman_image(pixman_image_t *image TINYDS_UNUSED, void *data)
+{
+ struct ds_buffer *buffer = data;
+ ds_buffer_end_data_ptr_access(buffer);
+ ds_buffer_unlock(buffer);
+}
+
+static uint32_t
+convert_drm_format_to_pixman(uint32_t fmt)
+{
+ switch (fmt) {
+ case DRM_FORMAT_XRGB8888:
+ return PIXMAN_x8r8g8b8;
+ case DRM_FORMAT_ARGB8888:
+ return PIXMAN_a8r8g8b8;
+ default:
+ assert(0 && "not reached");
+ }
+}
+
+static pixman_image_t *
+image_from_buffer(struct ds_buffer *buffer,
+ enum ds_buffer_data_ptr_access_flag access_flag)
+{
+ pixman_image_t *image;
+ void *data;
+ uint32_t format;
+ size_t stride;
+ int width, height;
+
+ ds_buffer_get_size(buffer, &width, &height);
+
+ if (!ds_buffer_begin_data_ptr_access(buffer,
+ access_flag, &data, &format, &stride))
+ return NULL;
+
+ format = convert_drm_format_to_pixman(format);
+ image = pixman_image_create_bits(format, width, height, data, stride);
+ if (!image) {
+ ds_buffer_end_data_ptr_access(buffer);
+ return NULL;
+ }
+
+ pixman_image_set_destroy_function(image,
+ destroy_pixman_image, ds_buffer_lock(buffer));
+
+ return image;
+}
+
+static void
+view_send_frame_done(struct tinyds_view *view)
+{
+ struct timespec now;
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ ds_surface_send_frame_done(ds_xdg_surface_get_surface(view->xdg_surface),
+ &now);
+}
--- /dev/null
+#include <assert.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+
+#include <wayland-server.h>
+#include <libds/log.h>
+#include <libds/backend.h>
+#include <libds/allocator/shm.h>
+#include <libds/backend/wayland.h>
+#include <libds/swapchain.h>
+#include <libds/compositor.h>
+
+#define WIDTH 700
+#define HEIGHT 400
+
+struct server
+{
+ struct ds_backend *backend;
+ struct ds_output *output;
+ struct ds_allocator *allocator;
+ struct ds_swapchain *swapchain;
+ struct ds_buffer *front_buffer;
+
+ struct wl_display *display;
+
+ struct {
+ struct wl_listener output_destroy;
+ struct wl_listener output_frame;
+ } listener;
+
+ int width, height;
+};
+
+struct server _server;
+
+static void init_server(struct server *server, struct wl_display *display);
+static void fini_server(struct server *server);
+static void output_handle_destroy(struct wl_listener *listener, void *data);
+static void output_handle_frame(struct wl_listener *listener, void *data);
+static void draw_output(struct server *server);
+
+int
+main(void)
+{
+ struct server *server = &_server;
+ struct wl_display *display;
+
+ ds_log_init(DS_DBG, NULL);
+
+ display = wl_display_create();
+ assert(display);
+
+ server->width = WIDTH;
+ server->height = HEIGHT;
+
+ init_server(server, display);
+
+ server->listener.output_destroy.notify = output_handle_destroy;
+ ds_output_add_destroy_listener(server->output,
+ &server->listener.output_destroy);
+
+ server->listener.output_frame.notify = output_handle_frame;
+ ds_output_add_frame_listener(server->output,
+ &server->listener.output_frame);
+
+ ds_backend_start(server->backend);
+
+ draw_output(server);
+
+ wl_display_run(server->display);
+
+ fini_server(server);
+ wl_display_destroy(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
+init_server(struct server *server, struct wl_display *display)
+{
+ server->display = display;
+
+ server->backend = create_backend_auto(display);
+ assert(server->backend);
+
+ server->allocator = ds_shm_allocator_create();
+ assert(server->allocator);
+
+ server->swapchain = ds_swapchain_create(server->allocator,
+ server->width, server->height, WL_SHM_FORMAT_XRGB8888);
+ assert(server->swapchain);
+
+ server->output =
+ ds_wl_backend_create_output(server->backend);
+ assert(server->output);
+}
+
+static void
+fini_server(struct server *server)
+{
+ ds_buffer_unlock(server->front_buffer);
+ ds_swapchain_destroy(server->swapchain);
+ ds_allocator_destroy(server->allocator);
+}
+
+static void
+paint_pixels(void *image, int padding, int width, int height, uint32_t time)
+{
+ const int halfh = padding + (height - padding * 2) / 2;
+ const int halfw = padding + (width - padding * 2) / 2;
+ int ir, or;
+ uint32_t *pixel = image;
+ int y;
+
+ /* squared radii thresholds */
+ or = (halfw < halfh ? halfw : halfh) - 8;
+ ir = or - 32;
+ or *= or;
+ ir *= ir;
+
+ pixel += padding * width;
+ for (y = padding; y < height - padding; y++) {
+ int x;
+ int y2 = (y - halfh) * (y - halfh);
+
+ pixel += padding;
+ for (x = padding; x < width - padding; x++) {
+ uint32_t v;
+
+ /* squared distance from center */
+ int r2 = (x - halfw) * (x - halfw) + y2;
+
+ if (r2 < ir)
+ v = (r2 / 32 + time / 64) * 0x0080401;
+ else if (r2 < or)
+ v = (y + time / 32) * 0x0080401;
+ else
+ v = (x + time / 16) * 0x0080401;
+ v &= 0x00ffffff;
+
+ /* cross if compositor uses X from XRGB as alpha */
+ if (abs(x - y) > 6 && abs(x + y - height) > 6)
+ v |= 0xff000000;
+
+ *pixel++ = v;
+ }
+
+ pixel += padding;
+ }
+}
+
+static inline int64_t
+timespec_to_msec(const struct timespec *a)
+{
+ return (int64_t)a->tv_sec * 1000 + a->tv_nsec / 1000000;
+}
+
+static void
+output_handle_destroy(struct wl_listener *listener,
+ void *data __attribute__((unused)))
+{
+ struct server *server =
+ wl_container_of(listener, server, listener.output_destroy);
+ wl_display_terminate(server->display);
+}
+
+static void
+output_handle_frame(struct wl_listener *listener,
+ void *data __attribute__((unused)))
+{
+ struct server *server =
+ wl_container_of(listener, server, listener.output_frame);
+ draw_output(server);
+}
+
+static void
+draw_output(struct server *server)
+{
+ struct ds_buffer *buffer;
+ void *data;
+ uint32_t format;
+ size_t stride;
+ struct timespec now;
+ uint32_t frame_time_msec;
+
+ ds_dbg("Redraw output");
+
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ frame_time_msec = timespec_to_msec(&now);
+
+ buffer = ds_swapchain_acquire(server->swapchain, NULL);
+ assert(buffer);
+
+ assert(ds_buffer_begin_data_ptr_access(buffer,
+ 0, &data, &format, &stride) == true);
+
+ paint_pixels(data, 20, server->width, server->height, frame_time_msec);
+
+ ds_buffer_end_data_ptr_access(buffer);
+
+ ds_output_attach_buffer(server->output, buffer);
+ ds_output_commit(server->output);
+
+ if (server->front_buffer)
+ ds_buffer_unlock(server->front_buffer);
+
+ server->front_buffer = buffer;
+}
--- /dev/null
+#ifndef LIBDS_ALLOCATOR_H
+#define LIBDS_ALLOCATOR_H
+
+#include <libds/buffer.h>
+
+struct ds_allocator;
+
+void
+ds_allocator_destroy(struct ds_allocator *alloc);
+
+struct ds_buffer *
+ds_allocator_create_buffer(struct ds_allocator *alloc, int width, int height,
+ uint32_t format);
+
+void
+ds_allocator_add_destroy_listener(struct ds_allocator *alloc,
+ struct wl_listener *listener);
+
+#endif
--- /dev/null
+#ifndef LIBDS_ALLOCATOR_SHM_H
+#define LIBDS_ALLOCATOR_SHM_H
+
+#include <libds/allocator.h>
+
+struct ds_allocator *
+ds_shm_allocator_create(void);
+
+#endif
--- /dev/null
+#ifndef LIBDS_BACKEND_H
+#define LIBDS_BACKEND_H
+
+#include <stdbool.h>
+#include <wayland-server.h>
+#include <libds/buffer.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ds_backend;
+
+bool
+ds_backend_start(struct ds_backend *backend);
+
+void
+ds_backend_destroy(struct ds_backend *backend);
+
+int
+ds_backend_get_drm_fd(struct ds_backend *backend);
+
+ds_buffer_caps_t
+ds_backend_get_buffer_caps(struct ds_backend *backend);
+
+void
+ds_backend_add_destroy_listener(struct ds_backend *backend,
+ struct wl_listener *listener);
+
+void
+ds_backend_add_new_output_listener(struct ds_backend *backend,
+ struct wl_listener *listener);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+#ifndef LIBDS_BACKEND_WAYLAND_H
+#define LIBDS_BACKEND_WAYLAND_H
+
+#include <libds/backend.h>
+#include <libds/output.h>
+
+struct ds_backend *
+ds_wl_backend_create(struct wl_display *display, const char *server_name);
+
+struct ds_output *
+ds_wl_backend_create_output(struct ds_backend *backend);
+
+#endif
--- /dev/null
+#ifndef LIBDS_BUFFER_H
+#define LIBDS_BUFFER_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <wayland-server.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ds_buffer;
+
+typedef enum
+{
+ DS_BUFFER_CAP_DATA_PTR = 1 << 0,
+ DS_BUFFER_CAP_DMABUF = 1 << 1,
+ DS_BUFFER_CAP_SHM = 1 << 2,
+} ds_buffer_caps_t;
+
+enum ds_buffer_data_ptr_access_flag
+{
+ DS_BUFFER_DATA_PTR_ACCESS_READ = 1 << 0,
+ DS_BUFFER_DATA_PTR_ACCESS_WRITE = 1 << 1,
+};
+
+struct ds_shm_attributes
+{
+ int fd;
+ uint32_t format;
+ int width, height, stride;
+ off_t offset;
+};
+
+struct ds_buffer *
+ds_buffer_from_resource(struct wl_resource *resource);
+
+void
+ds_buffer_drop(struct ds_buffer *buffer);
+
+struct ds_buffer *
+ds_buffer_lock(struct ds_buffer *buffer);
+
+void
+ds_buffer_unlock(struct ds_buffer *buffer);
+
+bool
+ds_buffer_get_shm(struct ds_buffer *buffer, struct ds_shm_attributes *attribs);
+
+void
+ds_buffer_get_size(struct ds_buffer *buffer, int *out_width, int *out_height);
+
+void
+ds_buffer_add_destroy_listener(struct ds_buffer *buffer,
+ struct wl_listener *listener);
+
+void
+ds_buffer_add_release_listener(struct ds_buffer *buffer,
+ struct wl_listener *listener);
+
+bool
+ds_buffer_begin_data_ptr_access(struct ds_buffer *buffer,
+ enum ds_buffer_data_ptr_access_flag flags, void **data,
+ uint32_t *format, size_t *stride);
+
+void
+ds_buffer_end_data_ptr_access(struct ds_buffer *buffer);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+#ifndef LIBDS_COMPOSITOR_H
+#define LIBDS_COMPOSITOR_H
+
+#include <wayland-server.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ds_compositor;
+
+struct ds_compositor *
+ds_compositor_create(struct wl_display *display);
+
+void
+ds_compositor_add_new_surface_listener(struct ds_compositor *compositor,
+ struct wl_listener *listener);
+
+void
+ds_compositor_add_destroy_listener(struct ds_compositor *compositor,
+ struct wl_listener *listener);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+#ifndef LIBDS_INTERFACES_ALLOCATOR_H
+#define LIBDS_INTERFACES_ALLOCATOR_H
+
+#include <wayland-server.h>
+#include <libds/buffer.h>
+
+struct ds_allocator;
+
+struct ds_allocator_interface {
+ struct ds_buffer *(*create_buffer)(struct ds_allocator *alloc,
+ int width, int height, uint32_t format);
+ void (*destroy)(struct ds_allocator *alloc);
+};
+
+struct ds_allocator
+{
+ const struct ds_allocator_interface *iface;
+
+ uint32_t buffer_caps;
+
+ struct {
+ struct wl_signal destroy;
+ } events;
+};
+
+void
+ds_allocator_init(struct ds_allocator *alloc,
+ const struct ds_allocator_interface *iface, uint32_t buffer_caps);
+
+void
+ds_allocator_destroy(struct ds_allocator *alloc);
+
+#endif
--- /dev/null
+#ifndef LIBDS_INTERFACES_BACKEND_H
+#define LIBDS_INTERFACES_BACKEND_H
+
+#include <wayland-server.h>
+
+struct ds_backend;
+
+struct ds_backend_interface
+{
+ bool (*start)(struct ds_backend *backend);
+ void (*destroy)(struct ds_backend *backend);
+ int (*get_drm_fd)(struct ds_backend *backend);
+};
+
+struct ds_backend
+{
+ const struct ds_backend_interface *iface;
+
+ struct wl_display *display;
+
+ struct
+ {
+ struct wl_signal destroy;
+ struct wl_signal new_output;
+ } events;
+
+ bool started;
+};
+
+void
+ds_backend_init(struct ds_backend *backend, const struct ds_backend_interface *iface);
+
+void
+ds_backend_finish(struct ds_backend *backend);
+
+#endif
--- /dev/null
+#ifndef LIBDS_INTERFACES_BUFFER_H
+#define LIBDS_INTERFACES_BUFFER_H
+
+#include <wayland-server.h>
+#include <libds/buffer.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ds_buffer;
+
+struct ds_buffer_interface
+{
+ void (*destroy)(struct ds_buffer *buffer);
+ bool (*get_shm)(struct ds_buffer *buffer,
+ struct ds_shm_attributes *attribs);
+ bool (*begin_data_ptr_access)(struct ds_buffer *buffer, uint32_t flags,
+ void **data, uint32_t *format, size_t *stride);
+ void (*end_data_ptr_access)(struct ds_buffer *buffer);
+};
+
+struct ds_buffer
+{
+ const struct ds_buffer_interface *iface;
+ void *iface_data;
+
+ int width, height;
+ size_t n_locks;
+
+ bool dropped;
+ bool accessing_data_ptr;
+
+ struct {
+ struct wl_signal destroy;
+ struct wl_signal release;
+ } events;
+};
+
+void
+ds_buffer_init(struct ds_buffer *buffer,
+ const struct ds_buffer_interface *iface, int width, int height);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+#ifndef LIBDS_INTERFACES_OUTPUT_H
+#define LIBDS_INTERFACES_OUTPUT_H
+
+#include <stdint.h>
+#include <wayland-server.h>
+#include <libds/backend.h>
+#include <libds/buffer.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ds_output;
+
+enum ds_output_state_field
+{
+ DS_OUTPUT_STATE_BUFFER = 1 << 0,
+ DS_OUTPUT_STATE_DAMAGE = 1 << 1,
+ DS_OUTPUT_STATE_MODE = 1 << 2,
+ DS_OUTPUT_STATE_ENABLED = 1 << 3,
+ DS_OUTPUT_STATE_SCALE = 1 << 4,
+ DS_OUTPUT_STATE_TRANSFORM = 1 << 5,
+};
+
+struct ds_output_interface
+{
+ void (*destroy)(struct ds_output *output);
+ bool (*commit)(struct ds_output *output);
+};
+
+struct ds_output_mode {
+ struct wl_list link;
+ int32_t width, height;
+ int32_t refresh;
+ bool preferred;
+};
+
+struct ds_output_state
+{
+ enum ds_output_state_field committed;
+ struct ds_buffer *buffer;
+ struct ds_output_mode *mode;
+
+ bool enabled;
+};
+
+struct ds_output
+{
+ const struct ds_output_interface *iface;
+
+ struct ds_backend *backend;
+
+ struct wl_display *display;
+ struct wl_global *global;
+
+ struct ds_buffer *back_buffer, *front_buffer;
+ struct ds_output_state pending;
+
+ struct wl_listener display_destroy;
+
+ struct {
+ struct wl_signal destroy;
+ struct wl_signal frame;
+ struct wl_signal commit;
+ } events;
+};
+
+void
+ds_output_init(struct ds_output *output, struct ds_backend *backend,
+ const struct ds_output_interface *iface, struct wl_display *display);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+#ifndef LIBDS_LOG_H
+#define LIBDS_LOG_H
+
+#include <stdbool.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum ds_log_level {
+ DS_SILENT = 0,
+ DS_ERR = 1,
+ DS_INF = 2,
+ DS_DBG = 3,
+ DS_LOG_LEVEL_LAST,
+};
+
+typedef void (*ds_log_func_t)(enum ds_log_level level,
+ const char *fmt, va_list args);
+
+void ds_log_init(enum ds_log_level level, ds_log_func_t callback);
+
+enum ds_log_level ds_log_get_level(void);
+
+#ifdef __GNUC__
+#define _DS_ATTRIB_PRINTF(start, end) __attribute__((format(printf, start, end)))
+#else
+#define _DS_ATTRIB_PRINTF(start, end)
+#endif
+
+void _ds_log(enum ds_log_level level, const char *format, ...) _DS_ATTRIB_PRINTF(2, 3);
+void _ds_vlog(enum ds_log_level level, const char *format, va_list args) _DS_ATTRIB_PRINTF(2, 0);
+
+#define ds_log(level, fmt, ...) \
+ _ds_log(level, "[%s:%d] " fmt, __FILE__, __LINE__, ##__VA_ARGS__)
+
+#define ds_vlog(level, fmt, args) \
+ _ds_vlog(level, "[%s:%d] " fmt, __FILE__, __LINE__, args)
+
+#define ds_log_errno(level, fmt, ...) \
+ ds_log(level, fmt ": %s", ##__VA_ARGS__, strerror(errno))
+
+#define ds_err(fmt, ...) \
+ ds_log(DS_ERR, fmt, ##__VA_ARGS__)
+
+#define ds_inf(fmt, ...) \
+ ds_log(DS_INF, fmt, ##__VA_ARGS__)
+
+#define ds_dbg(fmt, ...) \
+ ds_log(DS_DBG, fmt, ##__VA_ARGS__)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+#ifndef LIBDS_OUTPUT_H
+#define LIBDS_OUTPUT_H
+
+#include <time.h>
+#include <libds/buffer.h>
+
+struct ds_output;
+
+void
+ds_output_destroy(struct ds_output *output);
+
+bool
+ds_output_commit(struct ds_output *output);
+
+void
+ds_output_attach_buffer(struct ds_output *output, struct ds_buffer *buffer);
+
+void
+ds_output_add_destroy_listener(struct ds_output *output,
+ struct wl_listener *listener);
+
+void
+ds_output_add_frame_listener(struct ds_output *output,
+ struct wl_listener *listener);
+
+void
+ds_output_add_commit_listener(struct ds_output *output,
+ struct wl_listener *listener);
+
+#endif
--- /dev/null
+#ifndef LIBstruct ds_presentationIME_H
+#define LIBstruct ds_presentationIME_H
+
+#include <time.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ds_presentation;
+
+struct ds_presentation *
+ds_presentation_create(struct wl_display *display,
+ clockid_t clk_id);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+#ifndef LIBDS_SUBSURFACE_H
+#define LIBDS_SUBSURFACE_H
+
+#include <libds/surface.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ds_subsurface;
+
+bool ds_surface_is_subsurface(struct ds_surface *surface);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+#ifndef LIBDS_SURFACE_H
+#define LIBDS_SURFACE_H
+
+#include <time.h>
+#include <wayland-server.h>
+
+#include <libds/buffer.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ds_surface;
+
+typedef bool (*ds_surface_for_each_func_t)(struct ds_surface *surface,
+ int sx, int sy, void *data);
+
+void
+ds_surface_add_destroy_listener(struct ds_surface *surface,
+ struct wl_listener *listener);
+
+void
+ds_surface_add_commit_listener(struct ds_surface *surface,
+ struct wl_listener *listener);
+
+void
+ds_surface_add_new_subsurface_listener(struct ds_surface *surface,
+ struct wl_listener *listener);
+
+struct ds_buffer *
+ds_surface_get_buffer(struct ds_surface *surface);
+
+void
+ds_surface_for_each(struct ds_surface *surface,
+ ds_surface_for_each_func_t iterator, void *data);
+
+void
+ds_surface_send_frame_done(struct ds_surface *surface,
+ const struct timespec *when);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+#ifndef LIBDS_SWAPCHAIN_H
+#define LIBDS_SWAPCHAIN_H
+
+#include <libds/allocator.h>
+
+struct ds_swapchain;
+
+struct ds_swapchain *
+ds_swapchain_create(struct ds_allocator *alloc, int width, int height,
+ uint32_t format);
+
+void
+ds_swapchain_destroy(struct ds_swapchain *swapchain);
+
+struct ds_buffer *
+ds_swapchain_acquire(struct ds_swapchain *swapchain, int *age);
+
+void
+ds_swapchain_set_buffer_submitted(struct ds_swapchain *swapchain,
+ struct ds_buffer *buffer);
+
+#endif
--- /dev/null
+#ifndef LIBDS_XDG_SHELL_H
+#define LIBDS_XDG_SHELL_H
+
+#include <stdint.h>
+#include <wayland-server.h>
+
+#include "surface.h"
+
+struct ds_xdg_shell;
+
+struct ds_xdg_surface;
+
+struct ds_xdg_shell *
+ds_xdg_shell_create(struct wl_display *display);
+
+void
+ds_xdg_shell_add_destroy_listener(struct ds_xdg_shell *shell,
+ struct wl_listener *listener);
+
+void
+ds_xdg_shell_add_new_surface_listener(struct ds_xdg_shell *shell,
+ struct wl_listener *listener);
+
+void
+ds_xdg_surface_add_destroy_listener(struct ds_xdg_surface *surface,
+ struct wl_listener *listener);
+
+void
+ds_xdg_surface_add_map_listener(struct ds_xdg_surface *surface,
+ struct wl_listener *listener);
+
+void
+ds_xdg_surface_add_unmap_listener(struct ds_xdg_surface *surface,
+ struct wl_listener *listener);
+
+void
+ds_xdg_surface_ping(struct ds_xdg_surface *surface);
+
+struct ds_surface *
+ds_xdg_surface_get_surface(struct ds_xdg_surface *surface);
+
+uint32_t
+ds_xdg_toplevel_set_size(struct ds_xdg_surface *surface,
+ uint32_t width, uint32_t height);
+
+uint32_t
+ds_xdg_toplevel_set_activated(struct ds_xdg_surface *surface,
+ bool activated);
+
+#endif
--- /dev/null
+install_subdir('libds',
+ install_dir: get_option('includedir')
+)
--- /dev/null
+project('libds', 'c',
+ license: 'MIT',
+ version: '0.1.0',
+ default_options: [
+ 'warning_level=1',
+ 'c_std=gnu99',
+ 'buildtype=debug'
+ ]
+)
+
+libds_version = meson.project_version()
+version_arr = libds_version.split('.')
+libds_version_major = version_arr[0]
+libds_version_minor = version_arr[1]
+libds_version_patch = version_arr[2]
+
+dir_prefix = get_option('prefix')
+
+libds_inc = include_directories('include')
+common_inc = [ include_directories('.'), libds_inc ]
+
+cdata = configuration_data()
+cdata.set('LIBDS_VERSION_MAJOR', libds_version_major)
+cdata.set('LIBDS_VERSION_MINOR', libds_version_minor)
+cdata.set('LIBDS_VERSION_PATCH', libds_version_patch)
+
+subdir('include')
+subdir('src')
+
+configure_file(output: 'config.h', install: false, configuration: cdata)
+
+pkgconfig = import('pkgconfig')
+pkgconfig.generate(lib_libds,
+ version: meson.project_version(),
+ filebase: meson.project_name(),
+ name: meson.project_name(),
+ description: 'Wayland compositor library',
+)
--- /dev/null
+<manifest>
+ <request>
+ <domain name="_"/>
+ </request>
+</manifest>
--- /dev/null
+Name: libds
+Version: 0.0.1
+Release: 0
+Summary: Wayland Compositor Library
+License: MIT
+URL: http://www.tizen.org/
+Source: %name-%version.tar.xz
+Source1001: %name.manifest
+
+BuildRequires: meson
+BuildRequires: pkgconfig(wayland-server)
+BuildRequires: pkgconfig(wayland-client)
+BuildRequires: pkgconfig(wayland-protocols)
+BuildRequires: pkgconfig(pixman-1)
+BuildRequires: pkgconfig(libdrm)
+
+%description
+Wayland Compositor Library
+
+%package devel
+Summary: Wayland Compositor Library
+Group: Development/Libraries
+Requires: %{name} = %{version}-%{release}
+
+%description devel
+Development package of Wayland Compositor Library
+
+%prep
+%setup -q
+cp %{SOURCE1001} .
+
+%build
+meson setup \
+ --prefix /usr \
+ --libdir %{_libdir} \
+ --bindir %{_bindir} \
+ builddir
+ninja -C builddir all
+
+%install
+export DESTDIR=%{buildroot}
+ninja -C builddir install
+
+%files
+%manifest %{name}.manifest
+%defattr(-,root,root,-)
+%license LICENSE
+%{_libdir}/*.so.*
+
+%files devel
+%manifest %{name}.manifest
+%defattr(-,root,root,-)
+%license LICENSE
+%{_includedir}/*
+%{_libdir}/pkgconfig/libds.pc
+%{_libdir}/libds.so
--- /dev/null
+#include <assert.h>
+
+#include "addon.h"
+
+void
+ds_addon_set_init(struct ds_addon_set *set)
+{
+ wl_list_init(&set->addons);
+}
+
+void
+ds_addon_set_finish(ds_addon_set_t *set)
+{
+ ds_addon_t *addon, *tmp;
+
+ wl_list_for_each_safe(addon, tmp, &set->addons, link) {
+ ds_addon_finish(addon);
+ addon->impl->destroy(addon);
+ }
+}
+
+void
+ds_addon_init(ds_addon_t *addon, ds_addon_set_t *set,
+ const void *owner, const ds_addon_interface_t *impl)
+{
+ ds_addon_t *iter;
+
+ assert(owner && impl);
+
+ wl_list_for_each(iter, &set->addons, link) {
+ if (iter->owner == addon->owner && iter->impl == addon->impl)
+ assert(0 && "Can't have two addons of the same type with the same owner");
+ }
+
+ wl_list_insert(&set->addons, &addon->link);
+
+ addon->owner = owner;
+ addon->impl = impl;
+}
+
+void
+ds_addon_finish(ds_addon_t *addon)
+{
+ wl_list_remove(&addon->link);
+ wl_list_init(&addon->link);
+}
+
+ds_addon_t *
+ds_addon_find(ds_addon_set_t *set, const void *owner,
+ const ds_addon_interface_t *impl)
+{
+ struct ds_addon *addon;
+
+ wl_list_for_each(addon, &set->addons, link) {
+ if (addon->owner == owner && addon->impl == impl)
+ return addon;
+ }
+
+ return NULL;
+}
--- /dev/null
+#ifndef DS_ADDON_H
+#define DS_ADDON_H
+
+#include <wayland-util.h>
+
+typedef struct ds_addon_set ds_addon_set_t;
+typedef struct ds_addon_interface ds_addon_interface_t;
+typedef struct ds_addon ds_addon_t;
+
+struct ds_addon_set {
+ struct wl_list addons;
+};
+
+struct ds_addon_interface {
+ const char *name;
+ void (*destroy)(struct ds_addon *addon);
+};
+
+struct ds_addon {
+ const ds_addon_interface_t *impl;
+ const void *owner;
+ struct wl_list link;
+};
+
+void
+ds_addon_set_init(ds_addon_set_t *set);
+
+void
+ds_addon_set_finish(ds_addon_set_t *set);
+
+void
+ds_addon_init(ds_addon_t *addon, ds_addon_set_t *set,
+ const void *owner, const ds_addon_interface_t *impl);
+
+void
+ds_addon_finish(ds_addon_t *addon);
+
+ds_addon_t *
+ds_addon_find(ds_addon_set_t *set, const void *owner,
+ const ds_addon_interface_t *impl);
+
+#endif
--- /dev/null
+#include <assert.h>
+#include <stdlib.h>
+#include <wayland-server.h>
+
+#include "libds/log.h"
+#include "libds/interfaces/allocator.h"
+
+WL_EXPORT void
+ds_allocator_init(struct ds_allocator *alloc,
+ const struct ds_allocator_interface *iface, uint32_t buffer_caps)
+{
+ alloc->iface = iface;
+ alloc->buffer_caps = buffer_caps;
+
+ wl_signal_init(&alloc->events.destroy);
+}
+
+WL_EXPORT void
+ds_allocator_destroy(struct ds_allocator *alloc)
+{
+ wl_signal_emit(&alloc->events.destroy, NULL);
+ alloc->iface->destroy(alloc);
+}
+
+WL_EXPORT struct ds_buffer *
+ds_allocator_create_buffer(struct ds_allocator *alloc, int width, int height,
+ uint32_t format)
+{
+ return alloc->iface->create_buffer(alloc, width, height, format);
+}
+
+WL_EXPORT void
+ds_allocator_add_destroy_listener(struct ds_allocator *alloc,
+ struct wl_listener *listener)
+{
+ wl_signal_add(&alloc->events.destroy, listener);
+}
--- /dev/null
+#include <assert.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <drm_fourcc.h>
+#include <wayland-server.h>
+
+#include "libds/allocator/shm.h"
+#include "libds/interfaces/allocator.h"
+#include "libds/interfaces/buffer.h"
+#include "libds/log.h"
+#include "util.h"
+
+typedef struct ds_shm_allocator ds_shm_allocator_t;
+
+typedef struct ds_shm_buffer ds_shm_buffer_t;
+
+struct ds_shm_allocator
+{
+ struct ds_allocator base;
+};
+
+struct ds_shm_buffer
+{
+ struct ds_buffer base;
+ struct ds_shm_attributes shm;
+ void *data;
+ size_t size;
+};
+
+static const struct ds_allocator_interface shm_allocator_iface;
+
+WL_EXPORT struct ds_allocator *
+ds_shm_allocator_create(void)
+{
+ ds_shm_allocator_t *alloc;
+
+ alloc = calloc(1, sizeof *alloc);
+ if (!alloc)
+ return NULL;
+
+ ds_allocator_init(&alloc->base, &shm_allocator_iface,
+ DS_BUFFER_CAP_DATA_PTR | DS_BUFFER_CAP_SHM);
+
+ ds_dbg("Shm allocator(%p) created", alloc);
+
+ return &alloc->base;
+}
+
+static ds_shm_allocator_t *
+shm_allocator_from_allocator(struct ds_allocator *ds_allocator)
+{
+ assert(ds_allocator->iface == &shm_allocator_iface);
+ return (ds_shm_allocator_t *)ds_allocator;
+}
+
+static void
+shm_allocator_destroy(struct ds_allocator *ds_allocator)
+{
+ ds_shm_allocator_t *alloc;
+
+ alloc = shm_allocator_from_allocator(ds_allocator);
+ ds_dbg("Destroy Shm allocator(%p)", alloc);
+ free(alloc);
+}
+
+static const struct ds_buffer_interface shm_buffer_interface;
+
+static ds_shm_buffer_t *
+shm_buffer_from_buffer(struct ds_buffer *buffer)
+{
+ assert(buffer->iface == &shm_buffer_interface);
+ return (ds_shm_buffer_t *)buffer;
+}
+
+static void
+shm_buffer_destroy(struct ds_buffer *ds_buffer)
+{
+ ds_shm_buffer_t *buffer;
+
+ buffer = shm_buffer_from_buffer(ds_buffer);
+
+ ds_dbg("Destroy shm buffer(%p)", buffer);
+
+ munmap(buffer->data, buffer->size);
+ close(buffer->shm.fd);
+ free(buffer);
+}
+
+static bool
+shm_buffer_get_shm(struct ds_buffer *ds_buffer, struct ds_shm_attributes *shm)
+{
+ ds_shm_buffer_t *buffer;
+
+ buffer = shm_buffer_from_buffer(ds_buffer);
+ memcpy(shm, &buffer->shm, sizeof *shm);
+ return true;
+}
+
+static bool
+shm_buffer_begin_data_ptr_access(struct ds_buffer *ds_buffer, uint32_t flags,
+ void **data, uint32_t *format, size_t *stride)
+{
+ ds_shm_buffer_t *buffer;
+
+ buffer = shm_buffer_from_buffer(ds_buffer);
+ *data = buffer->data;
+ *format = buffer->shm.format;
+ *stride = buffer->shm.stride;
+ return true;
+}
+
+static void
+shm_buffer_end_data_ptr_access(struct ds_buffer *buffer)
+{
+ (void) buffer;
+
+ // This space is intentionally left blank
+}
+
+static const struct ds_buffer_interface shm_buffer_interface =
+{
+ .destroy = shm_buffer_destroy,
+ .get_shm = shm_buffer_get_shm,
+ .begin_data_ptr_access = shm_buffer_begin_data_ptr_access,
+ .end_data_ptr_access = shm_buffer_end_data_ptr_access,
+};
+
+static struct ds_buffer *
+shm_allocator_create_buffer(struct ds_allocator *ds_allocator,
+ int width, int height, uint32_t format)
+{
+ ds_shm_buffer_t *buffer;
+
+ buffer = calloc(1, sizeof *buffer);
+ if (!buffer)
+ return NULL;
+
+ ds_buffer_init(&buffer->base, &shm_buffer_interface, width, height);
+
+ // FIXME
+ int bytes_per_pixel = 4;
+ int stride = width * bytes_per_pixel;
+ buffer->size = stride * height;
+ buffer->shm.fd = allocate_shm_file(buffer->size);
+ if (buffer->shm.fd < 0) {
+ free(buffer);
+ return NULL;
+ }
+
+ buffer->shm.format = format;
+ buffer->shm.width = width;
+ buffer->shm.height = height;
+ buffer->shm.stride = stride;
+ buffer->shm.offset = 0;
+
+ buffer->data = mmap(NULL, buffer->size, PROT_READ | PROT_WRITE, MAP_SHARED,
+ buffer->shm.fd, 0);
+ if (buffer->data == MAP_FAILED) {
+ ds_log_errno(DS_ERR, "mmap failed");
+ close(buffer->shm.fd);
+ free(buffer);
+ return NULL;
+ }
+
+ ds_dbg("Shm buffer(%p) created: size(%dx%d)", buffer, width, height);
+
+ return &buffer->base;
+}
+
+static const struct ds_allocator_interface shm_allocator_iface = {
+ .destroy = shm_allocator_destroy,
+ .create_buffer = shm_allocator_create_buffer,
+};
--- /dev/null
+#include <stdlib.h>
+#include <wayland-server.h>
+
+#include "libds/interfaces/backend.h"
+
+WL_EXPORT bool
+ds_backend_start(struct ds_backend *backend)
+{
+ if (backend->iface->start)
+ return backend->iface->start(backend);
+
+ backend->started = true;
+
+ return true;
+}
+
+WL_EXPORT void
+ds_backend_destroy(struct ds_backend *backend)
+{
+ if (!backend)
+ return;
+
+ if (backend->iface && backend->iface->destroy)
+ backend->iface->destroy(backend);
+ else
+ free(backend);
+}
+
+WL_EXPORT void
+ds_backend_add_destroy_listener(struct ds_backend *backend,
+ struct wl_listener *listener)
+{
+ wl_signal_add(&backend->events.destroy, listener);
+}
+
+WL_EXPORT void
+ds_backend_add_new_output_listener(struct ds_backend *backend,
+ struct wl_listener *listener)
+{
+ wl_signal_add(&backend->events.new_output, listener);
+}
+
+void
+ds_backend_init(struct ds_backend *backend, const struct ds_backend_interface *iface)
+{
+ backend->iface = iface;
+ wl_signal_init(&backend->events.destroy);
+ wl_signal_init(&backend->events.new_output);
+}
+
+void
+ds_backend_finish(struct ds_backend *backend)
+{
+ wl_signal_emit(&backend->events.destroy, backend);
+}
--- /dev/null
+libds_files += files(
+ 'backend.c',
+)
+
+subdir('wayland')
--- /dev/null
+#include <assert.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <wayland-server.h>
+#include <wayland-client.h>
+
+#include "backend.h"
+#include "libds/log.h"
+#include "libds/output.h"
+
+#include "xdg-shell-client-protocol.h"
+
+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(ds_wl_backend_server_t *server,
+ const char *name);
+static void wl_backend_server_finish(ds_wl_backend_server_t *server);
+static int wl_backend_handle_dispatch_events(int fd, uint32_t mask,
+ void *data);
+
+WL_EXPORT struct ds_backend *
+ds_wl_backend_create(struct wl_display *display, const char *server_name)
+{
+ ds_wl_backend_t *wl_backend;
+ struct wl_event_loop *loop;
+ int fd;
+
+ wl_backend = calloc(1, sizeof *wl_backend);
+ if (!wl_backend) {
+ ds_log_errno(DS_ERR, "Could not allocate memory");
+ return NULL;
+ }
+
+ ds_backend_init(&wl_backend->base, &wl_backend_interface);
+
+ wl_backend->display = display;
+ wl_list_init(&wl_backend->buffers);
+ wl_list_init(&wl_backend->outputs);
+
+ if (!wl_backend_server_init(&wl_backend->server, server_name)) {
+ ds_err("Failed to initialize Wayland Server");
+ goto err_server;
+ }
+
+ loop = wl_display_get_event_loop(wl_backend->display);
+ 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_backend_handle_dispatch_events, wl_backend);
+ if (!wl_backend->server_event_source) {
+ ds_err("Failed to create event source");
+ goto err_src;
+ }
+
+ wl_backend->display_destroy.notify = wl_backend_handle_display_destroy;
+ wl_display_add_destroy_listener(display, &wl_backend->display_destroy);
+
+ ds_inf("Wayland backend(%p) created: server name \"%s\"",
+ wl_backend, server_name);
+
+ return &wl_backend->base;
+
+err_src:
+ wl_backend_server_finish(&wl_backend->server);
+err_server:
+ ds_backend_finish(&wl_backend->base);
+ free(wl_backend);
+
+ return NULL;
+}
+
+ds_wl_backend_t *
+wl_backend_from_backend(struct ds_backend *backend)
+{
+ assert(backend->iface == &wl_backend_interface);
+ return (ds_wl_backend_t *)backend;
+}
+
+static void
+wl_backend_destroy(ds_wl_backend_t *backend)
+{
+ ds_wl_output_t *output, *tmp_output;
+ ds_wl_buffer_t *buffer, *tmp_buffer;
+
+ ds_dbg("Destroy wayland backend(%p)", backend);
+
+ wl_list_for_each_safe(output, tmp_output, &backend->outputs, link) {
+ ds_output_destroy(&output->base);
+ }
+
+ wl_list_for_each_safe(buffer, tmp_buffer, &backend->buffers, link) {
+ destroy_wl_buffer(buffer);
+ }
+
+ ds_backend_finish(&backend->base);
+
+ wl_list_remove(&backend->display_destroy.link);
+
+ wl_event_source_remove(backend->server_event_source);
+
+ wl_backend_server_finish(&backend->server);
+
+ free(backend);
+}
+
+static void
+wl_backend_iface_destroy(struct ds_backend *backend)
+{
+ ds_wl_backend_t *wl_backend;
+
+ if (!backend)
+ return;
+
+ wl_backend = wl_backend_from_backend(backend);
+
+ wl_backend_destroy(wl_backend);
+}
+
+static const struct ds_backend_interface wl_backend_interface = {
+ .start = NULL,
+ .destroy = wl_backend_iface_destroy,
+ .get_drm_fd = NULL,
+};
+
+static void
+wl_backend_handle_display_destroy(struct wl_listener *listener, void *data)
+{
+ ds_wl_backend_t *wl_backend;
+
+ wl_backend = wl_container_of(listener, wl_backend, display_destroy);
+ wl_backend_destroy(wl_backend);
+}
+
+static void
+shm_handle_format(void *data, struct wl_shm *shm, uint32_t shm_format)
+{
+}
+
+static const struct wl_shm_listener shm_listener =
+{
+ .format = shm_handle_format,
+};
+
+static void
+xdg_wm_base_handle_ping(void *data, struct xdg_wm_base *base, uint32_t serial)
+{
+ xdg_wm_base_pong(base, serial);
+}
+
+static const struct xdg_wm_base_listener xdg_wm_base_listener =
+{
+ .ping = xdg_wm_base_handle_ping,
+};
+
+static void
+registry_handle_global(void *data, struct wl_registry *registry,
+ uint32_t name, const char *iface, uint32_t version)
+{
+ ds_wl_backend_server_t *server = data;
+
+ ds_log(DS_DBG, "Wayland global: %s v%d", iface, version);
+
+ if (strcmp(iface, wl_compositor_interface.name) == 0) {
+ server->compositor = wl_registry_bind(registry, name,
+ &wl_compositor_interface, 4);
+ }
+ else if (strcmp(iface, xdg_wm_base_interface.name) == 0) {
+ server->xdg_wm_base = wl_registry_bind(registry, name,
+ &xdg_wm_base_interface, 1);
+ xdg_wm_base_add_listener(server->xdg_wm_base,
+ &xdg_wm_base_listener, NULL);
+ }
+ else if (strcmp(iface, wl_shm_interface.name) == 0) {
+ server->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
+ wl_shm_add_listener(server->shm, &shm_listener, server);
+ }
+}
+
+static void
+registry_handle_global_remove(void *data, struct wl_registry *registry,
+ uint32_t name)
+{
+ // TODO
+}
+
+static const struct wl_registry_listener registry_listener = {
+ .global = registry_handle_global,
+ .global_remove = registry_handle_global_remove,
+};
+
+static bool
+wl_backend_server_init(ds_wl_backend_server_t *server, const char *name)
+{
+ server->display = wl_display_connect(name);
+ if (!server->display) {
+ ds_log_errno(DS_ERR, "Could not connect to display: name \"%s\"", name);
+ return false;
+ }
+
+ server->registry = wl_display_get_registry(server->display);
+ if (!server->registry) {
+ ds_log_errno(DS_ERR, "Could not get wl_registry");
+ goto err_reg;
+ }
+
+ wl_registry_add_listener(server->registry, ®istry_listener, server);
+
+ wl_display_roundtrip(server->display);
+
+ if (!server->compositor) {
+ ds_err("Wayland Server does not support wl_compositor");
+ goto err_bind;
+ }
+
+ if (!server->xdg_wm_base) {
+ ds_err("Wayland Server does not support xdg_wm_base");
+ goto err_bind;
+ }
+
+ return true;
+
+err_bind:
+ if (server->compositor)
+ wl_compositor_destroy(server->compositor);
+
+ if (server->xdg_wm_base)
+ xdg_wm_base_destroy(server->xdg_wm_base);
+
+ wl_registry_destroy(server->registry);
+err_reg:
+ wl_display_disconnect(server->display);
+
+ return false;
+}
+
+static void
+wl_backend_server_finish(ds_wl_backend_server_t *server)
+{
+ xdg_wm_base_destroy(server->xdg_wm_base);
+ wl_compositor_destroy(server->compositor);
+ wl_registry_destroy(server->registry);
+ wl_display_disconnect(server->display);
+}
+
+static int
+wl_backend_handle_dispatch_events(int fd, uint32_t mask, void *data)
+{
+ ds_wl_backend_t *wl_backend = data;
+ ds_wl_backend_server_t *server = &wl_backend->server;
+ int count = 0;
+
+ if ((mask & WL_EVENT_HANGUP) || (mask & WL_EVENT_ERROR)) {
+ if (mask & WL_EVENT_ERROR) {
+ ds_err("Failed to read from Wayland Server");
+ }
+ wl_display_terminate(wl_backend->display);
+ return 0;
+ }
+
+ count = 0;
+ if (mask & WL_EVENT_READABLE)
+ count = wl_display_dispatch(server->display);
+
+ if (mask & WL_EVENT_WRITABLE)
+ wl_display_flush(server->display);
+
+ if (mask == 0) {
+ count = wl_display_dispatch_pending(server->display);
+ wl_display_flush(server->display);
+ }
+
+ if (count < 0) {
+ ds_err("Failed to dispatch Wayland Server");
+ wl_display_terminate(wl_backend->display);
+ return 0;
+ }
+
+ return count;
+}
--- /dev/null
+#ifndef DS_BACKEND_WAYLAND_H
+#define DS_BACKEND_WAYLAND_H
+
+#include "libds/interfaces/backend.h"
+#include "libds/interfaces/output.h"
+
+typedef struct ds_wl_backend_server ds_wl_backend_server_t;
+
+typedef struct ds_wl_backend ds_wl_backend_t;
+
+typedef struct ds_wl_buffer ds_wl_buffer_t;
+
+typedef struct ds_wl_output ds_wl_output_t;
+
+struct ds_wl_backend_server
+{
+ struct wl_display *display;
+ struct wl_registry *registry;
+ struct wl_compositor *compositor;
+ struct xdg_wm_base *xdg_wm_base;
+ struct wl_event_source *event_source;
+ struct wl_shm *shm;
+};
+
+struct ds_wl_backend
+{
+ struct ds_backend base;
+
+ struct wl_display *display;
+ struct wl_listener display_destroy;
+
+ struct wl_list outputs; // ds_wl_output.link
+ struct wl_list buffers; // ds_wl_buffer.link
+
+ struct wl_event_source *server_event_source;
+ ds_wl_backend_server_t server;
+};
+
+struct ds_wl_buffer
+{
+ struct ds_buffer *buffer;
+ struct wl_buffer *wl_buffer;
+ struct wl_list link; // ds_wl_backend.buffers
+ struct wl_listener buffer_destroy;
+
+ bool released;
+};
+
+struct ds_wl_output
+{
+ struct ds_output base;
+
+ ds_wl_backend_t *backend;
+
+ struct wl_surface *surface;
+ struct xdg_surface *xdg_surface;
+ struct xdg_toplevel *xdg_toplevel;
+ struct wl_callback *frame_callback;
+
+ struct wl_list link;
+};
+
+ds_wl_backend_t *
+wl_backend_from_backend(struct ds_backend *backend);
+
+void
+destroy_wl_buffer(ds_wl_buffer_t *buffer);
+
+#endif
--- /dev/null
+libds_files += files(
+ 'backend.c',
+ 'output.c',
+)
+
+protocols = {
+ 'xdg-shell': wl_protocol_dir / 'stable/xdg-shell/xdg-shell.xml',
+}
+
+protocols_code = {}
+protocols_client_header = {}
+
+foreach name, path : protocols
+ client_header = custom_target(
+ name.underscorify() + '_client_h',
+ input: path,
+ output: '@BASENAME@-client-protocol.h',
+ command: [wayland_scanner, 'client-header', '@INPUT@', '@OUTPUT@'],
+ build_by_default: false,
+ )
+ libds_files += client_header
+endforeach
+
+libds_deps += [
+ dependency('wayland-client', required: true),
+]
--- /dev/null
+#include <assert.h>
+#include <stdlib.h>
+#include <wayland-client.h>
+
+#include "backend.h"
+#include "libds/log.h"
+#include "libds/output.h"
+
+#include "xdg-shell-client-protocol.h"
+
+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;
+
+struct ds_output *
+ds_wl_backend_create_output(struct ds_backend *ds_backend)
+{
+ ds_wl_backend_t *backend;
+ ds_wl_output_t *output;
+
+ backend = wl_backend_from_backend(ds_backend);
+
+ output = calloc(1, sizeof *output);
+ if (!output) {
+ ds_log_errno(DS_ERR, "Could not allocate ds_wl_output");
+ return NULL;
+ }
+
+ ds_output_init(&output->base, &backend->base, &wl_output_iface,
+ backend->display);
+
+ output->backend = backend;
+
+ output->surface = wl_compositor_create_surface(backend->server.compositor);
+ if (!output->surface) {
+ ds_log_errno(DS_ERR, "Could not create output surface");
+ goto err;
+ }
+ wl_surface_set_user_data(output->surface, output);
+
+ output->xdg_surface =
+ xdg_wm_base_get_xdg_surface(backend->server.xdg_wm_base,
+ output->surface);
+ if (!output->xdg_surface) {
+ ds_log_errno(DS_ERR, "Could not get xdg_surface");
+ goto err;
+ }
+ xdg_surface_add_listener(output->xdg_surface,
+ &wl_output_xdg_surface_listener, output);
+
+ output->xdg_toplevel =
+ xdg_surface_get_toplevel(output->xdg_surface);
+ if (!output->xdg_toplevel) {
+ ds_log_errno(DS_ERR, "Could not get xdg_toplevel");
+ goto err;
+ }
+ xdg_toplevel_add_listener(output->xdg_toplevel,
+ &wl_output_xdg_toplevel_listener, output);
+
+ xdg_toplevel_set_app_id(output->xdg_toplevel, "libds");
+
+ wl_surface_commit(output->surface);
+
+ wl_display_roundtrip(backend->server.display);
+
+ wl_list_insert(&backend->outputs, &output->link);
+
+ wl_signal_emit(&backend->base.events.new_output, &output->base);
+
+ ds_dbg("Wayland output(%p) created", output);
+
+ return &output->base;
+
+err:
+ ds_output_destroy(&output->base);
+
+ return NULL;
+}
+
+void
+destroy_wl_buffer(ds_wl_buffer_t *buffer)
+{
+ if (buffer == NULL)
+ return;
+
+ wl_list_remove(&buffer->buffer_destroy.link);
+ wl_list_remove(&buffer->link);
+ wl_buffer_destroy(buffer->wl_buffer);
+ free(buffer);
+}
+
+static ds_wl_output_t *
+wl_output_from_output(struct ds_output *ds_output)
+{
+ assert(ds_output->iface == &wl_output_iface);
+ return (ds_wl_output_t *)ds_output;
+}
+
+static void
+wl_output_iface_destroy(struct ds_output *ds_output)
+{
+ ds_wl_output_t *output;
+
+ output = wl_output_from_output(ds_output);
+
+ ds_dbg("Destroy wayland output(%p)", output);
+
+ wl_list_remove(&output->link);
+
+ if (output->frame_callback)
+ wl_callback_destroy(output->frame_callback);
+
+ if (output->xdg_toplevel)
+ xdg_toplevel_destroy(output->xdg_toplevel);
+
+ if (output->xdg_surface)
+ xdg_surface_destroy(output->xdg_surface);
+
+ if (output->surface)
+ wl_surface_destroy(output->surface);
+
+ wl_display_flush(output->backend->server.display);
+
+ free(output);
+}
+
+static struct wl_buffer *
+import_shm(ds_wl_backend_t *backend, struct ds_shm_attributes *shm)
+{
+ enum wl_shm_format wl_shm_format = WL_SHM_FORMAT_XRGB8888;
+ struct wl_shm_pool *pool;
+ struct wl_buffer *wl_buffer;
+ uint32_t size;
+
+ size = shm->stride * shm->height;
+ pool = wl_shm_create_pool(backend->server.shm, shm->fd, size);
+ if (!pool)
+ return NULL;
+
+ wl_buffer = wl_shm_pool_create_buffer(pool, shm->offset,
+ shm->width, shm->height, shm->stride, wl_shm_format);
+ wl_shm_pool_destroy(pool);
+
+ return wl_buffer;
+}
+
+static void
+buffer_handle_release(void *data, struct wl_buffer *wl_buffer)
+{
+ ds_wl_buffer_t *buffer = data;
+
+ ds_dbg("Wayland output: Buffer(%p) released.", buffer->buffer);
+ buffer->released = true;
+ ds_buffer_unlock(buffer->buffer);
+}
+
+static const struct wl_buffer_listener buffer_listener = {
+ .release = buffer_handle_release,
+};
+
+static void
+buffer_handle_buffer_destroy(struct wl_listener *listener, void *data)
+{
+ ds_wl_buffer_t *buffer;
+
+ buffer = wl_container_of(listener, buffer, buffer_destroy);
+ destroy_wl_buffer(buffer);
+}
+
+static ds_wl_buffer_t *
+create_wl_buffer(ds_wl_backend_t *backend, struct ds_buffer *ds_buffer)
+{
+ struct ds_shm_attributes shm;
+ ds_wl_buffer_t *buffer;
+ struct wl_buffer *wl_buffer;
+
+ if (ds_buffer_get_shm(ds_buffer, &shm)) {
+ wl_buffer = import_shm(backend, &shm);
+ }
+
+ buffer = calloc(1, sizeof *buffer);
+ if (!buffer) {
+ wl_buffer_destroy(wl_buffer);
+ return NULL;
+ }
+
+ buffer->wl_buffer = wl_buffer;
+ buffer->buffer = ds_buffer_lock(ds_buffer);
+ wl_list_insert(&backend->buffers, &buffer->link);
+
+ wl_buffer_add_listener(wl_buffer, &buffer_listener, buffer);
+
+ buffer->buffer_destroy.notify = buffer_handle_buffer_destroy;
+ ds_buffer_add_destroy_listener(ds_buffer, &buffer->buffer_destroy);
+
+ return buffer;
+}
+
+static ds_wl_buffer_t *
+get_or_create_wl_buffer(ds_wl_backend_t *backend, struct ds_buffer *ds_buffer)
+{
+ ds_wl_buffer_t *buffer;
+
+ wl_list_for_each(buffer, &backend->buffers, link) {
+ if (buffer->buffer == ds_buffer && buffer->released) {
+ buffer->released = false;
+ ds_buffer_lock(buffer->buffer);
+ return buffer;
+ }
+ }
+
+ return create_wl_buffer(backend, ds_buffer);
+}
+
+static void
+surface_frame_callback(void *data, struct wl_callback *cb, uint32_t time)
+{
+ ds_wl_output_t *output = data;
+
+ wl_callback_destroy(cb);
+ output->frame_callback = NULL;
+
+ wl_signal_emit(&output->base.events.frame, &output->base);
+}
+
+static const struct wl_callback_listener frame_listener = {
+ .done = surface_frame_callback
+};
+
+static bool
+wl_output_iface_commit(struct ds_output *ds_output)
+{
+ ds_wl_output_t *output;
+ ds_wl_buffer_t *buffer;
+ struct ds_buffer *ds_buffer;
+
+ output = wl_output_from_output(ds_output);
+
+ ds_buffer = ds_output->pending.buffer;
+ buffer = get_or_create_wl_buffer(output->backend, ds_buffer);
+ if (!buffer)
+ return NULL;
+
+ if (ds_output->pending.committed & DS_OUTPUT_STATE_BUFFER) {
+
+ if (output->frame_callback != NULL) {
+ ds_err("Skipping buffer swap");
+ return false;
+ }
+
+ output->frame_callback = wl_surface_frame(output->surface);
+ wl_callback_add_listener(output->frame_callback, &frame_listener,
+ output);
+ wl_surface_attach(output->surface, buffer->wl_buffer, 0, 0);
+ wl_surface_damage_buffer(output->surface, 0, 0, INT32_MAX, INT32_MAX);
+ wl_surface_commit(output->surface);
+
+ ds_dbg("Swap Buffer!!!!!");
+ }
+
+ wl_display_flush(output->backend->server.display);
+
+ return true;
+}
+
+const struct ds_output_interface wl_output_iface =
+{
+ .destroy = wl_output_iface_destroy,
+ .commit = wl_output_iface_commit,
+};
+
+static void
+wl_output_xdg_surface_handle_configure(void *data,
+ struct xdg_surface *xdg_surface, uint32_t serial)
+{
+ xdg_surface_ack_configure(xdg_surface, serial);
+}
+
+static const struct xdg_surface_listener wl_output_xdg_surface_listener =
+{
+ .configure = wl_output_xdg_surface_handle_configure,
+};
+
+static void
+wl_output_xdg_toplevel_handle_configure(void *data,
+ struct xdg_toplevel *xdg_toplevel,
+ int32_t width, int32_t height, struct wl_array *states)
+{
+ // TODO
+ // ds_wl_output_t *output = data;
+
+ if (width == 0 || height == 0)
+ return;
+}
+
+static void
+wl_output_xdg_toplevel_handle_close(void *data,
+ struct xdg_toplevel *xdg_toplevel)
+{
+ ds_wl_output_t *output = data;
+
+ ds_output_destroy(&output->base);
+}
+
+static const struct xdg_toplevel_listener wl_output_xdg_toplevel_listener =
+{
+ .configure = wl_output_xdg_toplevel_handle_configure,
+ .close = wl_output_xdg_toplevel_handle_close,
+};
--- /dev/null
+#include <assert.h>
+#include <stdlib.h>
+
+#include "buffer.h"
+#include "client_buffer.h"
+#include "libds/log.h"
+#include "libds/interfaces/buffer.h"
+
+static void buffer_consider_destroy(struct ds_buffer *buffer);
+static bool ds_resource_is_buffer(struct wl_resource *resource);
+
+WL_EXPORT void
+ds_buffer_init(struct ds_buffer *buffer, const struct ds_buffer_interface *iface,
+ int width, int height)
+{
+ buffer->iface = iface;
+ buffer->width = width;
+ buffer->height = height;
+
+ wl_signal_init(&buffer->events.destroy);
+ wl_signal_init(&buffer->events.release);
+}
+
+WL_EXPORT struct ds_buffer *
+ds_buffer_from_resource(struct wl_resource *resource)
+{
+ struct ds_buffer *buffer;
+
+ assert(resource && ds_resource_is_buffer(resource));
+
+ if (wl_shm_buffer_get(resource) != NULL) {
+ ds_shm_client_buffer_t *shm_client_buffer =
+ ds_shm_client_buffer_get_or_create(resource);
+ if (!shm_client_buffer) {
+ ds_err("Failed to create shm client buffer");
+ return NULL;
+ }
+
+ buffer = ds_buffer_lock(&shm_client_buffer->base);
+ }
+ else {
+ // TODO;
+ buffer = NULL;
+ }
+
+ return buffer;
+}
+
+WL_EXPORT void
+ds_buffer_drop(struct ds_buffer *buffer)
+{
+ assert(!buffer->dropped);
+ buffer->dropped = true;
+ ds_dbg("Buffer(%p) dropped: n_locks(%zu)", buffer, buffer->n_locks);
+ buffer_consider_destroy(buffer);
+}
+
+WL_EXPORT struct ds_buffer *
+ds_buffer_lock(struct ds_buffer *buffer)
+{
+ buffer->n_locks++;
+ ds_dbg("Buffer(%p) n_locks(%zu)", buffer, buffer->n_locks);
+ return buffer;
+}
+
+WL_EXPORT void
+ds_buffer_unlock(struct ds_buffer *buffer)
+{
+ assert(buffer->n_locks > 0);
+ buffer->n_locks--;
+ ds_dbg("Buffer(%p) n_locks(%zu)", buffer, buffer->n_locks);
+
+ if (buffer->n_locks == 0)
+ wl_signal_emit(&buffer->events.release, NULL);
+
+ buffer_consider_destroy(buffer);
+}
+
+WL_EXPORT bool
+ds_buffer_begin_data_ptr_access(struct ds_buffer *buffer, uint32_t flags,
+ void **data, uint32_t *format, size_t *stride)
+{
+ assert(!buffer->accessing_data_ptr);
+ if (!buffer->iface->begin_data_ptr_access)
+ return false;
+ if (!buffer->iface->begin_data_ptr_access(buffer, flags, data, format, stride))
+ return false;
+ buffer->accessing_data_ptr = true;
+ return true;
+}
+
+WL_EXPORT void
+ds_buffer_end_data_ptr_access(struct ds_buffer *buffer)
+{
+ assert(buffer->accessing_data_ptr);
+ buffer->iface->end_data_ptr_access(buffer);
+ buffer->accessing_data_ptr = false;
+}
+
+WL_EXPORT void
+ds_buffer_add_destroy_listener(struct ds_buffer *buffer,
+ struct wl_listener *listener)
+{
+ wl_signal_add(&buffer->events.destroy, listener);
+}
+
+void
+ds_buffer_add_release_listener(struct ds_buffer *buffer,
+ struct wl_listener *listener)
+{
+ wl_signal_add(&buffer->events.release, listener);
+}
+
+WL_EXPORT bool
+ds_buffer_get_shm(struct ds_buffer *buffer, struct ds_shm_attributes *attribs)
+{
+ if (!buffer->iface->get_shm)
+ return false;
+
+ return buffer->iface->get_shm(buffer, attribs);
+}
+
+WL_EXPORT void
+ds_buffer_get_size(struct ds_buffer *buffer, int *out_width, int *out_height)
+{
+ if (out_width)
+ *out_width = buffer->width;
+ if (out_height)
+ *out_height = buffer->height;
+}
+
+static void
+buffer_consider_destroy(struct ds_buffer *buffer)
+{
+ if (!buffer->dropped || buffer->n_locks > 0)
+ return;
+
+ assert(!buffer->accessing_data_ptr);
+
+ wl_signal_emit(&buffer->events.destroy, NULL);
+ buffer->iface->destroy(buffer);
+}
+
+static bool
+ds_resource_is_buffer(struct wl_resource *resource)
+{
+ return strcmp(wl_resource_get_class(resource),
+ wl_buffer_interface.name) == 0;
+}
--- /dev/null
+#ifndef DS_BUFFER_H
+#define DS_BUFFER_H
+
+#include <wayland-server.h>
+
+#include "libds/buffer.h"
+
+#endif
--- /dev/null
+#ifndef DS_CLIENT_BUFFER_H
+#define DS_CLIENT_BUFFER_H
+
+#include <wayland-server.h>
+
+#include "util.h"
+#include "libds/buffer.h"
+#include "libds/interfaces/buffer.h"
+
+typedef struct ds_shm_client_buffer ds_shm_client_buffer_t;
+
+struct ds_shm_client_buffer
+{
+ struct ds_buffer base;
+
+ uint32_t format;
+ size_t stride;
+
+ struct wl_resource *resource;
+ struct wl_shm_buffer *shm_buffer;
+
+ struct {
+ struct wl_listener buffer_release;
+ struct wl_listener resource_destroy;
+ } listener;
+};
+
+ds_shm_client_buffer_t *
+ds_shm_client_buffer_get_or_create(struct wl_resource *resource);
+
+#endif
--- /dev/null
+#include <assert.h>
+#include <stdlib.h>
+#include <drm_fourcc.h>
+
+#include "pixel_format.h"
+#include "buffer.h"
+#include "libds/log.h"
+#include "client_buffer.h"
+
+static void
+shm_client_buffer_resource_handle_destroy(struct wl_listener *listener,
+ void *data);
+static ds_shm_client_buffer_t *
+shm_client_buffer_create(struct wl_resource *resource);
+
+ds_shm_client_buffer_t *
+ds_shm_client_buffer_get_or_create(struct wl_resource *resource)
+{
+ ds_shm_client_buffer_t *buffer;
+ struct wl_listener *resource_destroy_listener;
+
+ resource_destroy_listener = wl_resource_get_destroy_listener(resource,
+ shm_client_buffer_resource_handle_destroy);
+ if (resource_destroy_listener) {
+ buffer = wl_container_of(resource_destroy_listener,
+ buffer, listener.resource_destroy);
+ return buffer;
+ }
+
+ return shm_client_buffer_create(resource);
+}
+
+static void
+shm_client_buffer_resource_handle_destroy(struct wl_listener *listener,
+ void *data)
+{
+ ds_shm_client_buffer_t *buffer;
+
+ buffer = wl_container_of(listener, buffer, listener.resource_destroy);
+
+ buffer->resource = NULL;
+ buffer->shm_buffer = NULL;
+ wl_list_remove(&buffer->listener.resource_destroy.link);
+ wl_list_init(&buffer->listener.resource_destroy.link);
+
+ ds_buffer_drop(&buffer->base);
+}
+
+static const struct ds_buffer_interface shm_client_buffer_iface;
+
+static ds_shm_client_buffer_t *
+shm_client_buffer_from_buffer(struct ds_buffer *buffer)
+{
+ assert(buffer->iface == &shm_client_buffer_iface);
+ return (ds_shm_client_buffer_t *)buffer;
+}
+
+static void
+shm_client_buffer_destroy(struct ds_buffer *ds_buffer)
+{
+ ds_shm_client_buffer_t *buffer;
+
+ buffer = shm_client_buffer_from_buffer(ds_buffer);
+
+ ds_dbg("Destroy shm client buffer(%p)", buffer);
+
+ wl_list_remove(&buffer->listener.resource_destroy.link);
+ wl_list_remove(&buffer->listener.buffer_release.link);
+ free(buffer);
+}
+
+static bool
+shm_client_buffer_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)
+{
+ ds_shm_client_buffer_t *buffer;
+
+ buffer = shm_client_buffer_from_buffer(ds_buffer);
+ *format = buffer->format;
+ *stride = buffer->stride;
+ if (buffer->shm_buffer) {
+ *data = wl_shm_buffer_get_data(buffer->shm_buffer);
+ wl_shm_buffer_begin_access(buffer->shm_buffer);
+ }
+ else
+ return false;
+
+ return true;
+}
+
+static void
+shm_client_buffer_end_data_ptr_access(struct ds_buffer *ds_buffer)
+{
+ ds_shm_client_buffer_t *buffer;
+
+ buffer = shm_client_buffer_from_buffer(ds_buffer);
+ if (buffer->shm_buffer)
+ wl_shm_buffer_end_access(buffer->shm_buffer);
+}
+
+static const struct ds_buffer_interface shm_client_buffer_iface = {
+ .destroy = shm_client_buffer_destroy,
+ .begin_data_ptr_access = shm_client_buffer_begin_data_ptr_access,
+ .end_data_ptr_access = shm_client_buffer_end_data_ptr_access,
+};
+
+static void
+shm_client_buffer_handle_release(struct wl_listener *listener, void *data)
+{
+ ds_shm_client_buffer_t *buffer;
+
+ buffer = wl_container_of(listener, buffer, listener.buffer_release);
+ if (buffer->resource)
+ wl_buffer_send_release(buffer->resource);
+}
+
+static ds_shm_client_buffer_t *
+shm_client_buffer_create(struct wl_resource *resource)
+{
+ ds_shm_client_buffer_t *buffer;
+ struct wl_shm_buffer *shm_buffer;
+ enum wl_shm_format wl_shm_format;
+ int32_t width;
+ int32_t height;
+
+ shm_buffer = wl_shm_buffer_get(resource);
+ width = wl_shm_buffer_get_width(shm_buffer);
+ height = wl_shm_buffer_get_height(shm_buffer);
+
+ buffer = calloc(1, sizeof(*buffer));
+ if (!buffer)
+ return NULL;
+
+ ds_buffer_init(&buffer->base, &shm_client_buffer_iface, width, height);
+
+ buffer->resource = resource;
+ buffer->shm_buffer = shm_buffer;
+
+ wl_shm_format = wl_shm_buffer_get_format(shm_buffer);
+ buffer->format = convert_wl_shm_format_to_drm(wl_shm_format);
+ buffer->stride = wl_shm_buffer_get_stride(shm_buffer);
+
+ buffer->listener.buffer_release.notify =
+ shm_client_buffer_handle_release;
+ ds_buffer_add_release_listener(&buffer->base,
+ &buffer->listener.buffer_release);
+
+ buffer->listener.resource_destroy.notify =
+ shm_client_buffer_resource_handle_destroy;
+ wl_resource_add_destroy_listener(resource,
+ &buffer->listener.resource_destroy);
+
+ ds_dbg("Shm client buffer(%p) created", buffer);
+
+ return buffer;
+}
--- /dev/null
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "libds/log.h"
+#include "subcompositor.h"
+#include "surface.h"
+#include "region.h"
+
+#define COMPOSITOR_VERSION 4
+
+struct ds_compositor
+{
+ struct wl_global *global;
+ ds_subcompositor_t subcompositor;
+
+ struct {
+ struct wl_signal new_surface;
+ struct wl_signal destroy;
+ } events;
+
+ struct wl_listener display_destroy;
+};
+
+static void compositor_bind(struct wl_client *client, void *data,
+ uint32_t version, uint32_t id);
+static void compositor_handle_display_destroy(struct wl_listener *listener,
+ void *data);
+
+WL_EXPORT struct ds_compositor *
+ds_compositor_create(struct wl_display *display)
+{
+ struct ds_compositor *compositor;
+
+ compositor = calloc(1, sizeof *compositor);
+ if (!compositor) {
+ ds_log_errno(DS_ERR, "Could not allocate memory");
+ return NULL;
+ }
+
+ compositor->global = wl_global_create(display, &wl_compositor_interface,
+ COMPOSITOR_VERSION, compositor, compositor_bind);
+ if (!compositor->global) {
+ ds_log_errno(DS_ERR, "Could not create global");
+ goto err_global;
+ }
+
+ if (!ds_subcompositor_init(&compositor->subcompositor, display)) {
+ ds_err("Could not initialize subcompositor");
+ goto err_subcomp;
+ }
+
+ wl_signal_init(&compositor->events.new_surface);
+ wl_signal_init(&compositor->events.destroy);
+
+ compositor->display_destroy.notify = compositor_handle_display_destroy;
+ wl_display_add_destroy_listener(display, &compositor->display_destroy);
+
+ ds_inf("Compositor(%p) created", compositor);
+
+ return compositor;
+
+err_subcomp:
+ wl_global_destroy(compositor->global);
+err_global:
+ free(compositor);
+
+ return NULL;
+}
+
+WL_EXPORT void
+ds_compositor_add_new_surface_listener(struct ds_compositor *compositor,
+ struct wl_listener *listener)
+{
+ wl_signal_add(&compositor->events.new_surface, listener);
+}
+
+WL_EXPORT void
+ds_compositor_add_destroy_listener(struct ds_compositor *compositor,
+ struct wl_listener *listener)
+{
+ wl_signal_add(&compositor->events.destroy, listener);
+}
+
+static void
+compositor_handle_create_surface(struct wl_client *client,
+ struct wl_resource *resource, uint32_t id)
+{
+ struct ds_compositor *compositor = wl_resource_get_user_data(resource);
+ struct ds_surface *surface;
+
+ surface = ds_surface_create(client,
+ wl_resource_get_version(resource), id);
+ if (!surface) {
+ ds_err("Could not create ds_surface");
+ return;
+ }
+
+ wl_signal_emit(&compositor->events.new_surface, surface);
+}
+
+static void
+compositor_handle_create_region(struct wl_client *client,
+ struct wl_resource *resource, uint32_t id)
+{
+ ds_region_add(client, wl_resource_get_version(resource), id);
+}
+
+static const struct wl_compositor_interface compositor_impl = {
+ .create_surface = compositor_handle_create_surface,
+ .create_region = compositor_handle_create_region,
+};
+
+static void compositor_bind(struct wl_client *client, void *data,
+ uint32_t version, uint32_t id)
+{
+ struct ds_compositor *compositor = data;
+ struct wl_resource *resource;
+
+ resource = wl_resource_create(client, &wl_compositor_interface,
+ version, id);
+ if (resource == NULL) {
+ wl_client_post_no_memory(client);
+ return;
+ }
+
+ wl_resource_set_implementation(resource, &compositor_impl,
+ compositor, NULL);
+}
+
+static void
+compositor_handle_display_destroy(struct wl_listener *listener, void *data)
+{
+ struct ds_compositor *compositor =
+ wl_container_of(listener, compositor, display_destroy);
+
+ ds_dbg("Destroy compositor(%p)", compositor);
+
+ wl_signal_emit(&compositor->events.destroy, compositor);
+
+ wl_list_remove(&compositor->display_destroy.link);
+ ds_subcompositor_finish(&compositor->subcompositor);
+ free(compositor);
+}
--- /dev/null
+#include <error.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <wayland-server.h>
+
+#include "libds/log.h"
+
+static bool colored = true;
+static enum ds_log_level log_level = DS_ERR;
+
+static const char *level_colors[] = {
+ [DS_SILENT] = "",
+ [DS_ERR] = "\x1B[1;31m",
+ [DS_INF] = "\x1B[1;34m",
+ [DS_DBG] = "\x1B[1;90m",
+};
+
+static const char *level_headers[] = {
+ [DS_SILENT] = "",
+ [DS_ERR] = "[ERROR]",
+ [DS_INF] = "[INFO]",
+ [DS_DBG] = "[DEBUG]",
+};
+
+static void log_stderr(enum ds_log_level level, const char *fmt, va_list args);
+static void log_wl(const char *fmt, va_list args);
+
+static ds_log_func_t log_callback = log_stderr;
+
+WL_EXPORT void
+ds_log_init(enum ds_log_level level, ds_log_func_t callback)
+{
+ if (level < DS_LOG_LEVEL_LAST)
+ log_level = level;
+ if (callback)
+ log_callback = callback;
+
+ wl_log_set_handler_server(log_wl);
+}
+
+WL_EXPORT void
+_ds_vlog(enum ds_log_level level, const char *fmt, va_list args)
+{
+ log_callback(level, fmt, args);
+}
+
+WL_EXPORT void
+_ds_log(enum ds_log_level level, const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ log_callback(level, fmt, args);
+ va_end(args);
+}
+
+enum ds_log_level
+ds_log_get_level(void)
+{
+ return log_level;
+}
+
+static void
+log_stderr(enum ds_log_level level, const char *fmt, va_list args)
+{
+ bool colored_tty = false;
+
+ if (level > log_level)
+ return;
+
+ unsigned c = (level < DS_LOG_LEVEL_LAST) ? level : DS_LOG_LEVEL_LAST - 1;
+
+ colored_tty = colored && isatty(STDERR_FILENO);
+ if (colored_tty)
+ fprintf(stderr, "%s", level_colors[c]);
+ else
+ fprintf(stderr, "%s ", level_headers[c]);
+
+ vfprintf(stderr, fmt, args);
+
+ if (colored_tty)
+ fprintf(stderr, "\x1B[0m");
+ fprintf(stderr, "\n");
+}
+
+static void
+log_wl(const char *fmt, va_list args)
+{
+ static char ds_fmt[1024];
+ int n = snprintf(ds_fmt, sizeof(ds_fmt), "[wayland] %s", fmt);
+ if (n > 0 && ds_fmt[n - 1] == '\n')
+ ds_fmt[n - 1] = '\0';
+ _ds_vlog(DS_INF, ds_fmt, args);
+}
--- /dev/null
+libds_files = [
+ 'log.c',
+ 'addon.c',
+ 'buffer.c',
+ 'allocator/allocator.c',
+ 'allocator/shm.c',
+ 'swapchain.c',
+ 'output.c',
+ 'compositor.c',
+ 'subcompositor.c',
+ 'region.c',
+ 'util/time.c',
+ 'util/shm.c',
+ 'surface/surface.c',
+ 'surface/subsurface.c',
+ 'client_buffer/shm_client_buffer.c',
+ 'xdg_shell/xdg_shell.c',
+ 'xdg_shell/xdg_surface.c',
+ 'xdg_shell/xdg_toplevel.c',
+ 'pixel_format.c',
+]
+
+protocols = {
+ 'xdg-shell': wl_protocol_dir / 'stable/xdg-shell/xdg-shell.xml',
+}
+
+protocols_code = {}
+protocols_server_header = {}
+
+foreach name, path : protocols
+ code = custom_target(
+ name.underscorify() + '_c',
+ input: path,
+ output: '@BASENAME@-protocol.c',
+ command: [wayland_scanner, 'private-code', '@INPUT@', '@OUTPUT@'],
+ )
+ libds_files += code
+
+ server_header = custom_target(
+ name.underscorify() + '_server_h',
+ input: path,
+ output: '@BASENAME@-server-protocol.h',
+ command: [wayland_scanner, 'server-header', '@INPUT@', '@OUTPUT@'],
+ build_by_default: false,
+ )
+ libds_files += server_header
+endforeach
+
+math = meson.get_compiler('c').find_library('m')
+wayland_server = dependency('wayland-server', required: true)
+pixman = dependency('pixman-1', required: true)
+libdrm = dependency('libdrm', required: true)
+
+libds_deps = [
+ math,
+ wayland_server,
+ pixman,
+ libdrm,
+]
+
+subdir('backend')
+
+lib_libds = shared_library('ds', libds_files,
+ dependencies: libds_deps,
+ include_directories: [ common_inc, include_directories('.') ],
+ version: meson.project_version(),
+ install: true
+)
+
+dep_libds = declare_dependency(
+ link_with: lib_libds,
+ dependencies: libds_deps,
+ include_directories: [ common_inc, include_directories('.') ],
+)
--- /dev/null
+#include <assert.h>
+#include <stdlib.h>
+
+#include "libds/log.h"
+#include "libds/output.h"
+#include "libds/interfaces/output.h"
+
+static void output_handle_display_destroy(struct wl_listener *listener,
+ void *data);
+static void output_state_clear(struct ds_output_state *state);
+static void output_state_clear_buffer(struct ds_output_state *state);
+
+WL_EXPORT void
+ds_output_init(struct ds_output *output, struct ds_backend *backend,
+ const struct ds_output_interface *iface, struct wl_display *display)
+{
+ assert(iface->commit);
+
+ output->backend = backend;
+ output->iface = iface;
+ output->display = display;
+
+ wl_signal_init(&output->events.destroy);
+ wl_signal_init(&output->events.frame);
+ wl_signal_init(&output->events.commit);
+
+ output->display_destroy.notify = output_handle_display_destroy;
+ wl_display_add_destroy_listener(display, &output->display_destroy);
+}
+
+WL_EXPORT void
+ds_output_destroy(struct ds_output *output)
+{
+ wl_list_remove(&output->display_destroy.link);
+
+ wl_signal_emit(&output->events.destroy, output);
+
+ if (output->iface && output->iface->destroy)
+ output->iface->destroy(output);
+ else
+ free(output);
+}
+
+WL_EXPORT bool
+ds_output_commit(struct ds_output *output)
+{
+ // TODO signal precommit
+
+ if (!output->iface->commit(output)) {
+ return false;
+ }
+
+ output_state_clear(&output->pending);
+
+ // TODO signal commit
+
+ return true;
+}
+
+WL_EXPORT void
+ds_output_attach_buffer(struct ds_output *output, struct ds_buffer *buffer)
+{
+ output_state_clear_buffer(&output->pending);
+ output->pending.committed |= DS_OUTPUT_STATE_BUFFER;
+ output->pending.buffer = ds_buffer_lock(buffer);
+}
+
+WL_EXPORT void
+ds_output_add_destroy_listener(struct ds_output *output,
+ struct wl_listener *listener)
+{
+ wl_signal_add(&output->events.destroy, listener);
+}
+
+WL_EXPORT void
+ds_output_add_frame_listener(struct ds_output *output,
+ struct wl_listener *listener)
+{
+ wl_signal_add(&output->events.frame, listener);
+}
+
+WL_EXPORT void
+ds_output_add_commit_listener(struct ds_output *output,
+ struct wl_listener *listener)
+{
+ wl_signal_add(&output->events.commit, listener);
+}
+
+static void
+output_handle_display_destroy(struct wl_listener *listener, void *data)
+{
+ struct ds_output *output;
+
+ output = wl_container_of(listener, output, display_destroy);
+ // TODO
+ // destroy wl_global
+}
+
+static void
+output_state_clear(struct ds_output_state *state)
+{
+ output_state_clear_buffer(state);
+ state->committed = 0;
+}
+
+static void
+output_state_clear_buffer(struct ds_output_state *state)
+{
+ if (!(state->committed & DS_OUTPUT_STATE_BUFFER))
+ return;
+
+ ds_buffer_unlock(state->buffer);
+ state->buffer = NULL;
+
+ state->committed &= ~DS_OUTPUT_STATE_BUFFER;
+}
--- /dev/null
+#include <drm_fourcc.h>
+#include "pixel_format.h"
+
+uint32_t
+convert_wl_shm_format_to_drm(enum wl_shm_format fmt)
+{
+ switch (fmt) {
+ case WL_SHM_FORMAT_XRGB8888:
+ return DRM_FORMAT_XRGB8888;
+ case WL_SHM_FORMAT_ARGB8888:
+ return DRM_FORMAT_ARGB8888;
+ default:
+ return (uint32_t)fmt;
+ }
+}
--- /dev/null
+#ifndef DS_PIXEL_FORMAT_H
+#define DS_PIXEL_FORMAT_H
+
+#include <stdint.h>
+#include <wayland-server.h>
+
+uint32_t convert_wl_shm_format_to_drm(enum wl_shm_format fmt);
+
+#endif
--- /dev/null
+#include "libds-private.h"
+#include "presentation-time-protocol.h"
+
+#define PRESENTATION_VERSION 1
+
+struct ds_presentation
+{
+ struct wl_global *global;
+
+ struct {
+ struct wl_signal destroy;
+ } events;
+};
+
+struct ds_presentation_feedback
+{
+ struct wl_list resources;
+
+}
+
+struct ds_presentation_surface_state
+{
+};
+
+struct ds_presentation_feedback
+{
+ struct wl_resource *resource;
+ struct wl_list link;
+ uint32_t psf_flags;
+};
+
+
+
+static void presentation_bind(struct wl_client *client, void *data,
+ uint32_t version, uint32_t id);
+static void handle_display_destroy(struct wl_listener *listener, void *data);
+
+struct ds_presentation *
+ds_presentation_create(struct wl_display *display, clockid_t clk_id)
+{
+ struct ds_presentation *presentation;
+
+ presentation = calloc(1, sizeof *presentation);
+ if (!presentation)
+ return NULL;
+
+ presentation->global = wl_global_create(display, &wp_presentation_interface,
+ PRESENTATION_VERSION, presentation, presentation_bind);
+ if (!presentation->global) {
+ free(presentation);
+ return NULL;
+ }
+
+ presentation->clock = clk_id;
+
+ wl_signal_init(&presentation->events.destroy);
+
+ presentation->display_destroy.notify = handle_display_destroy;
+ wl_display_add_destroy_listener(display, &presentation->display_destroy);
+}
+
+static void
+presentation_handle_destroy(struct wl_client *client, struct wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+static void
+presentation_handle_feedback(struct wl_client *client,
+ struct wl_resource *presentation_resource,
+ struct wl_resource *surface_resource, uint32_t id)
+{
+ struct ds_presentation *presentation;
+ struct ds_surface *surface;
+
+ presentation = wl_resource_get_user_data(presentation_resource);
+ surface = ds_surface_from_resource(surface_resource);
+}
+
+static const struct wp_presentation_interface presentation_impl = {
+ .destroy = presentation_handle_destroy,
+ .feedback = presentation_handle_feedback,
+};
+
+static void
+presentation_bind(struct wl_client *client, void *data,
+ uint32_t version, uint32_t id)
+{
+ struct ds_presentation *presentation = data;
+ struct wl_resource *resource;
+
+ resource = wl_resource_create(client, &wp_presentation_interface,
+ version, id);
+ if (!resource) {
+ wl_client_post_no_memory(client);
+ return;
+ }
+ wl_resource_set_implementation(resource, &presentation_impl,
+ presentation, NULL);
+
+ wp_presentation_send_clock_id(resource, (uint32_t)presentation->clock);
+}
+
+static void
+handle_display_destroy(struct wl_listener *listener, void *data)
+{
+ struct ds_presentation *presentation;
+
+ presentation = wl_container_of(listener, presentation, display_destroy);
+ wl_signal_emit(&presentation->events.destroy, presentation);
+ wl_list_remove(&presentation->display_destroy.link);
+ wl_global_destroy(presentation->global);
+ free(presentation);
+}
--- /dev/null
+#include <assert.h>
+#include <stdlib.h>
+#include <math.h>
+#include <pixman.h>
+
+#include "region.h"
+#include "libds/log.h"
+
+static const struct wl_region_interface region_impl;
+
+static void region_handle_resource_destroy(struct wl_resource *resource);
+
+void
+ds_region_add(struct wl_client *client, uint32_t version, uint32_t id)
+{
+ struct wl_resource *resource;
+ pixman_region32_t *region;
+
+ region = calloc(1, sizeof *region);
+ if (!region) {
+ wl_client_post_no_memory(client);
+ return;
+ }
+
+ pixman_region32_init(region);
+
+ resource = wl_resource_create(client, &wl_region_interface, version, id);
+ if (!resource) {
+ free(region);
+ wl_client_post_no_memory(client);
+ return;
+ }
+
+ wl_resource_set_implementation(resource, ®ion_impl, region,
+ region_handle_resource_destroy);
+}
+
+pixman_region32_t *
+ds_region_from_resource(struct wl_resource *resource)
+{
+ assert(wl_resource_instance_of(resource, &wl_region_interface,
+ ®ion_impl));
+ return wl_resource_get_user_data(resource);
+}
+
+void
+ds_region_transform(pixman_region32_t *dst, pixman_region32_t *src,
+ enum wl_output_transform transform, int width, int height)
+{
+ pixman_box32_t *src_rects, *dst_rects;
+ int nrects, i;
+
+ if (transform == WL_OUTPUT_TRANSFORM_NORMAL) {
+ pixman_region32_copy(dst, src);
+ return;
+ }
+
+ src_rects = pixman_region32_rectangles(src, &nrects);
+ dst_rects = malloc(nrects * sizeof *dst_rects);
+ if (!dst_rects)
+ return;
+
+ for (i = 0; i < nrects; i++) {
+ switch (transform) {
+ default:
+ ds_err("Unkown transform value(%d)", transform);
+ case WL_OUTPUT_TRANSFORM_NORMAL:
+ dst_rects[i].x1 = src_rects[i].x1;
+ dst_rects[i].y1 = src_rects[i].y1;
+ dst_rects[i].x2 = src_rects[i].x2;
+ dst_rects[i].y2 = src_rects[i].y2;
+ break;
+ case WL_OUTPUT_TRANSFORM_90:
+ dst_rects[i].x1 = height - src_rects[i].y2;
+ dst_rects[i].y1 = src_rects[i].x1;
+ dst_rects[i].x2 = height - src_rects[i].y1;
+ dst_rects[i].y2 = src_rects[i].x2;
+ break;
+ case WL_OUTPUT_TRANSFORM_180:
+ dst_rects[i].x1 = width - src_rects[i].x2;
+ dst_rects[i].y1 = height - src_rects[i].y2;
+ dst_rects[i].x2 = width - src_rects[i].x1;
+ dst_rects[i].y2 = height - src_rects[i].y1;
+ break;
+ case WL_OUTPUT_TRANSFORM_270:
+ dst_rects[i].x1 = src_rects[i].y1;
+ dst_rects[i].y1 = width - src_rects[i].x2;
+ dst_rects[i].x2 = src_rects[i].y2;
+ dst_rects[i].y2 = width - src_rects[i].x1;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED:
+ dst_rects[i].x1 = width - src_rects[i].x2;
+ dst_rects[i].y1 = src_rects[i].y1;
+ dst_rects[i].x2 = width - src_rects[i].x1;
+ dst_rects[i].y2 = src_rects[i].y2;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED_90:
+ dst_rects[i].x1 = src_rects[i].y1;
+ dst_rects[i].y1 = src_rects[i].x1;
+ dst_rects[i].x2 = src_rects[i].y2;
+ dst_rects[i].y2 = src_rects[i].x2;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED_180:
+ dst_rects[i].x1 = src_rects[i].x1;
+ dst_rects[i].y1 = height - src_rects[i].y2;
+ dst_rects[i].x2 = src_rects[i].x2;
+ dst_rects[i].y2 = height - src_rects[i].y1;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED_270:
+ dst_rects[i].x1 = height - src_rects[i].y2;
+ dst_rects[i].y1 = width - src_rects[i].x2;
+ dst_rects[i].x2 = height - src_rects[i].y1;
+ dst_rects[i].y2 = width - src_rects[i].x1;
+ break;
+ }
+ }
+
+ pixman_region32_fini(dst);
+ pixman_region32_init_rects(dst, dst_rects, nrects);
+ free(dst_rects);
+}
+
+void
+ds_region_scale_xy(pixman_region32_t *dst, pixman_region32_t *src,
+ float scale_x, float scale_y)
+{
+ pixman_box32_t *src_rects, *dst_rects;
+ int nrects, i;
+
+ if (scale_x == 1.0 && scale_y == 1.0) {
+ pixman_region32_copy(dst, src);
+ }
+
+ src_rects = pixman_region32_rectangles(src, &nrects);
+ dst_rects = malloc(nrects * sizeof *dst_rects);
+ if (!dst_rects)
+ return;
+
+ for (i = 0; i < nrects; i++) {
+ dst_rects[i].x1 = floor(src_rects[i].x1 * scale_x);
+ dst_rects[i].x2 = ceil(src_rects[i].x2 * scale_x);
+ dst_rects[i].y1 = floor(src_rects[i].y1 * scale_y);
+ dst_rects[i].y2 = ceil(src_rects[i].y2 * scale_y);
+ }
+
+ pixman_region32_fini(dst);
+ pixman_region32_init_rects(dst, dst_rects, nrects);
+ free(dst_rects);
+}
+
+void
+ds_region_scale(pixman_region32_t *dst, pixman_region32_t *src, float scale)
+{
+ ds_region_scale_xy(dst, src, scale, scale);
+}
+
+static void
+region_destroy(struct wl_client *client, struct wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+static void
+region_add(struct wl_client *client, struct wl_resource *resource,
+ int32_t x, int32_t y, int32_t width, int32_t height)
+{
+ pixman_region32_t *region = wl_resource_get_user_data(resource);
+ pixman_region32_union_rect(region, region, x, y, width, height);
+}
+
+static void
+region_subtract(struct wl_client *client, struct wl_resource *resource,
+ int32_t x, int32_t y, int32_t width, int32_t height)
+{
+ pixman_region32_t *region = wl_resource_get_user_data(resource);
+ pixman_region32_t rect;
+
+ pixman_region32_union_rect(region, region, x, y, width, height);
+ pixman_region32_init_rect(&rect, x, y, width, height);
+ pixman_region32_subtract(region, region, &rect);
+ pixman_region32_fini(&rect);
+}
+
+static const struct wl_region_interface region_impl = {
+ .destroy = region_destroy,
+ .add = region_add,
+ .subtract = region_subtract,
+};
+
+static void
+region_handle_resource_destroy(struct wl_resource *resource)
+{
+ pixman_region32_t *region = wl_resource_get_user_data(resource);
+ pixman_region32_fini(region);
+ free(region);
+}
--- /dev/null
+#ifndef DS_REGION_H
+#define DS_REGION_H
+
+#include <pixman.h>
+#include <wayland-server.h>
+
+void
+ds_region_add(struct wl_client *client, uint32_t version, uint32_t id);
+
+pixman_region32_t *
+ds_region_from_resource(struct wl_resource *resource);
+
+void
+ds_region_transform(pixman_region32_t *dst, pixman_region32_t *src,
+ enum wl_output_transform transform, int width, int height);
+
+void
+ds_region_scale_xy(pixman_region32_t *dst, pixman_region32_t *src,
+ float scale_x, float scale_y);
+
+void
+ds_region_scale(pixman_region32_t *dst, pixman_region32_t *src, float scale);
+
+#endif
--- /dev/null
+#include "subcompositor.h"
+#include "surface.h"
+#include "libds/log.h"
+
+#define SUBCOMPOSITOR_VERSION 1
+
+static void subcompositor_bind(struct wl_client *client, void *data,
+ uint32_t version, uint32_t id);
+
+bool
+ds_subcompositor_init(ds_subcompositor_t *subcomp,
+ struct wl_display *display)
+{
+ subcomp->global = wl_global_create(display, &wl_subcompositor_interface,
+ SUBCOMPOSITOR_VERSION, subcomp, subcompositor_bind);
+ if (!subcomp->global) {
+ ds_log_errno(DS_ERR, "Could not create global");
+ return false;
+ }
+
+ return true;
+}
+
+void
+ds_subcompositor_finish(ds_subcompositor_t *subcomp)
+{
+ wl_global_destroy(subcomp->global);
+}
+
+static void
+subcompositor_handle_destroy(struct wl_client *client,
+ struct wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+static void
+subcompositor_handle_get_subsurface(struct wl_client *client,
+ struct wl_resource *resource, uint32_t id,
+ struct wl_resource *surface_resource,
+ struct wl_resource *parent_resource)
+{
+ struct ds_surface *surface = ds_surface_from_resource(surface_resource);
+ struct ds_surface *parent = ds_surface_from_resource(parent_resource);
+
+ if (surface == parent) {
+ ds_inf("ds_surface(%p) cannot be its own parent", surface);
+ wl_resource_post_error(resource,
+ WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE,
+ "%d: wl_surface@%d cannot be its own parent",
+ id, wl_resource_get_id(surface_resource));
+ return;
+ }
+
+ if (ds_surface_is_subsurface(surface)) {
+ ds_inf("ds_surface(%p) is already a sub-surface", surface);
+ wl_resource_post_error(resource,
+ WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE,
+ "%d: wl_surface@%d is already a sub-surface",
+ id, wl_resource_get_id(surface_resource));
+ return;
+ }
+
+ if (ds_surface_is_ancestor_of(surface, parent)) {
+ ds_inf("ds_surface(%p) is an ancestor of given parent(%p)",
+ surface, parent);
+ wl_resource_post_error(resource,
+ WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE,
+ "%d: wl_surface@%d is an ancestor of parent",
+ id, wl_resource_get_id(surface_resource));
+ return;
+ }
+
+ ds_subsurface_create(resource, surface, parent,
+ wl_resource_get_version(resource), id);
+}
+
+static const struct wl_subcompositor_interface subcompositor_impl = {
+ .destroy = subcompositor_handle_destroy,
+ .get_subsurface = subcompositor_handle_get_subsurface,
+};
+
+static void
+subcompositor_bind(struct wl_client *client, void *data,
+ uint32_t version, uint32_t id)
+{
+ ds_subcompositor_t *subcomp = data;
+ struct wl_resource *resource;
+
+ resource = wl_resource_create(client, &wl_subcompositor_interface, 1, id);
+ if (resource == NULL) {
+ wl_client_post_no_memory(client);
+ return;
+ }
+
+ wl_resource_set_implementation(resource, &subcompositor_impl,
+ subcomp, NULL);
+}
--- /dev/null
+#ifndef DS_SUBCOMPOSITOR_H
+#define DS_SUBCOMPOSITOR_H
+
+#include <wayland-server.h>
+
+typedef struct ds_subcompositor ds_subcompositor_t;
+
+struct ds_subcompositor
+{
+ struct wl_global *global;
+};
+
+bool
+ds_subcompositor_init(ds_subcompositor_t *subcomp,
+ struct wl_display *display);
+
+void
+ds_subcompositor_finish(ds_subcompositor_t *subcomp);
+
+#endif
--- /dev/null
+#ifndef DS_SURFACE_H
+#define DS_SURFACE_H
+
+#include <wayland-server.h>
+
+#include "libds/surface.h"
+
+struct ds_surface_role
+{
+ const char *name;
+ void (*commit)(struct ds_surface *surface);
+};
+
+struct ds_surface *
+ds_surface_create(struct wl_client *client, uint32_t version, uint32_t id);
+
+struct ds_surface *
+ds_surface_from_resource(struct wl_resource *resource);
+
+bool
+ds_surface_set_role(struct ds_surface *surface,
+ const struct ds_surface_role *role, void *role_data,
+ struct wl_resource *error_resource, uint32_t error_code);
+
+const struct ds_surface_role *
+ds_surface_get_role(struct ds_surface *surface);
+
+void *
+ds_surface_get_role_data(struct ds_surface *surface);
+
+void
+ds_surface_reset_role_data(struct ds_surface *surface);
+
+bool
+ds_surface_has_buffer(struct ds_surface *surface);
+
+bool
+ds_surface_is_ancestor_of(struct ds_surface *surface,
+ struct ds_surface *target_surface);
+
+bool
+ds_surface_is_subsurface(struct ds_surface *surface);
+
+struct ds_subsurface *
+ds_subsurface_create(struct wl_resource *subcomp_resource,
+ struct ds_surface *surface, struct ds_surface *parent,
+ uint32_t version, uint32_t id);
+
+#endif
--- /dev/null
+#include <assert.h>
+#include <stdlib.h>
+
+#include "libds/log.h"
+#include "libds/surface.h"
+#include "surface-private.h"
+
+static const struct wl_subsurface_interface subsurface_impl;
+static const struct ds_surface_role subsurface_role;
+
+static void subsurface_handle_resource_destroy(struct wl_resource *resource);
+static void subsurface_handle_surface_destroy(struct wl_listener *listener,
+ void *data);
+static void subsurface_link_surface(struct ds_subsurface *subsurface,
+ struct ds_surface *surface);
+static void subsurface_unlink_surface(struct ds_subsurface *subsurface);
+static void subsurface_link_parent(struct ds_subsurface *subsurface,
+ struct ds_surface *parent);
+static void subsurface_unlink_parent(struct ds_subsurface *subsurface);
+static struct ds_subsurface *subsurface_find_sibling(struct ds_subsurface *subsurface,
+ struct ds_surface *surface);
+
+struct ds_subsurface *
+ds_subsurface_create(struct wl_resource *subcomp_resource,
+ struct ds_surface *surface, struct ds_surface *parent,
+ uint32_t version, uint32_t id)
+{
+ struct wl_client *client;
+ struct ds_subsurface *subsurface;
+
+ client = wl_resource_get_client(subcomp_resource);
+
+ subsurface = calloc(1, sizeof *subsurface);
+ if (!subsurface) {
+ ds_log_errno(DS_ERR, "Could not allocate memory");
+ wl_client_post_no_memory(client);
+ return NULL;
+ }
+
+ if (!ds_surface_set_role(surface, &subsurface_role, subsurface,
+ subcomp_resource, WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE)) {
+ free(subsurface);
+ return NULL;
+ }
+
+ subsurface->resource = wl_resource_create(client, &wl_subsurface_interface,
+ version, id);
+ if (!subsurface->resource) {
+ ds_log_errno(DS_ERR, "Could not create resource");
+ free(subsurface);
+ wl_client_post_no_memory(client);
+ return NULL;
+ }
+ wl_resource_set_implementation(subsurface->resource, &subsurface_impl,
+ subsurface, subsurface_handle_resource_destroy);
+
+ subsurface->synchronized = true;
+
+ wl_signal_init(&subsurface->events.destroy);
+
+ subsurface_link_surface(subsurface, surface);
+ subsurface_link_parent(subsurface, parent);
+
+ ds_inf("New ds_subsurface %p: surface %p, parent surface %p",
+ subsurface, surface, parent);
+
+ wl_signal_emit(&parent->events.new_subsurface, subsurface);
+
+ return subsurface;
+}
+
+bool
+ds_surface_is_subsurface(struct ds_surface *surface)
+{
+ return ds_surface_get_role(surface) == &subsurface_role;
+}
+
+struct ds_subsurface *
+ds_subsurface_from_ds_surface(struct ds_surface *surface)
+{
+ assert(ds_surface_is_subsurface(surface));
+ return (struct ds_subsurface *)surface->role_data;
+}
+
+struct ds_surface *
+ds_subsurface_get_parent(struct ds_subsurface *subsurface)
+{
+ return subsurface->parent;
+}
+
+static const struct ds_surface_role subsurface_role = {
+ .name = "wl_subsurface",
+};
+
+static void
+subsurface_handle_destroy(struct wl_client *client,
+ struct wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+static void
+subsurface_handle_set_position(struct wl_client *client,
+ struct wl_resource *resource, int32_t x, int32_t y)
+{
+ struct ds_subsurface *subsurface;
+
+ subsurface = wl_resource_get_user_data(resource);
+ if (!subsurface)
+ return;
+
+ subsurface->pending.x = x;
+ subsurface->pending.y = y;
+}
+
+static void
+subsurface_handle_place_above(struct wl_client *client,
+ struct wl_resource *resource, struct wl_resource *sibling_resource)
+{
+ struct ds_subsurface *subsurface, *sibling;
+ struct ds_surface *sibling_surface;
+ struct wl_list *node;
+
+ subsurface = wl_resource_get_user_data(resource);
+ if (!subsurface)
+ return;
+
+ sibling_surface = ds_surface_from_resource(sibling_resource);
+ if (sibling_surface == subsurface->parent) {
+ node = &subsurface->parent->pending.subsurfaces_above;
+ }
+ else {
+ sibling = subsurface_find_sibling(subsurface, sibling_surface);
+ if (!sibling) {
+ wl_resource_post_error(subsurface->resource,
+ WL_SUBSURFACE_ERROR_BAD_SURFACE,
+ "%s: wl_surface@%d is not a parent or sibling",
+ "place_above", wl_resource_get_id(sibling_resource));
+ return;
+ }
+ node = &sibling->pending.link;
+ }
+
+ wl_list_remove(&subsurface->pending.link);
+ wl_list_insert(node, &subsurface->pending.link);
+
+ subsurface->reordered = true;
+}
+
+static void
+subsurface_handle_place_below(struct wl_client *client,
+ struct wl_resource *resource, struct wl_resource *sibling_resource)
+{
+ struct ds_subsurface *subsurface, *sibling;
+ struct ds_surface *sibling_surface;
+ struct wl_list *node;
+
+ subsurface = wl_resource_get_user_data(resource);
+ if (!subsurface)
+ return;
+
+ sibling_surface = ds_surface_from_resource(sibling_resource);
+ if (sibling_surface == subsurface->parent) {
+ node = &subsurface->parent->pending.subsurfaces_below;
+ }
+ else {
+ sibling = subsurface_find_sibling(subsurface, sibling_surface);
+ if (!sibling) {
+ wl_resource_post_error(subsurface->resource,
+ WL_SUBSURFACE_ERROR_BAD_SURFACE,
+ "%s: wl_surface@%d is not a parent or sibling",
+ "place_below", wl_resource_get_id(sibling_resource));
+ return;
+ }
+ node = &sibling->pending.link;
+
+ }
+
+ wl_list_remove(&subsurface->pending.link);
+ wl_list_insert(node->prev, &subsurface->pending.link);
+
+ subsurface->reordered = true;
+}
+
+static void
+subsurface_handle_set_sync(struct wl_client *client,
+ struct wl_resource *resource)
+{
+ struct ds_subsurface *subsurface;
+
+ subsurface = wl_resource_get_user_data(resource);
+ if (!subsurface)
+ return;
+
+ subsurface->synchronized = true;
+}
+
+static void
+subsurface_handle_set_desync(struct wl_client *client,
+ struct wl_resource *resource)
+{
+ struct ds_subsurface *subsurface;
+
+ subsurface = wl_resource_get_user_data(resource);
+ if (!subsurface)
+ return;
+
+ if (subsurface->synchronized) {
+ subsurface->synchronized = false;
+
+ // TODO: flush
+ }
+}
+
+static const struct wl_subsurface_interface subsurface_impl = {
+ .destroy = subsurface_handle_destroy,
+ .set_position = subsurface_handle_set_position,
+ .place_above = subsurface_handle_place_above,
+ .place_below = subsurface_handle_place_below,
+ .set_sync = subsurface_handle_set_sync,
+ .set_desync = subsurface_handle_set_desync,
+};
+
+static void
+subsurface_destroy(struct ds_subsurface *subsurface)
+{
+ wl_signal_emit(&subsurface->events.destroy, subsurface);
+
+ if (subsurface->parent)
+ subsurface_unlink_parent(subsurface);
+
+ if (subsurface->surface)
+ subsurface_unlink_surface(subsurface);
+
+ wl_resource_set_user_data(subsurface->resource, NULL);
+
+ free(subsurface);
+}
+
+static void
+subsurface_handle_resource_destroy(struct wl_resource *resource)
+{
+ struct ds_subsurface *subsurface;
+
+ subsurface = wl_resource_get_user_data(resource);
+ if (!subsurface)
+ return;
+
+ subsurface_destroy(subsurface);
+}
+
+static
+void subsurface_handle_surface_destroy(struct wl_listener *listener,
+ void *data)
+{
+ struct ds_subsurface *subsurface;
+
+ subsurface = wl_container_of(listener, subsurface,
+ listener.surface_destroy);
+ subsurface_destroy(subsurface);
+}
+
+static void
+subsurface_handle_parent_destroy(struct wl_listener *listener,
+ void *data)
+{
+ struct ds_subsurface *subsurface;
+
+ subsurface = wl_container_of(listener, subsurface,
+ listener.parent_destroy);
+ subsurface_unlink_parent(subsurface);
+}
+
+static void
+subsurface_link_surface(struct ds_subsurface *subsurface, struct ds_surface *surface)
+{
+ subsurface->surface = surface;
+ subsurface->listener.surface_destroy.notify =
+ subsurface_handle_surface_destroy;
+ ds_surface_add_destroy_listener(surface,
+ &subsurface->listener.surface_destroy);
+}
+
+static void
+subsurface_unlink_surface(struct ds_subsurface *subsurface)
+{
+ wl_list_remove(&subsurface->listener.surface_destroy.link);
+ subsurface->surface->role_data = NULL;
+ subsurface->surface = NULL;
+}
+
+static void
+subsurface_link_parent(struct ds_subsurface *subsurface, struct ds_surface *parent)
+{
+ subsurface->parent = parent;
+ subsurface->listener.parent_destroy.notify =
+ subsurface_handle_parent_destroy;
+ ds_surface_add_destroy_listener(parent,
+ &subsurface->listener.parent_destroy);
+ wl_list_insert(parent->current.subsurfaces_above.prev,
+ &subsurface->current.link);
+ wl_list_insert(parent->pending.subsurfaces_above.prev,
+ &subsurface->pending.link);
+}
+
+static void
+subsurface_unlink_parent(struct ds_subsurface *subsurface)
+{
+ wl_list_remove(&subsurface->current.link);
+ wl_list_remove(&subsurface->pending.link);
+ wl_list_remove(&subsurface->listener.parent_destroy.link);
+ subsurface->parent = NULL;
+}
+
+static struct ds_subsurface *
+subsurface_find_sibling(struct ds_subsurface *subsurface, struct ds_surface *surface)
+{
+ struct ds_surface *parent = subsurface->parent;
+ struct ds_subsurface *sibling;
+
+ wl_list_for_each(sibling, &parent->pending.subsurfaces_below, pending.link) {
+ if (sibling->surface == surface && sibling != subsurface)
+ return sibling;
+ }
+
+ wl_list_for_each(sibling, &parent->pending.subsurfaces_above, pending.link) {
+ if (sibling->surface == surface && sibling != subsurface) {
+ return sibling;
+ }
+ }
+
+ return NULL;
+}
--- /dev/null
+#ifndef DS_SURFACE_PRIVATE_H
+#define DS_SURFACE_PRIVATE_H
+
+#include <stdint.h>
+#include <pixman.h>
+#include <wayland-server.h>
+
+#include "addon.h"
+#include "buffer.h"
+#include "surface.h"
+
+typedef enum
+{
+ DS_SURFACE_STATE_BUFFER = 1 << 0,
+ DS_SURFACE_STATE_SURFACE_DAMAGE = 1 << 1,
+ DS_SURFACE_STATE_BUFFER_DAMAGE = 1 << 2,
+ DS_SURFACE_STATE_OPAQUE_REGION = 1 << 3,
+ DS_SURFACE_STATE_INPUT_REGION = 1 << 4,
+ DS_SURFACE_STATE_TRANSFORM = 1 << 5,
+ DS_SURFACE_STATE_SCALE = 1 << 6,
+ DS_SURFACE_STATE_FRAME_CALLBACK_LIST = 1 << 7,
+ DS_SURFACE_STATE_VIEWPORT = 1 << 8,
+} ds_surface_state_field_t;
+
+struct ds_surface_state
+{
+ ds_surface_state_field_t committed;
+
+ struct ds_buffer *buffer;
+ int32_t dx, dy;
+ pixman_region32_t surface_damage, buffer_damage;
+ pixman_region32_t opaque, input;
+ enum wl_output_transform transform;
+ int32_t scale;
+ struct wl_list frame_callback_list;
+
+ int width, height;
+ int buffer_width, buffer_height;
+
+ struct wl_list subsurfaces_below;
+ struct wl_list subsurfaces_above;
+
+ // TODO viewport
+};
+
+struct ds_surface
+{
+ struct wl_resource *resource;
+
+ struct ds_buffer *buffer;
+
+ pixman_region32_t buffer_damage;
+ pixman_region32_t opaque_region;
+ pixman_region32_t input_region;
+
+ struct ds_surface_state current, pending;
+
+ const struct ds_surface_role *role;
+ void *role_data;
+
+ int sx, sy;
+
+ struct {
+ struct wl_signal commit;
+ struct wl_signal new_subsurface;
+ struct wl_signal destroy;
+ } events;
+
+ struct ds_addon_set addons;
+};
+
+struct ds_subsurface_parent_state
+{
+ int32_t x, y;
+ struct wl_list link;
+};
+
+struct ds_subsurface
+{
+ struct wl_resource *resource;
+ struct ds_surface *surface;
+ struct ds_surface *parent;
+
+ struct ds_subsurface_parent_state current, pending;
+
+ struct {
+ struct wl_signal destroy;
+ } events;
+
+ struct {
+ struct wl_listener surface_destroy;
+ struct wl_listener parent_destroy;
+ } listener;
+
+ bool synchronized;
+ bool reordered;
+ bool mapped;
+};
+
+struct ds_surface *
+ds_surface_get_root_surface(struct ds_surface *surface);
+
+struct ds_subsurface *
+ds_subsurface_from_ds_surface(struct ds_surface *surface);
+
+struct ds_surface *
+ds_subsurface_get_parent(struct ds_subsurface *subsurface);
+
+#endif
--- /dev/null
+#include <assert.h>
+#include <stdlib.h>
+
+#include "surface-private.h"
+#include "region.h"
+#include "util.h"
+
+#include "libds/log.h"
+#include "libds/surface.h"
+
+#define CALLBACK_VERSION 1
+
+static const struct wl_surface_interface surface_impl;
+
+static void surface_handle_resource_destroy(struct wl_resource *resource);
+static void surface_state_init(struct ds_surface_state *state);
+static void surface_state_finish(struct ds_surface_state *state);
+static void surface_state_move(struct ds_surface_state *state,
+ struct ds_surface_state *next);
+static void surface_finalize_pending(struct ds_surface *surface);
+static void surface_commit_state(struct ds_surface *surface,
+ struct ds_surface_state *next);
+static bool surface_for_each(struct ds_surface *surface, int x, int y,
+ ds_surface_for_each_func_t iterator, void *data);
+
+WL_EXPORT void
+ds_surface_add_destroy_listener(struct ds_surface *surface,
+ struct wl_listener *listener)
+{
+ wl_signal_add(&surface->events.destroy, listener);
+}
+
+WL_EXPORT void
+ds_surface_add_commit_listener(struct ds_surface *surface,
+ struct wl_listener *listener)
+{
+ wl_signal_add(&surface->events.commit, listener);
+}
+
+WL_EXPORT void
+ds_surface_add_new_subsurface_listener(struct ds_surface *surface,
+ struct wl_listener *listener)
+{
+ wl_signal_add(&surface->events.new_subsurface, listener);
+}
+
+WL_EXPORT struct ds_buffer *
+ds_surface_get_buffer(struct ds_surface *surface)
+{
+ return surface->buffer;
+}
+
+WL_EXPORT void
+ds_surface_for_each(struct ds_surface *surface,
+ ds_surface_for_each_func_t iterator, void *data)
+{
+ surface_for_each(surface, 0, 0, iterator, data);
+}
+
+WL_EXPORT void
+ds_surface_send_frame_done(struct ds_surface *surface,
+ const struct timespec *when)
+{
+ struct wl_resource *resource, *tmp;
+
+ wl_resource_for_each_safe(resource, tmp,
+ &surface->current.frame_callback_list) {
+ wl_callback_send_done(resource, timespec_to_msec(when));
+ wl_resource_destroy(resource);
+ }
+}
+
+struct ds_surface *
+ds_surface_create(struct wl_client *client, uint32_t version, uint32_t id)
+{
+ struct ds_surface *surface;
+
+ surface = calloc(1, sizeof *surface);
+ if (!surface) {
+ wl_client_post_no_memory(client);
+ return NULL;
+ }
+
+ surface->resource =
+ wl_resource_create(client, &wl_surface_interface, version, id);
+ if (!surface->resource) {
+ free(surface);
+ wl_client_post_no_memory(client);
+ return NULL;
+ }
+
+ wl_resource_set_implementation(surface->resource, &surface_impl,
+ surface, surface_handle_resource_destroy);
+
+ surface_state_init(&surface->current);
+ surface_state_init(&surface->pending);
+
+ wl_signal_init(&surface->events.commit);
+ wl_signal_init(&surface->events.destroy);
+ wl_signal_init(&surface->events.new_subsurface);
+
+ pixman_region32_init(&surface->buffer_damage);
+ pixman_region32_init(&surface->opaque_region);
+ pixman_region32_init(&surface->input_region);
+
+ ds_inf("New ds_surface %p (res %p)", surface, surface->resource);
+
+ return surface;
+}
+
+struct ds_surface *
+ds_surface_from_resource(struct wl_resource *resource)
+{
+ assert(wl_resource_instance_of(resource, &wl_surface_interface,
+ &surface_impl));
+ return wl_resource_get_user_data(resource);
+}
+
+bool
+ds_surface_set_role(struct ds_surface *surface,
+ const struct ds_surface_role *role, void *role_data,
+ struct wl_resource *error_resource, uint32_t error_code)
+{
+ assert(role != NULL);
+
+ if (surface->role != NULL && surface->role != role) {
+ ds_err("Cannot assign role %s to ds_surface(%p) "
+ "already has role %s\n",
+ role->name, surface, surface->role->name);
+ if (error_resource != NULL) {
+ wl_resource_post_error(error_resource, error_code,
+ "Cannot assign role %s to wl_surface@%d "
+ "already has role %s\n",
+ role->name, wl_resource_get_id(surface->resource),
+ surface->role->name);
+ }
+ return false;
+ }
+
+ if (surface->role_data != NULL && surface->role_data != role_data) {
+ ds_err("Cannot reassign role %s to ds_surface(%p), "
+ "role object still exists", role->name, surface);
+ wl_resource_post_error(error_resource, error_code,
+ "Cannot reassign role %s to wl_surface@%d, "
+ "role object still exists", role->name,
+ wl_resource_get_id(surface->resource));
+ return false;
+ }
+
+ surface->role = role;
+ surface->role_data = role_data;
+
+ ds_inf("Set ds_surface(%p) role: %s", surface, role->name);
+
+ return true;
+}
+
+const struct ds_surface_role *
+ds_surface_get_role(struct ds_surface *surface)
+{
+ return surface->role;
+}
+
+void *
+ds_surface_get_role_data(struct ds_surface *surface)
+{
+ return surface->role_data;
+}
+
+void
+ds_surface_reset_role_data(struct ds_surface *surface)
+{
+ surface->role_data = NULL;
+}
+
+bool
+ds_surface_is_ancestor_of(struct ds_surface *surface,
+ struct ds_surface *target_surface)
+{
+ struct ds_subsurface *target_subsurface;
+ struct ds_surface *parent_surface;
+
+ while (target_surface &&
+ ds_surface_is_subsurface(target_surface)) {
+ target_subsurface = ds_subsurface_from_ds_surface(target_surface);
+ if (!target_subsurface)
+ break;
+
+ parent_surface = ds_subsurface_get_parent(target_subsurface);
+ if (surface == parent_surface)
+ return true;
+
+ target_surface = parent_surface;
+ }
+
+ return false;
+}
+
+struct ds_surface *
+ds_surface_get_root_surface(struct ds_surface *surface)
+{
+ struct ds_subsurface *subsurface;
+
+ while (ds_surface_is_subsurface(surface)) {
+ subsurface = ds_subsurface_from_ds_surface(surface);
+ if (!subsurface)
+ break;
+
+ surface = ds_subsurface_get_parent(subsurface);
+ if (!surface)
+ return NULL;
+ }
+
+ return surface;
+}
+
+bool
+ds_surface_has_buffer(struct ds_surface *surface)
+{
+ return !!surface->buffer;
+}
+
+static void
+surface_handle_destroy(struct wl_client *client, struct wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+static void
+surface_handle_attach(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *buffer_resource,
+ int32_t dx, int32_t dy)
+{
+ struct ds_surface *surface;
+ struct ds_buffer *buffer = NULL;
+
+ if (buffer_resource) {
+ buffer = ds_buffer_from_resource(buffer_resource);
+ if (!buffer) {
+ wl_resource_post_error(buffer_resource, 0, "unknown buffer type");
+ return;
+ }
+ }
+
+ surface = wl_resource_get_user_data(resource);
+ surface->pending.committed |= DS_SURFACE_STATE_BUFFER;
+ surface->pending.dx = dx;
+ surface->pending.dy = dy;
+
+ if (surface->pending.buffer)
+ ds_buffer_unlock(surface->pending.buffer);
+
+ surface->pending.buffer = buffer;
+
+ ds_dbg("ds_surface(%p) attach buffer(%p)", surface, buffer);
+}
+
+static void
+surface_handle_damage(struct wl_client *client,
+ struct wl_resource *resource,
+ int32_t x, int32_t y, int32_t width, int32_t height)
+{
+ struct ds_surface *surface;
+
+ surface = wl_resource_get_user_data(resource);
+
+ ds_dbg("ds_surface(%p) damage: x %d y %d width %d height %d",
+ surface, x, y, width, height);
+
+ if (width < 0 || height < 0)
+ return;
+
+ surface->pending.committed |= DS_SURFACE_STATE_SURFACE_DAMAGE;
+ pixman_region32_union_rect(&surface->pending.surface_damage,
+ &surface->pending.surface_damage,
+ x, y, width, height);
+}
+
+static void
+callback_handle_resource_destroy(struct wl_resource *resource)
+{
+ wl_list_remove(wl_resource_get_link(resource));
+}
+
+static void
+surface_handle_frame(struct wl_client *client,
+ struct wl_resource *resource, uint32_t callback)
+{
+ struct ds_surface *surface;
+ struct wl_resource *callback_resource;
+
+ surface = wl_resource_get_user_data(resource);
+
+ ds_dbg("ds_surface(%p) frame", surface);
+
+ callback_resource = wl_resource_create(client, &wl_callback_interface,
+ CALLBACK_VERSION, callback);
+ if (!callback_resource) {
+ wl_resource_post_no_memory(resource);
+ return;
+ }
+
+ wl_resource_set_implementation(callback_resource, NULL, NULL,
+ callback_handle_resource_destroy);
+
+ wl_list_insert(surface->pending.frame_callback_list.prev,
+ wl_resource_get_link(callback_resource));
+
+ surface->pending.committed |= DS_SURFACE_STATE_FRAME_CALLBACK_LIST;
+}
+
+static void
+surface_handle_set_opaque_region(struct wl_client *client,
+ struct wl_resource *resource, struct wl_resource *region_resource)
+{
+ struct ds_surface *surface;
+ pixman_region32_t *region;
+
+ surface = wl_resource_get_user_data(resource);
+
+ ds_dbg("ds_surface(%p) set opaque region", surface);
+
+ surface->pending.committed |= DS_SURFACE_STATE_OPAQUE_REGION;
+ if (region_resource) {
+ region = ds_region_from_resource(region_resource);
+ pixman_region32_copy(&surface->pending.opaque, region);
+ }
+ else {
+ pixman_region32_clear(&surface->pending.opaque);
+ }
+}
+
+static void
+surface_handle_set_input_region(struct wl_client *client,
+ struct wl_resource *resource, struct wl_resource *region_resource)
+{
+ struct ds_surface *surface = wl_resource_get_user_data(resource);
+ pixman_region32_t *region;
+
+ ds_dbg("ds_surface(%p) set input region", surface);
+
+ surface->pending.committed |= DS_SURFACE_STATE_INPUT_REGION;
+ if (region_resource) {
+ region = ds_region_from_resource(region_resource);
+ pixman_region32_copy(&surface->pending.input, region);
+ }
+ else {
+ pixman_region32_fini(&surface->pending.input);
+ pixman_region32_init_rect(&surface->pending.input,
+ INT32_MIN, INT32_MIN, UINT32_MAX, UINT32_MAX);
+ }
+}
+
+static void
+surface_handle_commit(struct wl_client *client, struct wl_resource *resource)
+{
+ struct ds_surface *surface;
+
+ surface = wl_resource_get_user_data(resource);
+
+ ds_dbg("ds_surface(%p) commit", surface);
+
+ // TODO handle subsurface
+
+ surface_finalize_pending(surface);
+
+ surface_commit_state(surface, &surface->pending);
+
+ // TODO handle subsurfaces of a given surface
+
+ if (surface->role && surface->role->commit)
+ surface->role->commit(surface);
+
+ wl_signal_emit(&surface->events.commit, surface);
+}
+
+static void
+surface_handle_set_buffer_transform(struct wl_client *client,
+ struct wl_resource *resource, int32_t transform)
+{
+ struct ds_surface *surface = wl_resource_get_user_data(resource);
+
+ ds_dbg("ds_surface(%p) set buffer transform(%d)", surface, transform);
+
+ if (transform < WL_OUTPUT_TRANSFORM_NORMAL ||
+ transform > WL_OUTPUT_TRANSFORM_FLIPPED_270) {
+ ds_err("Specified transform value(%d) is invalid. ds_surface(%p)",
+ transform, surface);
+ wl_resource_post_error(resource, WL_SURFACE_ERROR_INVALID_TRANSFORM,
+ "Specified transform value(%d) is invalid", transform);
+ return;
+ }
+
+ surface->pending.committed |= DS_SURFACE_STATE_TRANSFORM;
+ surface->pending.transform = transform;
+}
+
+static void
+surface_handle_set_buffer_scale(struct wl_client *client,
+ struct wl_resource *resource, int32_t scale)
+{
+ struct ds_surface *surface = wl_resource_get_user_data(resource);
+
+ ds_dbg("ds_surface(%p) set buffer scale(%d)", surface, scale);
+
+ if (scale <= 0) {
+ ds_err("Specified scale value(%d) is not positive. ds_surface(%p)",
+ scale, surface);
+ wl_resource_post_error(resource, WL_SURFACE_ERROR_INVALID_SCALE,
+ "Specified scale value(%d) is not positive", scale);
+ return;
+ }
+
+ surface->pending.committed |= DS_SURFACE_STATE_SCALE;
+ surface->pending.scale = scale;
+}
+
+static void
+surface_handle_damage_buffer(struct wl_client *client,
+ struct wl_resource *resource,
+ int32_t x, int32_t y, int32_t width, int32_t height)
+{
+ struct ds_surface *surface = wl_resource_get_user_data(resource);
+
+ ds_dbg("ds_surface(%p) damage: x %d y %d width %d height %d",
+ surface, x, y, width, height);
+
+ if (width < 0 || height < 0) {
+ ds_err("Size cannot be less than zero. ds_surface(%p) "
+ "width %d height %d", surface, width, height);
+ return;
+ }
+
+ surface->pending.committed |= DS_SURFACE_STATE_BUFFER_DAMAGE;
+ pixman_region32_union_rect(&surface->pending.buffer_damage,
+ &surface->pending.buffer_damage,
+ x, y, width, height);
+}
+
+static const struct wl_surface_interface surface_impl = {
+ .destroy = surface_handle_destroy,
+ .attach = surface_handle_attach,
+ .damage = surface_handle_damage,
+ .frame = surface_handle_frame,
+ .set_opaque_region = surface_handle_set_opaque_region,
+ .set_input_region = surface_handle_set_input_region,
+ .commit = surface_handle_commit,
+ .set_buffer_transform = surface_handle_set_buffer_transform,
+ .set_buffer_scale = surface_handle_set_buffer_scale,
+ .damage_buffer = surface_handle_damage_buffer,
+};
+
+static void
+surface_handle_resource_destroy(struct wl_resource *resource)
+{
+ struct ds_surface *surface = wl_resource_get_user_data(resource);
+
+ ds_inf("Destroy ds_surface %p (res %p)", surface, surface->resource);
+
+ wl_signal_emit(&surface->events.destroy, surface);
+
+ if (surface->buffer)
+ ds_buffer_unlock(surface->buffer);
+
+ surface_state_finish(&surface->pending);
+ surface_state_finish(&surface->current);
+
+ pixman_region32_fini(&surface->buffer_damage);
+ pixman_region32_fini(&surface->opaque_region);
+ pixman_region32_fini(&surface->input_region);
+
+ free(surface);
+}
+
+static void
+surface_state_init(struct ds_surface_state *state)
+{
+ state->scale = 1;
+ state->transform = WL_OUTPUT_TRANSFORM_NORMAL;
+
+ wl_list_init(&state->subsurfaces_above);
+ wl_list_init(&state->subsurfaces_below);
+
+ wl_list_init(&state->frame_callback_list);
+
+ pixman_region32_init(&state->surface_damage);
+ pixman_region32_init(&state->buffer_damage);
+ pixman_region32_init(&state->opaque);
+ pixman_region32_init_rect(&state->input,
+ INT32_MIN, INT32_MIN, UINT32_MAX, UINT32_MAX);
+}
+
+static void
+surface_state_finish(struct ds_surface_state *state)
+{
+ struct wl_resource *resource, *tmp;
+
+ if (state->buffer)
+ ds_buffer_unlock(state->buffer);
+
+ wl_resource_for_each_safe(resource, tmp, &state->frame_callback_list)
+ wl_resource_destroy(resource);
+
+ pixman_region32_fini(&state->surface_damage);
+ pixman_region32_fini(&state->buffer_damage);
+ pixman_region32_fini(&state->opaque);
+ pixman_region32_fini(&state->input);
+}
+
+static void
+surface_state_move(struct ds_surface_state *state, struct ds_surface_state *next)
+{
+ state->width = next->width;
+ state->height = next->height;
+ state->buffer_width = next->buffer_width;
+ state->buffer_height = next->buffer_height;
+
+ if (next->committed & DS_SURFACE_STATE_SCALE)
+ state->scale = next->scale;
+
+ if (next->committed & DS_SURFACE_STATE_TRANSFORM)
+ state->transform = next->transform;
+
+ if (next->committed & DS_SURFACE_STATE_BUFFER) {
+ state->dx = next->dx;
+ state->dy = next->dy;
+ next->dx = next->dy = 0;
+
+ if (state->buffer) {
+ ds_buffer_unlock(state->buffer);
+ state->buffer = NULL;
+ }
+
+ if (next->buffer) {
+ state->buffer = ds_buffer_lock(next->buffer);
+ ds_buffer_unlock(next->buffer);
+ next->buffer = NULL;
+ }
+ }
+ else {
+ state->dx = state->dy = 0;
+ }
+
+ if (next->committed & DS_SURFACE_STATE_SURFACE_DAMAGE) {
+ pixman_region32_copy(&state->surface_damage, &next->surface_damage);
+ pixman_region32_clear(&next->surface_damage);
+ }
+ else
+ pixman_region32_clear(&state->surface_damage);
+
+ if (next->committed & DS_SURFACE_STATE_BUFFER_DAMAGE) {
+ pixman_region32_copy(&state->buffer_damage, &next->buffer_damage);
+ pixman_region32_clear(&next->buffer_damage);
+ }
+ else
+ pixman_region32_clear(&state->buffer_damage);
+
+ if (next->committed & DS_SURFACE_STATE_OPAQUE_REGION)
+ pixman_region32_copy(&state->opaque, &next->opaque);
+
+ if (next->committed & DS_SURFACE_STATE_INPUT_REGION)
+ pixman_region32_copy(&state->input, &next->input);
+
+ if (next->committed & DS_SURFACE_STATE_FRAME_CALLBACK_LIST) {
+ wl_list_insert_list(&state->frame_callback_list,
+ &next->frame_callback_list);
+ wl_list_init(&next->frame_callback_list);
+ }
+
+ // FIXME
+ // state->committed |= next->committed; ??
+ state->committed = next->committed;
+ next->committed = 0;
+}
+
+static void
+surface_state_viewport_src_size(struct ds_surface_state *state,
+ int *out_width, int *out_height)
+{
+ int width, height, tmp;
+
+ if (state->buffer_width == 0 && state->buffer_height == 0) {
+ *out_width = 0;
+ *out_height = 0;
+ return;
+ }
+
+ width = state->buffer_width / state->scale;
+ height = state->buffer_height / state->scale;
+ if ((state->transform & WL_OUTPUT_TRANSFORM_90) != 0) {
+ tmp = width;
+ width = height;
+ height = tmp;
+ }
+
+ *out_width = width;
+ *out_height = height;
+}
+
+static void
+surface_finalize_pending(struct ds_surface *surface)
+{
+ struct ds_surface_state *pending = &surface->pending;
+
+ if ((pending->committed & DS_SURFACE_STATE_BUFFER)) {
+ if (pending->buffer) {
+ ds_buffer_get_size(pending->buffer,
+ &pending->buffer_width, &pending->buffer_height);
+ }
+ else {
+ pending->buffer_width = 0;
+ pending->buffer_height = 0;
+ }
+ }
+
+ surface_state_viewport_src_size(pending,
+ &pending->width, &pending->height);
+
+ pixman_region32_intersect_rect(&pending->surface_damage,
+ &pending->surface_damage,
+ 0, 0, pending->width, pending->height);
+
+ pixman_region32_intersect_rect(&pending->buffer_damage,
+ &pending->buffer_damage,
+ 0, 0, pending->buffer_width, pending->buffer_height);
+}
+
+// FIXME
+static enum wl_output_transform
+ds_output_transform_invert(enum wl_output_transform tr)
+{
+ if ((tr & WL_OUTPUT_TRANSFORM_90) && !(tr & WL_OUTPUT_TRANSFORM_FLIPPED)) {
+ tr ^= WL_OUTPUT_TRANSFORM_180;
+ }
+ return tr;
+}
+
+static void
+surface_update_damage(pixman_region32_t *buffer_damage,
+ struct ds_surface_state *current, struct ds_surface_state *pending)
+{
+ pixman_region32_clear(buffer_damage);
+
+ if (pending->width != current->width ||
+ pending->height != current->height) {
+ pixman_region32_union_rect(buffer_damage, buffer_damage,
+ 0, 0, pending->buffer_width, pending->buffer_height);
+ }
+ else {
+ pixman_region32_t surface_damage;
+
+ pixman_region32_init(&surface_damage);
+ pixman_region32_copy(&surface_damage, &pending->surface_damage);
+
+ // TODO viewport
+
+ ds_region_transform(&surface_damage, &surface_damage,
+ ds_output_transform_invert(pending->transform),
+ pending->width, pending->height);
+ ds_region_scale(&surface_damage, &surface_damage, pending->scale);
+
+ pixman_region32_union(buffer_damage,
+ &pending->buffer_damage, &surface_damage);
+
+ pixman_region32_fini(&surface_damage);
+ }
+}
+
+static void
+surface_update_buffer(struct ds_surface *surface)
+{
+ if (!surface->current.buffer) {
+ if (surface->buffer) {
+ ds_buffer_unlock(surface->buffer);
+ surface->buffer = NULL;
+ }
+ return;
+ }
+
+ if (surface->buffer) {
+ ds_buffer_unlock(surface->buffer);
+ surface->buffer = NULL;
+ }
+
+ if (surface->current.buffer) {
+ surface->buffer = ds_buffer_lock(surface->current.buffer);
+ ds_buffer_unlock(surface->current.buffer);
+ surface->current.buffer = NULL;
+ }
+}
+
+static void
+surface_commit_state(struct ds_surface *surface, struct ds_surface_state *next)
+{
+ surface->sx += next->dx;
+ surface->sy += next->dy;
+ surface_update_damage(&surface->buffer_damage, &surface->current, next);
+
+ surface_state_move(&surface->current, next);
+
+ // FIXME no need?
+ if (surface->current.committed & DS_SURFACE_STATE_BUFFER)
+ surface_update_buffer(surface);
+
+ wl_signal_emit(&surface->events.commit, surface);
+}
+
+static bool
+surface_for_each(struct ds_surface *surface, int x, int y,
+ ds_surface_for_each_func_t iterator, void *data)
+{
+ struct ds_subsurface *subsurface;
+ struct ds_subsurface_parent_state *state;
+ bool stop = false;
+
+ wl_list_for_each(subsurface,
+ &surface->current.subsurfaces_below, current.link) {
+ state = &subsurface->current;
+ stop = surface_for_each(subsurface->surface,
+ x + state->x, y + state->y, iterator, data);
+ if (stop)
+ return true;
+ }
+
+ stop = iterator(surface, x, y, data);
+ if (stop)
+ return true;
+
+ wl_list_for_each(subsurface,
+ &surface->current.subsurfaces_above, current.link) {
+ state = &subsurface->current;
+ stop = surface_for_each(subsurface->surface,
+ x + state->x, y + state->y, iterator, data);
+ if (stop)
+ return true;
+ }
+
+ return false;
+}
--- /dev/null
+#include <assert.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include "libds/log.h"
+#include "libds/allocator.h"
+
+#define DS_SWAPCHAIN_CAP 4
+
+struct ds_swapchain_slot
+{
+ struct ds_buffer *buffer;
+
+ struct wl_listener buffer_release;
+
+ int age;
+ bool acquired;
+};
+
+struct ds_swapchain
+{
+ struct ds_allocator *allocator;
+
+ struct ds_swapchain_slot slots[DS_SWAPCHAIN_CAP];
+
+ struct wl_listener allocator_destroy;
+
+ int width, height;
+ uint32_t format;
+};
+
+static void swapchain_handle_allocator_destroy(struct wl_listener *listener,
+ void *data);
+static bool swapchain_has_buffer(struct ds_swapchain *swapchain,
+ struct ds_buffer *buffer);
+static struct ds_buffer *swapchain_slot_acquire(struct ds_swapchain *swapchain,
+ struct ds_swapchain_slot *slot, int *age);
+static void swapchain_slot_reset(struct ds_swapchain_slot *slot);
+
+struct ds_swapchain *
+ds_swapchain_create(struct ds_allocator *alloc, int width, int height,
+ uint32_t format)
+{
+ struct ds_swapchain *swapchain = NULL;
+
+ swapchain = calloc(1, sizeof *swapchain);
+ if (!swapchain)
+ return NULL;
+
+ swapchain->allocator = alloc;
+ swapchain->width = width;
+ swapchain->height = height;
+ swapchain->format = format;
+
+ swapchain->allocator_destroy.notify =
+ swapchain_handle_allocator_destroy;
+ ds_allocator_add_destroy_listener(alloc,
+ &swapchain->allocator_destroy);
+
+ ds_inf("Swapchain(%p) created", swapchain);
+
+ return swapchain;
+}
+
+void
+ds_swapchain_destroy(struct ds_swapchain *swapchain)
+{
+ size_t i;
+
+ ds_dbg("Destroy swapchain(%p)", swapchain);
+
+ for (i = 0; i < DS_SWAPCHAIN_CAP; i++)
+ swapchain_slot_reset(&swapchain->slots[i]);
+
+ wl_list_remove(&swapchain->allocator_destroy.link);
+ free(swapchain);
+}
+
+struct ds_buffer *
+ds_swapchain_acquire(struct ds_swapchain *swapchain, int *age)
+{
+ struct ds_swapchain_slot *slot, *free_slot = NULL;
+ size_t i;
+
+ for (i = 0; i < DS_SWAPCHAIN_CAP; i++) {
+ slot = &swapchain->slots[i];
+ if (slot->acquired)
+ continue;
+
+ if (slot->buffer != NULL)
+ return swapchain_slot_acquire(swapchain, slot, age);
+
+ free_slot = slot;
+ }
+
+ if (free_slot == NULL) {
+ ds_err("No free output buffer slot");
+ return NULL;
+ }
+
+ if (!swapchain->allocator)
+ return NULL;
+
+ free_slot->buffer = ds_allocator_create_buffer(swapchain->allocator,
+ swapchain->width, swapchain->height, swapchain->format);
+ if (!free_slot->buffer) {
+ ds_err("Failed to allocate buffer");
+ return NULL;
+ }
+
+ ds_dbg("Allocating new swapchain buffer(%p)", free_slot->buffer);
+
+ return swapchain_slot_acquire(swapchain, free_slot, age);
+}
+
+void
+ds_swapchain_set_buffer_submitted(struct ds_swapchain *swapchain,
+ struct ds_buffer *buffer)
+{
+ struct ds_swapchain_slot *slot;
+ size_t i;
+
+ assert(buffer);
+
+ if (!swapchain_has_buffer(swapchain, buffer))
+ return;
+
+ for (i = 0; i < DS_SWAPCHAIN_CAP; i++) {
+ slot = &swapchain->slots[i];
+ if (slot->buffer == buffer)
+ slot->age = 1;
+ else if (slot->age > 0)
+ slot->age++;
+ }
+}
+
+static void
+swapchain_handle_allocator_destroy(struct wl_listener *listener, void *data)
+{
+ struct ds_swapchain *swapchain;
+
+ swapchain = wl_container_of(listener, swapchain, allocator_destroy);
+ swapchain->allocator = NULL;
+}
+
+static bool swapchain_has_buffer(struct ds_swapchain *swapchain,
+ struct ds_buffer *buffer)
+{
+ struct ds_swapchain_slot *slot;
+ size_t i;
+
+ for (i = 0; i < DS_SWAPCHAIN_CAP; i++) {
+ slot = &swapchain->slots[i];
+ if (slot->buffer == buffer)
+ return true;
+ }
+
+ return false;
+}
+
+static void
+swapchain_slot_handle_buffer_release(struct wl_listener *listener, void *data)
+{
+ struct ds_swapchain_slot *slot;
+
+ slot = wl_container_of(listener, slot, buffer_release);
+
+ ds_dbg("Buffer(%p) released.", slot->buffer);
+
+ wl_list_remove(&slot->buffer_release.link);
+ slot->acquired = false;
+}
+
+static struct ds_buffer *
+swapchain_slot_acquire(struct ds_swapchain *swapchain, struct ds_swapchain_slot *slot,
+ int *age)
+{
+ assert(!slot->acquired);
+ assert(slot->buffer);
+
+ slot->acquired = true;
+
+ slot->buffer_release.notify = swapchain_slot_handle_buffer_release;
+ ds_buffer_add_release_listener(slot->buffer, &slot->buffer_release);
+
+ if (age != NULL)
+ *age = slot->age;
+
+ return ds_buffer_lock(slot->buffer);
+}
+
+static void
+swapchain_slot_reset(struct ds_swapchain_slot *slot)
+{
+ if (slot->acquired)
+ wl_list_remove(&slot->buffer_release.link);
+
+ if (slot->buffer)
+ ds_buffer_drop(slot->buffer);
+
+ memset(slot, 0, sizeof *slot);
+}
--- /dev/null
+#ifndef DS_UTIL_H
+#define DS_UTIL_H
+
+#include <stdint.h>
+#include <time.h>
+
+int64_t
+timespec_to_msec(const struct timespec *a);
+
+int
+allocate_shm_file(size_t size);
+
+#endif
--- /dev/null
+/*
+ * Copyright © 2012 Collabora, Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#define _POSIX_C_SOURCE 200809L
+
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mman.h>
+
+int
+os_fd_set_cloexec(int fd)
+{
+ long flags;
+
+ if (fd == -1)
+ return -1;
+
+ flags = fcntl(fd, F_GETFD);
+ if (flags == -1)
+ return -1;
+
+ if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1)
+ return -1;
+
+ return 0;
+}
+
+static int
+set_cloexec_or_close(int fd)
+{
+ if (os_fd_set_cloexec(fd) != 0) {
+ close(fd);
+ return -1;
+ }
+ return fd;
+}
+
+static int
+create_tmpfile_cloexec(char *tmpname)
+{
+ int fd;
+
+#ifdef HAVE_MKOSTEMP
+ fd = mkostemp(tmpname, O_CLOEXEC);
+ if (fd >= 0)
+ unlink(tmpname);
+#else
+ fd = mkstemp(tmpname);
+ if (fd >= 0) {
+ fd = set_cloexec_or_close(fd);
+ unlink(tmpname);
+ }
+#endif
+
+ return fd;
+}
+
+/*
+ * Create a new, unique, anonymous file of the given size, and
+ * return the file descriptor for it. The file descriptor is set
+ * CLOEXEC. The file is immediately suitable for mmap()'ing
+ * the given size at offset zero.
+ *
+ * The file should not have a permanent backing store like a disk,
+ * but may have if XDG_RUNTIME_DIR is not properly implemented in OS.
+ *
+ * The file name is deleted from the file system.
+ *
+ * The file is suitable for buffer sharing between processes by
+ * transmitting the file descriptor over Unix sockets using the
+ * SCM_RIGHTS methods.
+ *
+ * If the C library implements posix_fallocate(), it is used to
+ * guarantee that disk space is available for the file at the
+ * given size. If disk space is insufficient, errno is set to ENOSPC.
+ * If posix_fallocate() is not supported, program may receive
+ * SIGBUS on accessing mmap()'ed file contents instead.
+ *
+ * If the C library implements memfd_create(), it is used to create the
+ * file purely in memory, without any backing file name on the file
+ * system, and then sealing off the possibility of shrinking it. This
+ * can then be checked before accessing mmap()'ed file contents, to
+ * make sure SIGBUS can't happen. It also avoids requiring
+ * XDG_RUNTIME_DIR.
+ */
+int
+allocate_shm_file(off_t size)
+{
+ static const char template[] = "/weston-shared-XXXXXX";
+ const char *path;
+ char *name;
+ int fd;
+ int ret;
+
+#ifdef HAVE_MEMFD_CREATE
+ fd = memfd_create("weston-shared", MFD_CLOEXEC | MFD_ALLOW_SEALING);
+ if (fd >= 0) {
+ /* We can add this seal before calling posix_fallocate(), as
+ * the file is currently zero-sized anyway.
+ *
+ * There is also no need to check for the return value, we
+ * couldn't do anything with it anyway.
+ */
+ fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK);
+ } else
+#endif
+ {
+ path = getenv("XDG_RUNTIME_DIR");
+ if (!path) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ name = malloc(strlen(path) + sizeof(template));
+ if (!name)
+ return -1;
+
+ strcpy(name, path);
+ strcat(name, template);
+
+ fd = create_tmpfile_cloexec(name);
+
+ free(name);
+
+ if (fd < 0)
+ return -1;
+ }
+
+#ifdef HAVE_POSIX_FALLOCATE
+ do {
+ ret = posix_fallocate(fd, 0, size);
+ } while (ret == EINTR);
+ if (ret != 0) {
+ close(fd);
+ errno = ret;
+ return -1;
+ }
+#else
+ do {
+ ret = ftruncate(fd, size);
+ } while (ret < 0 && errno == EINTR);
+ if (ret < 0) {
+ close(fd);
+ return -1;
+ }
+#endif
+
+ return fd;
+}
--- /dev/null
+#define _POSIX_C_SOURCE 200809L
+
+#include <stdint.h>
+#include <time.h>
+
+int64_t timespec_to_msec(const struct timespec *a) {
+ return (int64_t)a->tv_sec * 1000 + a->tv_nsec / 1000000;
+}
--- /dev/null
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "xdg_shell.h"
+#include "libds/log.h"
+#include "libds/xdg_shell.h"
+
+#define XDG_WM_BASE_VERSION 2
+#define XDG_SHELL_PING_TIMEOUT 10000
+
+static void xdg_shell_handle_display_destroy(struct wl_listener *listener, void *data);
+static void xdg_shell_bind(struct wl_client *wl_client, void *data,
+ uint32_t verison, uint32_t id);
+
+WL_EXPORT struct ds_xdg_shell *
+ds_xdg_shell_create(struct wl_display *display)
+{
+ struct ds_xdg_shell *shell;
+
+ shell = calloc(1, sizeof *shell);
+ if (!shell) {
+ return NULL;
+ }
+
+ shell->ping_timeout = XDG_SHELL_PING_TIMEOUT;
+
+ wl_list_init(&shell->clients);
+
+ shell->global = wl_global_create(display, &xdg_wm_base_interface,
+ XDG_WM_BASE_VERSION, shell, xdg_shell_bind);
+ if (!shell->global) {
+ free(shell);
+ return NULL;
+ }
+
+ wl_signal_init(&shell->events.destroy);
+ wl_signal_init(&shell->events.new_surface);
+
+ shell->display_destroy.notify = xdg_shell_handle_display_destroy;
+ wl_display_add_destroy_listener(display, &shell->display_destroy);
+
+ ds_inf("Global created: xdg_wm_base shell(%p)", shell);
+
+ return shell;
+}
+
+WL_EXPORT void
+ds_xdg_shell_add_destroy_listener(struct ds_xdg_shell *shell,
+ struct wl_listener *listener)
+{
+ wl_signal_add(&shell->events.destroy, listener);
+}
+
+void
+ds_xdg_shell_add_new_surface_listener(struct ds_xdg_shell *shell,
+ struct wl_listener *listener)
+{
+ wl_signal_add(&shell->events.new_surface, listener);
+}
+
+static void
+xdg_shell_handle_display_destroy(struct wl_listener *listener, void *data)
+{
+ struct ds_xdg_shell *shell;
+
+ shell = wl_container_of(listener, shell, display_destroy);
+
+ ds_inf("Global destroy: xdg_wm_base shell(%p)", shell);
+
+ wl_signal_emit(&shell->events.destroy, shell);
+ wl_list_remove(&shell->display_destroy.link);
+ wl_global_destroy(shell->global);
+ free(shell);
+}
+
+static void
+xdg_shell_handle_destroy(struct wl_client *wl_client,
+ struct wl_resource *resource)
+{
+ struct ds_xdg_client *client;
+
+ client = wl_resource_get_user_data(resource);
+
+ if (!wl_list_empty(&client->surfaces)) {
+ wl_resource_post_error(client->resource,
+ XDG_WM_BASE_ERROR_DEFUNCT_SURFACES,
+ "xdg_wm_base was destroyed before children");
+ return;
+ }
+
+ wl_resource_destroy(resource);
+}
+
+static void
+xdg_shell_handle_create_positioner(struct wl_client *wl_client,
+ struct wl_resource *resource, uint32_t id)
+{
+ // TODO
+}
+
+static void
+xdg_shell_handle_get_xdg_surface(struct wl_client *wl_client,
+ struct wl_resource *resource, uint32_t id,
+ struct wl_resource *surface_resource)
+{
+ struct ds_xdg_client *client;
+ struct ds_surface *surface;
+
+ client = wl_resource_get_user_data(resource);
+ surface = ds_surface_from_resource(surface_resource);
+ create_xdg_surface(client, surface, id);
+}
+
+static void
+xdg_shell_handle_pong(struct wl_client *wl_client,
+ struct wl_resource *resource, uint32_t serial)
+{
+ struct ds_xdg_client *client;
+
+ client = wl_resource_get_user_data(resource);
+ if (client->ping_serial != serial)
+ return;
+
+ wl_event_source_timer_update(client->ping_timer, 0);
+ client->ping_serial = 0;
+}
+
+static const struct xdg_wm_base_interface xdg_shell_impl = {
+ .destroy = xdg_shell_handle_destroy,
+ .create_positioner = xdg_shell_handle_create_positioner,
+ .get_xdg_surface = xdg_shell_handle_get_xdg_surface,
+ .pong = xdg_shell_handle_pong,
+};
+
+static void
+xdg_client_handle_resource_destroy(struct wl_resource *resource)
+{
+ struct ds_xdg_client *client;
+
+ client = wl_resource_get_user_data(resource);
+
+ if (client->ping_timer != NULL)
+ wl_event_source_remove(client->ping_timer);
+
+ wl_list_remove(&client->link);
+ free(client);
+}
+
+static int
+xdg_client_handle_ping_timeout(void *user_data)
+{
+ struct ds_xdg_client *client;
+ struct ds_xdg_surface *surface;
+
+ client = user_data;
+
+ wl_list_for_each(surface, &client->surfaces, link)
+ wl_signal_emit(&surface->events.ping_timeout, surface);
+
+ client->ping_serial = 0;
+
+ return 1;
+}
+
+static void
+xdg_client_init_ping_timer(struct ds_xdg_client *client)
+{
+ struct wl_display *display;
+ struct wl_event_loop *loop;
+
+ display = wl_client_get_display(client->wl_client);
+ loop = wl_display_get_event_loop(display);
+ client->ping_timer = wl_event_loop_add_timer(loop,
+ xdg_client_handle_ping_timeout, client);
+ if (client->ping_timer == NULL)
+ wl_client_post_no_memory(client->wl_client);
+}
+
+static void
+xdg_shell_bind(struct wl_client *wl_client, void *data, uint32_t version,
+ uint32_t id)
+{
+ struct ds_xdg_shell *shell;
+ struct ds_xdg_client *client;
+
+ shell = data;
+
+ client = calloc(1, sizeof *client);
+ if (client == NULL) {
+ wl_client_post_no_memory(wl_client);
+ return;
+ }
+
+ client->wl_client = wl_client;
+ client->shell = shell;
+
+ wl_list_init(&client->surfaces);
+
+ client->resource =
+ wl_resource_create(wl_client, &xdg_wm_base_interface, version, id);
+ if (client->resource == NULL) {
+ free(client);
+ wl_client_post_no_memory(wl_client);
+ return;
+ }
+
+ wl_resource_set_implementation(client->resource, &xdg_shell_impl, client,
+ xdg_client_handle_resource_destroy);
+
+ wl_list_insert(&shell->clients, &client->link);
+
+ xdg_client_init_ping_timer(client);
+}
--- /dev/null
+#ifndef DS_XDG_SHELL_H
+#define DS_XDG_SHELL_H
+
+#include <wayland-server.h>
+
+#include "xdg-shell-server-protocol.h"
+
+#include "surface.h"
+#include "libds/output.h"
+
+typedef enum
+{
+ DS_XDG_SURFACE_ROLE_NONE,
+ DS_XDG_SURFACE_ROLE_TOPLEVEL,
+ DS_XDG_SURFACE_ROLE_POPUP,
+} ds_xdg_surface_role_t;
+
+struct ds_xdg_shell
+{
+ struct wl_global *global;
+
+ struct wl_list clients;
+
+ struct wl_listener display_destroy;
+
+ struct {
+ struct wl_signal destroy;
+ struct wl_signal new_surface;
+ } events;
+
+ uint32_t ping_timeout;
+};
+
+struct ds_xdg_client
+{
+ struct ds_xdg_shell *shell;
+
+ struct wl_resource *resource;
+ struct wl_client *wl_client;
+ struct wl_event_source *ping_timer;
+
+ struct wl_list surfaces;
+
+ struct wl_list link; // ds_xdg_shell::clients
+
+ uint32_t ping_serial;
+};
+
+struct ds_xdg_toplevel_state
+{
+ bool maximized, fullscreen, resizing, activated;
+ uint32_t tiled;
+ uint32_t width, height;
+ uint32_t max_width, max_height;
+ uint32_t min_width, min_height;
+};
+
+struct ds_xdg_toplevel_configure
+{
+ bool maximized, fullscreen, resizing, activated;
+ uint32_t tiled;
+ uint32_t width, height;
+};
+
+struct ds_xdg_toplevel_requested
+{
+ bool maximized, minimized, fullscreen;
+ struct ds_output *fullscreen_output;
+ struct wl_listener fullscreen_output_destroy;
+};
+
+struct ds_xdg_toplevel
+{
+ struct wl_resource *resource;
+ struct ds_xdg_surface *base;
+ bool added;
+
+ struct ds_xdg_surface *parent;
+ struct wl_listener parent_unmap;
+
+ struct ds_xdg_toplevel_state current, pending;
+ struct ds_xdg_toplevel_configure scheduled;
+ struct ds_xdg_toplevel_requested requested;
+
+ char *title;
+ char *app_id;
+
+ struct {
+ struct wl_signal request_maximize;
+ struct wl_signal request_fullscreen;
+ struct wl_signal request_minimize;
+ struct wl_signal request_move;
+ struct wl_signal request_resize;
+ struct wl_signal request_show_window_menu;
+ struct wl_signal set_parent;
+ struct wl_signal set_title;
+ struct wl_signal set_app_id;
+ } events;
+};
+
+struct ds_xdg_popup
+{
+
+};
+
+struct ds_xdg_surface_state
+{
+ uint32_t configure_serial;
+ struct {
+ int x, y;
+ int width, height;
+ } geometry;
+};
+
+struct ds_xdg_surface
+{
+ struct ds_xdg_client *client;
+ struct ds_surface *ds_surface;
+
+ ds_xdg_surface_role_t role;
+
+ union {
+ struct ds_xdg_toplevel *toplevel;
+ struct ds_xdg_popup *popup;
+ };
+
+ struct wl_resource *resource;
+
+ struct wl_event_source *configure_idle;
+ uint32_t scheduled_serial;
+ struct wl_list configure_list;
+
+ struct ds_xdg_surface_state current, pending;
+
+ struct wl_list link; // ds_xdg_client::surfaces
+
+ struct {
+ struct wl_listener surface_destroy;
+ struct wl_listener surface_commit;
+ } listener;
+
+ struct {
+ struct wl_signal destroy;
+ struct wl_signal ping_timeout;
+ struct wl_signal new_popup;
+ struct wl_signal map;
+ struct wl_signal unmap;
+ struct wl_signal configure;
+ struct wl_signal ack_configure;
+ } events;
+
+ bool added, configured, mapped;
+};
+
+struct ds_xdg_surface_configure
+{
+ struct ds_xdg_surface *surface;
+ struct wl_list link;
+ uint32_t serial;
+
+ struct ds_xdg_toplevel_configure *toplevel_configure;
+};
+
+uint32_t
+ds_xdg_surface_schedule_configure(struct ds_xdg_surface *surface);
+
+struct ds_xdg_surface *
+create_xdg_surface(struct ds_xdg_client *client, struct ds_surface *surface,
+ uint32_t id);
+
+void
+reset_xdg_surface(struct ds_xdg_surface *surface);
+
+void
+create_xdg_toplevel(struct ds_xdg_surface *surface, uint32_t id);
+
+void
+handle_xdg_surface_commit(struct ds_surface *ds_surface);
+
+void
+handle_xdg_surface_toplevel_committed(struct ds_xdg_surface *surface);
+
+void
+send_xdg_toplevel_configure(struct ds_xdg_surface *surface,
+ struct ds_xdg_surface_configure *configure);
+
+#endif
--- /dev/null
+#include <assert.h>
+#include <stdlib.h>
+
+#include "xdg_shell.h"
+#include "libds/log.h"
+
+static const struct xdg_surface_interface xdg_surface_impl;
+static void xdg_surface_handle_surface_destroy(struct wl_listener *listener,
+ void *data);
+static void xdg_surface_handle_surface_commit(struct wl_listener *listener,
+ void *data);
+static void xdg_surface_handle_resource_destroy(struct wl_resource *resource);
+static void xdg_surface_configure_destroy(struct ds_xdg_surface_configure *configure);
+static void surface_send_configure(void *user_data);
+
+void
+ds_xdg_surface_add_destroy_listener(struct ds_xdg_surface *surface,
+ struct wl_listener *listener)
+{
+ wl_signal_add(&surface->events.destroy, listener);
+}
+
+WL_EXPORT void
+ds_xdg_surface_add_map_listener(struct ds_xdg_surface *surface,
+ struct wl_listener *listener)
+{
+ wl_signal_add(&surface->events.map, listener);
+}
+
+WL_EXPORT void
+ds_xdg_surface_add_unmap_listener(struct ds_xdg_surface *surface,
+ struct wl_listener *listener)
+{
+ wl_signal_add(&surface->events.unmap, listener);
+}
+
+struct ds_surface *
+ds_xdg_surface_get_surface(struct ds_xdg_surface *surface)
+{
+ return surface->ds_surface;
+}
+
+struct ds_xdg_surface *
+create_xdg_surface(struct ds_xdg_client *client, struct ds_surface *ds_surface,
+ uint32_t id)
+{
+ struct ds_xdg_surface *surface;
+
+ surface = calloc(1, sizeof *surface);
+ if (!surface) {
+ wl_client_post_no_memory(client->wl_client);
+ return NULL;
+ }
+
+ surface->client = client;
+ surface->role = DS_XDG_SURFACE_ROLE_NONE;
+ surface->ds_surface = ds_surface;
+ surface->resource = wl_resource_create(client->wl_client,
+ &xdg_surface_interface, wl_resource_get_version(client->resource),
+ id);
+ if (!surface->resource) {
+ free(surface);
+ wl_client_post_no_memory(client->wl_client);
+ return NULL;
+ }
+
+ if (ds_surface_has_buffer(surface->ds_surface)) {
+ wl_resource_destroy(surface->resource);
+ free(surface);
+ wl_resource_post_error(client->resource,
+ XDG_SURFACE_ERROR_UNCONFIGURED_BUFFER,
+ "xdg_surface must not have a buffer at creation");
+ return NULL;
+ }
+
+ wl_list_init(&surface->configure_list);
+
+ wl_signal_init(&surface->events.destroy);
+ wl_signal_init(&surface->events.ping_timeout);
+ wl_signal_init(&surface->events.new_popup);
+ wl_signal_init(&surface->events.map);
+ wl_signal_init(&surface->events.unmap);
+ wl_signal_init(&surface->events.configure);
+ wl_signal_init(&surface->events.ack_configure);
+
+ surface->listener.surface_destroy.notify =
+ xdg_surface_handle_surface_destroy;
+ ds_surface_add_destroy_listener(ds_surface,
+ &surface->listener.surface_destroy);
+
+ surface->listener.surface_commit.notify =
+ xdg_surface_handle_surface_commit;
+ ds_surface_add_commit_listener(ds_surface,
+ &surface->listener.surface_commit);
+
+ wl_resource_set_implementation(surface->resource, &xdg_surface_impl,
+ surface, xdg_surface_handle_resource_destroy);
+
+ wl_list_insert(&client->surfaces, &surface->link);
+
+ ds_inf("New xdg_surface %p (res %p)", surface, surface->resource);
+
+ return surface;
+}
+
+void
+unmap_xdg_surface(struct ds_xdg_surface *surface)
+{
+ struct ds_xdg_surface_configure *configure, *tmp;
+
+ // TODO handle popup
+
+ if (surface->mapped)
+ wl_signal_emit(&surface->events.unmap, surface);
+
+ switch (surface->role) {
+ case DS_XDG_SURFACE_ROLE_TOPLEVEL:
+ if (surface->toplevel->parent) {
+ wl_list_remove(&surface->toplevel->parent_unmap.link);
+ surface->toplevel->parent = NULL;
+ }
+ free(surface->toplevel->title);
+ surface->toplevel->title = NULL;
+ free(surface->toplevel->app_id);
+ surface->toplevel->app_id = NULL;
+ break;
+ case DS_XDG_SURFACE_ROLE_POPUP:
+ // TODO
+ break;
+ case DS_XDG_SURFACE_ROLE_NONE:
+ assert(false && "not reached");
+ }
+
+ wl_list_for_each_safe(configure, tmp, &surface->configure_list, link)
+ xdg_surface_configure_destroy(configure);
+
+ if (surface->configure_idle) {
+ wl_event_source_remove(surface->configure_idle);
+ surface->configure_idle = NULL;
+ }
+
+ surface->configured = false;
+ surface->mapped = false;
+}
+
+void
+reset_xdg_surface(struct ds_xdg_surface *surface)
+{
+ struct ds_xdg_toplevel_requested *req;
+
+ if (surface->role != DS_XDG_SURFACE_ROLE_NONE)
+ unmap_xdg_surface(surface);
+
+ if (surface->added) {
+ wl_signal_emit(&surface->events.destroy, surface);
+ surface->added = false;
+ }
+
+ switch (surface->role) {
+ case DS_XDG_SURFACE_ROLE_TOPLEVEL:
+ wl_resource_set_user_data(surface->toplevel->resource, NULL);
+ surface->toplevel->resource = NULL;
+ req = &surface->toplevel->requested;
+ if (req->fullscreen_output)
+ wl_list_remove(&req->fullscreen_output_destroy.link);
+ free(surface->toplevel);
+ surface->toplevel = NULL;
+ break;
+ case DS_XDG_SURFACE_ROLE_POPUP:
+ // TODO
+ break;
+ case DS_XDG_SURFACE_ROLE_NONE:
+ // This space is intentionally left blank
+ break;
+ }
+
+ surface->role = DS_XDG_SURFACE_ROLE_NONE;
+}
+
+void
+destroy_xdg_surface(struct ds_xdg_surface *surface)
+{
+ reset_xdg_surface(surface);
+
+ wl_resource_set_user_data(surface->resource, NULL);
+
+ ds_surface_reset_role_data(surface->ds_surface);
+
+ wl_list_remove(&surface->link);
+ wl_list_remove(&surface->listener.surface_destroy.link);
+ wl_list_remove(&surface->listener.surface_commit.link);
+
+ free(surface);
+}
+
+void
+handle_xdg_surface_commit(struct ds_surface *ds_surface)
+{
+ struct ds_xdg_surface *surface;
+
+ surface = ds_surface_get_role_data(ds_surface);
+ surface->current = surface->pending;
+
+ switch (surface->role) {
+ case DS_XDG_SURFACE_ROLE_NONE:
+ // inert toplevel or popup
+ break;
+ case DS_XDG_SURFACE_ROLE_TOPLEVEL:
+ handle_xdg_surface_toplevel_committed(surface);
+ // TODO
+ break;
+ case DS_XDG_SURFACE_ROLE_POPUP:
+ // TODO
+ break;
+ }
+
+ if (!surface->added) {
+ surface->added = true;
+ wl_signal_emit(&surface->client->shell->events.new_surface, surface);
+ }
+
+ if (surface->configured &&
+ ds_surface_has_buffer(surface->ds_surface) &&
+ !surface->mapped) {
+ surface->mapped = true;
+ wl_signal_emit(&surface->events.map, surface);
+ }
+}
+
+void handle_xdg_surface_precommit(struct ds_surface *ds_surface)
+{
+ struct ds_xdg_surface *surface;
+
+ surface = ds_surface_get_role_data(ds_surface);
+
+ // TODO
+ (void)surface;
+}
+
+uint32_t
+ds_xdg_surface_schedule_configure(struct ds_xdg_surface *surface)
+{
+ struct wl_display *display;
+ struct wl_event_loop *loop;
+
+ display = wl_client_get_display(surface->client->wl_client);
+ loop = wl_display_get_event_loop(display);
+
+ if (!surface->configure_idle) {
+ surface->scheduled_serial = wl_display_next_serial(display);
+ surface->configure_idle = wl_event_loop_add_idle(loop,
+ surface_send_configure, surface);
+ if (!surface->configure_idle)
+ wl_client_post_no_memory(surface->client->wl_client);
+ }
+
+ return surface->scheduled_serial;
+}
+
+void
+ds_xdg_surface_ping(struct ds_xdg_surface *surface)
+{
+}
+
+static void
+xdg_surface_handle_surface_destroy(struct wl_listener *listener, void *data)
+{
+ struct ds_xdg_surface *surface;
+
+ surface = wl_container_of(listener, surface, listener.surface_destroy);
+ destroy_xdg_surface(surface);
+}
+
+static void
+xdg_surface_handle_surface_commit(struct wl_listener *listener, void *data)
+{
+ struct ds_xdg_surface *surface;
+
+ surface = wl_container_of(listener, surface, listener.surface_commit);
+
+ if (ds_surface_has_buffer(surface->ds_surface) &&
+ !surface->configured) {
+ wl_resource_post_error(surface->resource,
+ XDG_SURFACE_ERROR_UNCONFIGURED_BUFFER,
+ "xdg_surface has never been configured");
+ return;
+ }
+
+ if (!ds_surface_get_role(surface->ds_surface)) {
+ wl_resource_post_error(surface->resource,
+ XDG_SURFACE_ERROR_NOT_CONSTRUCTED,
+ "xdg_surface must have a role");
+ return;
+ }
+}
+
+static void
+xdg_surface_handle_resource_destroy(struct wl_resource *resource)
+{
+ struct ds_xdg_surface *surface;
+
+ surface = wl_resource_get_user_data(resource);
+ if (!surface)
+ return;
+
+ destroy_xdg_surface(surface);
+}
+
+static void
+xdg_surface_configure_destroy(struct ds_xdg_surface_configure *configure)
+{
+ wl_list_remove(&configure->link);
+ free(configure->toplevel_configure);
+ free(configure);
+}
+
+static void
+xdg_surface_handle_destroy(struct wl_client *client,
+ struct wl_resource *resource)
+{
+ struct ds_xdg_surface *surface;
+
+ surface = wl_resource_get_user_data(resource);
+
+ if (surface->role != DS_XDG_SURFACE_ROLE_NONE) {
+ ds_err("Tried to destroy an xdg_surface before its role object");
+ return;
+ }
+
+ wl_resource_destroy(resource);
+}
+
+static void
+xdg_surface_handle_get_toplevel(struct wl_client *client,
+ struct wl_resource *resource, uint32_t id)
+{
+ struct ds_xdg_surface *surface;
+
+ surface = wl_resource_get_user_data(resource);
+ create_xdg_toplevel(surface, id);
+}
+
+static void
+xdg_surface_handle_get_popup(struct wl_client *client,
+ struct wl_resource *resource, uint32_t id,
+ struct wl_resource *parent_resource,
+ struct wl_resource *positioner_resource)
+{
+ struct ds_xdg_surface *surface;
+
+ surface = wl_resource_get_user_data(resource);
+
+ // TODO
+ (void)surface;
+}
+
+static void
+xdg_surface_handle_ack_configure(struct wl_client *client,
+ struct wl_resource *resource, uint32_t serial)
+{
+ struct ds_xdg_surface *surface;
+
+ surface = wl_resource_get_user_data(resource);
+
+ if (surface->role == DS_XDG_SURFACE_ROLE_NONE) {
+ wl_resource_post_error(surface->resource,
+ XDG_SURFACE_ERROR_NOT_CONSTRUCTED,
+ "xdg_surface must have a role");
+ return;
+ }
+
+ bool found = false;
+ struct ds_xdg_surface_configure *configure, *tmp;
+ wl_list_for_each(configure, &surface->configure_list, link) {
+ if (configure->serial == serial) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ wl_resource_post_error(surface->client->resource,
+ XDG_WM_BASE_ERROR_INVALID_SURFACE_STATE,
+ "wrong configure serial: %u", serial);
+ return;
+ }
+
+ wl_list_for_each_safe(configure, tmp, &surface->configure_list, link) {
+ if (configure->serial == serial)
+ break;
+
+ wl_signal_emit(&surface->events.ack_configure, configure);
+ xdg_surface_configure_destroy(configure);
+ }
+
+ switch (surface->role) {
+ case DS_XDG_SURFACE_ROLE_NONE:
+ assert(0 && "not reached");
+ break;
+ case DS_XDG_SURFACE_ROLE_TOPLEVEL:
+ // TODO
+ break;
+ case DS_XDG_SURFACE_ROLE_POPUP:
+ break;
+ }
+
+ surface->configured = true;
+ surface->pending.configure_serial = serial;
+
+ wl_signal_emit(&surface->events.ack_configure, configure);
+ xdg_surface_configure_destroy(configure);
+}
+
+static void
+xdg_surface_handle_set_window_geometry(struct wl_client *client,
+ struct wl_resource *resource,
+ int32_t x, int32_t y, int32_t width, int32_t height)
+{
+ struct ds_xdg_surface *surface;
+
+ surface = wl_resource_get_user_data(resource);
+
+ if (surface->role == DS_XDG_SURFACE_ROLE_NONE) {
+ wl_resource_post_error(surface->resource,
+ XDG_SURFACE_ERROR_NOT_CONSTRUCTED,
+ "xdg_surface must have a role");
+ return;
+ }
+
+ if (width <= 0 || height <= 0) {
+ ds_err("Client tried to set invalid geometry");
+ wl_resource_post_error(resource, -1,
+ "Tried to set invalid xdg_surface geometry");
+ return;
+ }
+
+ surface->pending.geometry.x = x;
+ surface->pending.geometry.y = y;
+ surface->pending.geometry.width = width;
+ surface->pending.geometry.height = height;
+}
+
+static const struct xdg_surface_interface xdg_surface_impl =
+{
+ .destroy = xdg_surface_handle_destroy,
+ .get_toplevel = xdg_surface_handle_get_toplevel,
+ .get_popup = xdg_surface_handle_get_popup,
+ .ack_configure = xdg_surface_handle_ack_configure,
+ .set_window_geometry = xdg_surface_handle_set_window_geometry,
+};
+
+static void
+surface_send_configure(void *user_data)
+{
+ struct ds_xdg_surface *surface;
+ struct ds_xdg_surface_configure *configure;
+
+ surface = user_data;
+ surface->configure_idle = NULL;
+
+ configure = calloc(1, sizeof *configure);
+ if (!configure) {
+ wl_client_post_no_memory(surface->client->wl_client);
+ return;
+ }
+
+ wl_list_insert(surface->configure_list.prev, &configure->link);
+ configure->serial = surface->scheduled_serial;
+ configure->surface = surface;
+
+ switch (surface->role) {
+ case DS_XDG_SURFACE_ROLE_NONE:
+ assert(0 && "not reached");
+ break;
+ case DS_XDG_SURFACE_ROLE_TOPLEVEL:
+ send_xdg_toplevel_configure(surface, configure);
+ break;
+ case DS_XDG_SURFACE_ROLE_POPUP:
+ break;
+ }
+
+ wl_signal_emit(&surface->events.configure, configure);
+
+ xdg_surface_send_configure(surface->resource, configure->serial);
+}
--- /dev/null
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "xdg_shell.h"
+
+const struct ds_surface_role xdg_toplevel_surface_role = {
+ .name = "xdg_toplevel",
+ .commit = handle_xdg_surface_commit,
+};
+
+static const struct xdg_toplevel_interface xdg_toplevel_impl;
+
+static void xdg_toplevel_handle_resource_destroy(struct wl_resource *resource);
+
+void
+create_xdg_toplevel(struct ds_xdg_surface *surface, uint32_t id)
+{
+ if (!ds_surface_set_role(surface->ds_surface, &xdg_toplevel_surface_role,
+ surface, surface->resource, XDG_WM_BASE_ERROR_ROLE)) {
+ return;
+ }
+
+ if (surface->role != DS_XDG_SURFACE_ROLE_NONE) {
+ wl_resource_post_error(surface->resource,
+ XDG_SURFACE_ERROR_ALREADY_CONSTRUCTED,
+ "xdg_surface has already been constructed");
+ return;
+ }
+
+ assert(surface->toplevel == NULL);
+
+ surface->toplevel = calloc(1, sizeof *surface->toplevel);
+ if (!surface->toplevel) {
+ wl_resource_post_no_memory(surface->resource);
+ return;
+ }
+
+ surface->toplevel->base = surface;
+
+ wl_signal_init(&surface->toplevel->events.request_maximize);
+ wl_signal_init(&surface->toplevel->events.request_fullscreen);
+ wl_signal_init(&surface->toplevel->events.request_minimize);
+ wl_signal_init(&surface->toplevel->events.request_move);
+ wl_signal_init(&surface->toplevel->events.request_resize);
+ wl_signal_init(&surface->toplevel->events.request_show_window_menu);
+ wl_signal_init(&surface->toplevel->events.set_parent);
+ wl_signal_init(&surface->toplevel->events.set_title);
+ wl_signal_init(&surface->toplevel->events.set_app_id);
+
+ surface->toplevel->resource = wl_resource_create(
+ surface->client->wl_client, &xdg_toplevel_interface,
+ wl_resource_get_version(surface->resource), id);
+ if (!surface->toplevel->resource) {
+ free(surface->toplevel);
+ wl_resource_post_no_memory(surface->resource);
+ return;
+ }
+
+ wl_resource_set_implementation(surface->toplevel->resource,
+ &xdg_toplevel_impl, surface,
+ xdg_toplevel_handle_resource_destroy);
+
+ surface->role = DS_XDG_SURFACE_ROLE_TOPLEVEL;
+}
+
+void
+handle_xdg_surface_toplevel_committed(struct ds_xdg_surface *surface)
+{
+ if (!surface->toplevel->added) {
+ ds_xdg_surface_schedule_configure(surface);
+ surface->toplevel->added = true;
+ return;
+ }
+
+ surface->toplevel->current = surface->toplevel->pending;
+}
+
+void
+send_xdg_toplevel_configure(struct ds_xdg_surface *surface,
+ struct ds_xdg_surface_configure *configure)
+{
+ struct wl_array states;
+
+ configure->toplevel_configure =
+ malloc(sizeof *configure->toplevel_configure);
+ if (!configure->toplevel_configure) {
+ wl_resource_post_no_memory(surface->toplevel->resource);
+ return;
+ }
+
+ *configure->toplevel_configure = surface->toplevel->scheduled;
+
+ wl_array_init(&states);
+ if (surface->toplevel->scheduled.maximized) {
+ uint32_t *s = wl_array_add(&states, sizeof(uint32_t));
+ if (!s)
+ goto error_out;
+ *s = XDG_TOPLEVEL_STATE_MAXIMIZED;
+ }
+
+ if (surface->toplevel->scheduled.fullscreen) {
+ uint32_t *s = wl_array_add(&states, sizeof(uint32_t));
+ if (!s)
+ goto error_out;
+ *s = XDG_TOPLEVEL_STATE_FULLSCREEN;
+ }
+
+ if (surface->toplevel->scheduled.resizing) {
+ uint32_t *s = wl_array_add(&states, sizeof(uint32_t));
+ if (!s)
+ goto error_out;
+ *s = XDG_TOPLEVEL_STATE_RESIZING;
+ }
+
+ if (surface->toplevel->scheduled.activated) {
+ uint32_t *s = wl_array_add(&states, sizeof(uint32_t));
+ if (!s)
+ goto error_out;
+ *s = XDG_TOPLEVEL_STATE_ACTIVATED;
+ }
+
+ if (surface->toplevel->scheduled.tiled) {
+ ;;;
+ // TODO
+ }
+
+ uint32_t width = surface->toplevel->scheduled.width;
+ uint32_t height = surface->toplevel->scheduled.height;
+
+ xdg_toplevel_send_configure(surface->toplevel->resource, width, height,
+ &states);
+ wl_array_release(&states);
+
+ return;
+
+error_out:
+ wl_array_release(&states);
+ wl_resource_post_no_memory(surface->toplevel->resource);
+}
+
+void
+destroy_xdg_toplevel(struct ds_xdg_surface *xdg_surface)
+{
+ reset_xdg_surface(xdg_surface);
+}
+
+static void
+xdg_toplevel_handle_destroy(struct wl_client *client,
+ struct wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+static void
+xdg_toplevel_handle_set_parent(struct wl_client *client,
+ struct wl_resource *resource, struct wl_resource *parent_resource)
+{
+ // TODO
+}
+
+static void
+xdg_toplevel_handle_set_title(struct wl_client *client,
+ struct wl_resource *resource, const char *title)
+{
+ struct ds_xdg_surface *surface;
+ char *tmp;
+
+ surface = wl_resource_get_user_data(resource);
+ tmp = strdup(title);
+ if (!tmp) {
+ wl_resource_post_no_memory(resource);
+ return;
+ }
+
+ if (surface->toplevel->title)
+ free(surface->toplevel->title);
+
+ surface->toplevel->title = tmp;
+ wl_signal_emit(&surface->toplevel->events.set_title, surface);
+}
+
+static void
+xdg_toplevel_handle_set_app_id(struct wl_client *client,
+ struct wl_resource *resource, const char *app_id)
+{
+ struct ds_xdg_surface *surface;
+ char *tmp;
+
+ surface = wl_resource_get_user_data(resource);
+ tmp = strdup(app_id);
+ if (!tmp) {
+ wl_resource_post_no_memory(resource);
+ return;
+ }
+
+ if (surface->toplevel->app_id)
+ free(surface->toplevel->app_id);
+
+ surface->toplevel->app_id= tmp;
+ wl_signal_emit(&surface->toplevel->events.set_app_id, surface);
+
+}
+
+static void
+xdg_toplevel_handle_show_window_menu(struct wl_client *client,
+ struct wl_resource *resource, struct wl_resource *seat_resource,
+ uint32_t serial, int32_t x, int32_t y)
+{
+ // TODO
+}
+
+static void
+xdg_toplevel_handle_move(struct wl_client *client,
+ struct wl_resource *resource, struct wl_resource *seat_resource,
+ uint32_t serial)
+{
+ // TODO
+}
+
+static void
+xdg_toplevel_handle_resize(struct wl_client *client,
+ struct wl_resource *resource, struct wl_resource *seat_resource,
+ uint32_t serial, uint32_t edges)
+{
+ // TODO
+}
+
+static void
+xdg_toplevel_handle_set_max_size(struct wl_client *client,
+ struct wl_resource *resource, int32_t width, int32_t height)
+{
+ struct ds_xdg_surface *surface;
+
+ surface = wl_resource_get_user_data(resource);
+ surface->toplevel->pending.max_width = width;
+ surface->toplevel->pending.max_height = height;
+}
+
+static void
+xdg_toplevel_handle_set_min_size(struct wl_client *client,
+ struct wl_resource *resource, int32_t width, int32_t height)
+{
+ struct ds_xdg_surface *surface;
+
+ surface = wl_resource_get_user_data(resource);
+ surface->toplevel->pending.min_width = width;
+ surface->toplevel->pending.min_height = height;
+}
+
+static void
+xdg_toplevel_handle_set_maximized(struct wl_client *client,
+ struct wl_resource *resource)
+{
+ struct ds_xdg_surface *surface;
+
+ surface = wl_resource_get_user_data(resource);
+ surface->toplevel->requested.maximized = true;
+ wl_signal_emit(&surface->toplevel->events.request_maximize, surface);
+ ds_xdg_surface_schedule_configure(surface);
+}
+
+static void
+xdg_toplevel_handle_unset_maximized(struct wl_client *client,
+ struct wl_resource *resource)
+{
+ struct ds_xdg_surface *surface;
+
+ surface = wl_resource_get_user_data(resource);
+ surface->toplevel->requested.maximized = false;
+ wl_signal_emit(&surface->toplevel->events.request_maximize, surface);
+ ds_xdg_surface_schedule_configure(surface);
+}
+
+static void
+xdg_toplevel_handle_set_fullscreen(struct wl_client *client,
+ struct wl_resource *resource, struct wl_resource *output_resource)
+{
+ // TODO
+}
+
+static void
+xdg_toplevel_handle_unset_fullscreen(struct wl_client *client,
+ struct wl_resource *resource)
+{
+ // TODO
+}
+
+static void
+xdg_toplevel_handle_set_minimized(struct wl_client *client,
+ struct wl_resource *resource)
+{
+ struct ds_xdg_surface *surface;
+
+ surface = wl_resource_get_user_data(resource);
+ surface->toplevel->requested.minimized = true;
+ wl_signal_emit(&surface->toplevel->events.request_maximize, surface);
+}
+
+static const struct xdg_toplevel_interface xdg_toplevel_impl = {
+ xdg_toplevel_handle_destroy,
+ xdg_toplevel_handle_set_parent,
+ xdg_toplevel_handle_set_title,
+ xdg_toplevel_handle_set_app_id,
+ xdg_toplevel_handle_show_window_menu,
+ xdg_toplevel_handle_move,
+ xdg_toplevel_handle_resize,
+ xdg_toplevel_handle_set_max_size,
+ xdg_toplevel_handle_set_min_size,
+ xdg_toplevel_handle_set_maximized,
+ xdg_toplevel_handle_unset_maximized,
+ xdg_toplevel_handle_set_fullscreen,
+ xdg_toplevel_handle_unset_fullscreen,
+ xdg_toplevel_handle_set_minimized,
+};
+
+static void
+xdg_toplevel_handle_resource_destroy(struct wl_resource *resource)
+{
+ struct ds_xdg_surface *surface;
+
+ surface = wl_resource_get_user_data(resource);
+ if (!surface)
+ return;
+
+ destroy_xdg_toplevel(surface);
+}
--- /dev/null
+wayland_protos = dependency('wayland-protocols',
+ fallback: ['wayland-protocols', 'wayland_protocols'],
+ default_options: ['tests=false'],
+)
+wl_protocol_dir = wayland_protos.get_variable('pkgdatadir')
+
+wayland_scanner_dep = dependency('wayland-scanner', native: true)
+wayland_scanner = find_program(
+ wayland_scanner_dep.get_variable('wayland_scanner'),
+ native: true,
+)
+
+subdir('libds')
+subdir('tests')
--- /dev/null
+tests = [
+ { 'name': 'test-compositor' },
+ { 'name': 'test-backend' },
+ {
+ 'name': 'test-surface',
+ 'deps': [ dependency('wayland-client') ],
+ },
+ {
+ 'name': 'test-subsurface',
+ 'deps': [ dependency('wayland-client') ],
+ },
+]
+
+foreach t : tests
+ t_deps = [ dep_libds ]
+ t_deps += t.get('deps', [])
+
+ test('libds-' + t.get('name'),
+ executable('libds-' + t.get('name'), t.get('name') + '.c',
+ dependencies: t_deps ,
+ include_directories: common_inc,
+ install: false
+ )
+ )
+endforeach
--- /dev/null
+#include <assert.h>
+#include <stdbool.h>
+#include <stdio.h>
+
+#include <wayland-server.h>
+#include <libds/backend/wayland.h>
+
+static struct ds_backend *
+create_possible_wl_backend(struct wl_display *display)
+{
+ struct ds_backend *backend;
+ char wl_name[512];
+
+ for (int i = 0; i < 5; i++) {
+ snprintf(wl_name, sizeof wl_name, "wayland-%d", i);
+ backend = ds_wl_backend_create(display, wl_name);
+ if (backend)
+ break;
+ }
+
+ return backend;
+}
+
+static void
+test_wl_backend_create(void)
+{
+ struct wl_display *display;
+ struct ds_backend *backend;
+
+ display = wl_display_create();
+
+ backend = create_possible_wl_backend(display);
+ assert(backend);
+
+ ds_backend_destroy(backend);
+}
+
+struct test
+{
+ struct wl_listener backend_destroy;
+ bool cb_called;
+};
+
+static void
+cb_backend_destroy(struct wl_listener *listener, void *data)
+{
+ struct test *test;
+
+ test = wl_container_of(listener, test, backend_destroy);
+ test->cb_called = true;
+}
+
+static void
+test_wl_backend_destroy_signal(void)
+{
+ struct wl_display *display;
+ struct ds_backend *backend;
+ struct test test;
+
+ display = wl_display_create();
+
+ backend = create_possible_wl_backend(display);
+ assert(backend);
+
+ test.cb_called = false;
+ test.backend_destroy.notify = cb_backend_destroy;
+ ds_backend_add_destroy_listener(backend, &test.backend_destroy);
+
+ wl_display_destroy(display);
+ assert(test.cb_called);
+}
+
+int
+main(void)
+{
+ test_wl_backend_create();
+ test_wl_backend_destroy_signal();
+
+ return 0;
+}
--- /dev/null
+#include <assert.h>
+#include <stdbool.h>
+
+#include <wayland-server.h>
+#include <libds/compositor.h>
+#include <libds/log.h>
+
+static void
+test_compositor_create(void)
+{
+ struct wl_display *display;
+ struct ds_compositor *compositor;
+
+ display = wl_display_create();
+ compositor = ds_compositor_create(display);
+ assert(compositor);
+
+ wl_display_destroy(display);
+}
+
+struct test
+{
+ struct wl_listener compositor_destroy;
+ bool destroyed;
+};
+
+static void
+cb_compositor_destroy(struct wl_listener *listener, void *data)
+{
+ struct test *test;
+
+ test = wl_container_of(listener, test, compositor_destroy);
+ test->destroyed = true;
+}
+
+static void
+test_compositor_destroy_signal(void)
+{
+ struct wl_display *display;
+ struct ds_compositor *compositor;
+ struct test test;
+
+ display = wl_display_create();
+ compositor = ds_compositor_create(display);
+ assert(compositor);
+
+ test.destroyed = false;
+ test.compositor_destroy.notify = cb_compositor_destroy;
+ ds_compositor_add_destroy_listener(compositor,
+ &test.compositor_destroy);
+
+ wl_display_destroy(display);
+ assert(test.destroyed == true);
+}
+
+int
+main(void)
+{
+ test_compositor_create();
+ test_compositor_destroy_signal();
+
+ return 0;
+}
--- /dev/null
+#include <assert.h>
+#include <stdbool.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <wayland-server.h>
+#include <wayland-client.h>
+#include <libds/compositor.h>
+#include <libds/surface.h>
+#include <libds/subsurface.h>
+#include <libds/log.h>
+
+struct server_base
+{
+ struct wl_display *display;
+ struct ds_compositor *compositor;
+ const char *socket;
+};
+
+const char *
+test_server_init(struct server_base *server)
+{
+ server->display = wl_display_create();
+ assert(server->display);
+ server->compositor = ds_compositor_create(server->display);
+ assert(server->compositor);
+ server->socket = wl_display_add_socket_auto(server->display);
+ assert(server->socket);
+
+ return server->socket;
+}
+
+void
+test_server_finish(struct server_base *server)
+{
+ wl_display_destroy(server->display);
+}
+
+struct client
+{
+ struct wl_display *display;
+ struct wl_registry *registry;
+ struct wl_compositor *compositor;
+ struct wl_subcompositor *subcompositor;
+ struct wl_surface *surface;
+ struct wl_subsurface *subsurface;
+};
+
+static void
+handle_global(void *data, struct wl_registry *registry, uint32_t id,
+ const char *interface, uint32_t version)
+{
+ struct client *client = data;
+
+ if (strcmp(interface, "wl_compositor") == 0) {
+ client->compositor =
+ wl_registry_bind(registry, id, &wl_compositor_interface, version);
+ }
+ else if (strcmp(interface, "wl_subcompositor") == 0) {
+ client->subcompositor =
+ wl_registry_bind(registry, id, &wl_subcompositor_interface, version);
+ }
+}
+
+static const struct wl_registry_listener registry_listener = {
+ .global = handle_global,
+};
+
+void
+test_client_init(struct client *client, const char *name)
+{
+ client->display = wl_display_connect(name);
+ assert(client->display);
+ client->registry = wl_display_get_registry(client->display);
+ assert(client->registry);
+
+ wl_registry_add_listener(client->registry, ®istry_listener, client);
+
+ wl_display_roundtrip(client->display);
+
+ assert(client->compositor);
+ assert(client->subcompositor);
+}
+
+void
+test_client_finish(struct client *client)
+{
+ wl_subcompositor_destroy(client->subcompositor);
+ wl_compositor_destroy(client->compositor);
+ wl_registry_destroy(client->registry);
+ wl_display_disconnect(client->display);
+}
+
+struct test_server
+{
+ struct server_base base;
+ bool cb_called;
+
+ struct wl_listener new_surface;
+ struct wl_listener new_subsurface1;
+ struct wl_listener new_subsurface2;
+};
+
+static void
+cb_new_subsurface1(struct wl_listener *listener, void *data)
+{
+ struct ds_subsurface *subsurface = data;
+ struct test_server *server;
+
+ assert(subsurface);
+
+ server = wl_container_of(listener, server, new_subsurface1);
+ server->cb_called = true;
+ wl_display_terminate(server->base.display);
+}
+
+static void
+cb_new_subsurface2(struct wl_listener *listener, void *data)
+{
+ struct ds_subsurface *subsurface = data;
+ struct test_server *server;
+
+ assert(subsurface);
+
+ server = wl_container_of(listener, server, new_subsurface2);
+ server->cb_called = true;
+ wl_display_terminate(server->base.display);
+}
+
+static void
+cb_new_surface(struct wl_listener *listener, void *data)
+{
+ struct ds_surface *surface = data;
+ struct test_server *server;
+
+ server = wl_container_of(listener, server, new_surface);
+ if (!server->new_subsurface1.notify) {
+ server->new_subsurface1.notify = cb_new_subsurface1;
+ ds_surface_add_new_subsurface_listener(surface,
+ &server->new_subsurface1);
+ }
+ else {
+ server->new_subsurface2.notify = cb_new_subsurface2;
+ ds_surface_add_new_subsurface_listener(surface,
+ &server->new_subsurface2);
+ }
+}
+
+static void
+run_client(const char *name)
+{
+ struct client client;
+
+ test_client_init(&client, name);
+
+ struct wl_surface *surface =
+ wl_compositor_create_surface(client.compositor);
+
+ struct wl_surface *child_surface =
+ wl_compositor_create_surface(client.compositor);
+
+ struct wl_subsurface *subsurface =
+ wl_subcompositor_get_subsurface(client.subcompositor,
+ child_surface, surface);
+
+ wl_display_roundtrip(client.display);
+
+ wl_subsurface_destroy(subsurface);
+ wl_surface_destroy(child_surface);
+ wl_surface_destroy(surface);
+
+ test_client_finish(&client);
+}
+
+static void
+test_subsurface_create(void)
+{
+ struct test_server server = {
+ .new_subsurface1 = { .notify = NULL },
+ .cb_called = false
+ };
+ pid_t pid;
+
+ const char *socket_name = test_server_init(&server.base);
+
+ pid = fork();
+ assert(pid != -1);
+
+ if (pid == 0) {
+ run_client(socket_name);
+ exit(0);
+ }
+
+ server.new_surface.notify = cb_new_surface;
+ ds_compositor_add_new_surface_listener(server.base.compositor,
+ &server.new_surface);
+
+ wl_display_run(server.base.display);
+
+ assert(server.cb_called);
+
+ test_server_finish(&server.base);
+}
+
+int
+main(void)
+{
+ test_subsurface_create();
+ return 0;
+}
--- /dev/null
+#include <assert.h>
+#include <stdbool.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <wayland-server.h>
+#include <wayland-client.h>
+#include <libds/compositor.h>
+#include <libds/surface.h>
+#include <libds/log.h>
+
+struct server_base
+{
+ struct wl_display *display;
+ struct ds_compositor *compositor;
+ const char *socket;
+};
+
+void
+test_server_init(struct server_base *server)
+{
+ server->display = wl_display_create();
+ assert(server->display);
+ server->compositor = ds_compositor_create(server->display);
+ assert(server->compositor);
+ server->socket = wl_display_add_socket_auto(server->display);
+ assert(server->socket);
+}
+
+void
+test_server_finish(struct server_base *server)
+{
+ wl_display_destroy(server->display);
+}
+
+struct client
+{
+ struct wl_display *display;
+ struct wl_registry *registry;
+ struct wl_compositor *compositor;
+ struct wl_surface *surface;
+};
+
+static void
+handle_global(void *data, struct wl_registry *registry, uint32_t id,
+ const char *interface, uint32_t version)
+{
+ struct client *client = data;
+
+ if (strcmp(interface, "wl_compositor") == 0) {
+ client->compositor =
+ wl_registry_bind(registry, id, &wl_compositor_interface, version);
+ }
+}
+
+static const struct wl_registry_listener registry_listener = {
+ .global = handle_global,
+};
+
+void
+test_client_init(struct client *client, const char *name)
+{
+ client->display = wl_display_connect(name);
+ assert(client->display);
+ client->registry = wl_display_get_registry(client->display);
+ assert(client->registry);
+
+ wl_registry_add_listener(client->registry, ®istry_listener, client);
+
+ wl_display_roundtrip(client->display);
+
+ assert(client->compositor);
+}
+
+void
+test_client_finish(struct client *client)
+{
+ wl_compositor_destroy(client->compositor);
+ wl_registry_destroy(client->registry);
+ wl_display_disconnect(client->display);
+}
+
+struct test_server {
+ struct server_base base;
+ bool cb_called;
+
+ struct wl_listener new_surface;
+ struct wl_listener surface_destroy;
+};
+
+static void
+cb_surface_destroy(struct wl_listener *listener, void *data)
+{
+ struct test_server *server;
+
+ server = wl_container_of(listener, server, surface_destroy);
+ server->cb_called = true;
+ wl_display_terminate(server->base.display);
+}
+
+static void
+cb_new_surface(struct wl_listener *listener, void *data)
+{
+ struct ds_surface *surface = data;
+ struct test_server *server;
+
+ server = wl_container_of(listener, server, new_surface);
+ server->surface_destroy.notify = cb_surface_destroy;
+ ds_surface_add_destroy_listener(surface, &server->surface_destroy);
+}
+
+static void
+run_client(const char *name)
+{
+ struct client client;
+
+ test_client_init(&client, name);
+
+ struct wl_surface *surface =
+ wl_compositor_create_surface(client.compositor);
+
+ wl_display_roundtrip(client.display);
+
+ wl_surface_destroy(surface);
+
+ test_client_finish(&client);
+}
+
+static void
+test_surface_create_and_destroy(void)
+{
+ struct test_server server = { .cb_called = false };
+ pid_t pid;
+
+ test_server_init(&server.base);
+
+ pid = fork();
+ assert(pid != -1);
+
+ if (pid == 0) {
+ run_client(server.base.socket);
+ exit(0);
+ }
+
+ server.new_surface.notify = cb_new_surface;
+ ds_compositor_add_new_surface_listener(server.base.compositor,
+ &server.new_surface);
+
+ wl_display_run(server.base.display);
+
+ assert(server.cb_called);
+
+ test_server_finish(&server.base);
+}
+
+int
+main(void)
+{
+ test_surface_create_and_destroy();
+ return 0;
+}