From f1159535128132bda0265c918d461a8e29190df6 Mon Sep 17 00:00:00 2001 From: =?utf8?q?=EC=9E=84=EC=88=98=EC=B0=AC/Tizen=20Platform=20Lab=28SR=29/?= =?utf8?q?=EC=82=BC=EC=84=B1=EC=A0=84=EC=9E=90?= Date: Thu, 19 May 2022 13:40:51 +0900 Subject: [PATCH 02/10] Initial commit Change-Id: I80ef1a95978dba596bc1be9091f5906918d7f709 --- .gitignore | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 2 ++ 2 files changed, 54 insertions(+) create mode 100644 .gitignore create mode 100644 README.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c6127b3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,52 @@ +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf diff --git a/README.md b/README.md new file mode 100644 index 0000000..0b9094e --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# libds-tizen +Display Server library for Tizen -- 2.7.4 From b31978a44444d3a5e121e9e8ad15d8b85ed496e5 Mon Sep 17 00:00:00 2001 From: Seunghun Lee Date: Mon, 21 Feb 2022 13:58:09 +0900 Subject: [PATCH 03/10] Initial libds Change-Id: I1ce11f5a018ea4adf7817cda3a5f0cec3d2787ad --- LICENSE | 21 + TODO.md | 8 + examples/meson.build | 22 + examples/tinyds.c | 522 ++++++++++++++++++++ examples/wl-backend.c | 226 +++++++++ include/libds/allocator.h | 19 + include/libds/allocator/shm.h | 9 + include/libds/backend.h | 38 ++ include/libds/backend/wayland.h | 13 + include/libds/buffer.h | 74 +++ include/libds/compositor.h | 27 + include/libds/interfaces/allocator.h | 33 ++ include/libds/interfaces/backend.h | 36 ++ include/libds/interfaces/buffer.h | 48 ++ include/libds/interfaces/output.h | 76 +++ include/libds/log.h | 59 +++ include/libds/output.h | 30 ++ include/libds/presentation.h | 20 + include/libds/subsurface.h | 18 + include/libds/surface.h | 45 ++ include/libds/swapchain.h | 22 + include/libds/xdg_shell.h | 50 ++ include/meson.build | 3 + meson.build | 38 ++ packaging/libds.manifest | 5 + packaging/libds.spec | 56 +++ src/libds/addon.c | 60 +++ src/libds/addon.h | 42 ++ src/libds/allocator/allocator.c | 37 ++ src/libds/allocator/shm.c | 174 +++++++ src/libds/backend/backend.c | 55 +++ src/libds/backend/meson.build | 5 + src/libds/backend/wayland/backend.c | 281 +++++++++++ src/libds/backend/wayland/backend.h | 69 +++ src/libds/backend/wayland/meson.build | 26 + src/libds/backend/wayland/output.c | 309 ++++++++++++ src/libds/buffer.c | 149 ++++++ src/libds/buffer.h | 8 + src/libds/client_buffer.h | 31 ++ src/libds/client_buffer/shm_client_buffer.c | 157 ++++++ src/libds/compositor.c | 143 ++++++ src/libds/log.c | 96 ++++ src/libds/meson.build | 74 +++ src/libds/output.c | 116 +++++ src/libds/pixel_format.c | 15 + src/libds/pixel_format.h | 9 + src/libds/presentation.c | 114 +++++ src/libds/region.c | 196 ++++++++ src/libds/region.h | 24 + src/libds/subcompositor.c | 98 ++++ src/libds/subcompositor.h | 20 + src/libds/surface.h | 49 ++ src/libds/surface/subsurface.c | 333 +++++++++++++ src/libds/surface/surface-private.h | 109 ++++ src/libds/surface/surface.c | 740 ++++++++++++++++++++++++++++ src/libds/swapchain.c | 202 ++++++++ src/libds/util.h | 13 + src/libds/util/shm.c | 174 +++++++ src/libds/util/time.c | 8 + src/libds/xdg_shell/xdg_shell.c | 214 ++++++++ src/libds/xdg_shell/xdg_shell.h | 187 +++++++ src/libds/xdg_shell/xdg_surface.c | 485 ++++++++++++++++++ src/libds/xdg_shell/xdg_toplevel.c | 327 ++++++++++++ src/meson.build | 14 + src/tests/meson.build | 25 + src/tests/test-backend.c | 80 +++ src/tests/test-compositor.c | 63 +++ src/tests/test-subsurface.c | 211 ++++++++ src/tests/test-surface.c | 162 ++++++ 69 files changed, 7222 insertions(+) create mode 100644 LICENSE create mode 100644 TODO.md create mode 100644 examples/meson.build create mode 100644 examples/tinyds.c create mode 100644 examples/wl-backend.c create mode 100644 include/libds/allocator.h create mode 100644 include/libds/allocator/shm.h create mode 100644 include/libds/backend.h create mode 100644 include/libds/backend/wayland.h create mode 100644 include/libds/buffer.h create mode 100644 include/libds/compositor.h create mode 100644 include/libds/interfaces/allocator.h create mode 100644 include/libds/interfaces/backend.h create mode 100644 include/libds/interfaces/buffer.h create mode 100644 include/libds/interfaces/output.h create mode 100644 include/libds/log.h create mode 100644 include/libds/output.h create mode 100644 include/libds/presentation.h create mode 100644 include/libds/subsurface.h create mode 100644 include/libds/surface.h create mode 100644 include/libds/swapchain.h create mode 100644 include/libds/xdg_shell.h create mode 100644 include/meson.build create mode 100644 meson.build create mode 100644 packaging/libds.manifest create mode 100644 packaging/libds.spec create mode 100644 src/libds/addon.c create mode 100644 src/libds/addon.h create mode 100644 src/libds/allocator/allocator.c create mode 100644 src/libds/allocator/shm.c create mode 100644 src/libds/backend/backend.c create mode 100644 src/libds/backend/meson.build create mode 100644 src/libds/backend/wayland/backend.c create mode 100644 src/libds/backend/wayland/backend.h create mode 100644 src/libds/backend/wayland/meson.build create mode 100644 src/libds/backend/wayland/output.c create mode 100644 src/libds/buffer.c create mode 100644 src/libds/buffer.h create mode 100644 src/libds/client_buffer.h create mode 100644 src/libds/client_buffer/shm_client_buffer.c create mode 100644 src/libds/compositor.c create mode 100644 src/libds/log.c create mode 100644 src/libds/meson.build create mode 100644 src/libds/output.c create mode 100644 src/libds/pixel_format.c create mode 100644 src/libds/pixel_format.h create mode 100644 src/libds/presentation.c create mode 100644 src/libds/region.c create mode 100644 src/libds/region.h create mode 100644 src/libds/subcompositor.c create mode 100644 src/libds/subcompositor.h create mode 100644 src/libds/surface.h create mode 100644 src/libds/surface/subsurface.c create mode 100644 src/libds/surface/surface-private.h create mode 100644 src/libds/surface/surface.c create mode 100644 src/libds/swapchain.c create mode 100644 src/libds/util.h create mode 100644 src/libds/util/shm.c create mode 100644 src/libds/util/time.c create mode 100644 src/libds/xdg_shell/xdg_shell.c create mode 100644 src/libds/xdg_shell/xdg_shell.h create mode 100644 src/libds/xdg_shell/xdg_surface.c create mode 100644 src/libds/xdg_shell/xdg_toplevel.c create mode 100644 src/meson.build create mode 100644 src/tests/meson.build create mode 100644 src/tests/test-backend.c create mode 100644 src/tests/test-compositor.c create mode 100644 src/tests/test-subsurface.c create mode 100644 src/tests/test-surface.c diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..54e4ef7 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +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. diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..f9681ed --- /dev/null +++ b/TODO.md @@ -0,0 +1,8 @@ +[] 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 diff --git a/examples/meson.build b/examples/meson.build new file mode 100644 index 0000000..a107a77 --- /dev/null +++ b/examples/meson.build @@ -0,0 +1,22 @@ +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) diff --git a/examples/tinyds.c b/examples/tinyds.c new file mode 100644 index 0000000..3925014 --- /dev/null +++ b/examples/tinyds.c @@ -0,0 +1,522 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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); +} diff --git a/examples/wl-backend.c b/examples/wl-backend.c new file mode 100644 index 0000000..38dea1b --- /dev/null +++ b/examples/wl-backend.c @@ -0,0 +1,226 @@ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/include/libds/allocator.h b/include/libds/allocator.h new file mode 100644 index 0000000..d4a6a80 --- /dev/null +++ b/include/libds/allocator.h @@ -0,0 +1,19 @@ +#ifndef LIBDS_ALLOCATOR_H +#define LIBDS_ALLOCATOR_H + +#include + +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 diff --git a/include/libds/allocator/shm.h b/include/libds/allocator/shm.h new file mode 100644 index 0000000..1a02338 --- /dev/null +++ b/include/libds/allocator/shm.h @@ -0,0 +1,9 @@ +#ifndef LIBDS_ALLOCATOR_SHM_H +#define LIBDS_ALLOCATOR_SHM_H + +#include + +struct ds_allocator * +ds_shm_allocator_create(void); + +#endif diff --git a/include/libds/backend.h b/include/libds/backend.h new file mode 100644 index 0000000..71d9e63 --- /dev/null +++ b/include/libds/backend.h @@ -0,0 +1,38 @@ +#ifndef LIBDS_BACKEND_H +#define LIBDS_BACKEND_H + +#include +#include +#include + +#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 diff --git a/include/libds/backend/wayland.h b/include/libds/backend/wayland.h new file mode 100644 index 0000000..e7f233a --- /dev/null +++ b/include/libds/backend/wayland.h @@ -0,0 +1,13 @@ +#ifndef LIBDS_BACKEND_WAYLAND_H +#define LIBDS_BACKEND_WAYLAND_H + +#include +#include + +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 diff --git a/include/libds/buffer.h b/include/libds/buffer.h new file mode 100644 index 0000000..eac5146 --- /dev/null +++ b/include/libds/buffer.h @@ -0,0 +1,74 @@ +#ifndef LIBDS_BUFFER_H +#define LIBDS_BUFFER_H + +#include +#include +#include +#include + +#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 diff --git a/include/libds/compositor.h b/include/libds/compositor.h new file mode 100644 index 0000000..343fa0c --- /dev/null +++ b/include/libds/compositor.h @@ -0,0 +1,27 @@ +#ifndef LIBDS_COMPOSITOR_H +#define LIBDS_COMPOSITOR_H + +#include + +#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 diff --git a/include/libds/interfaces/allocator.h b/include/libds/interfaces/allocator.h new file mode 100644 index 0000000..1e60668 --- /dev/null +++ b/include/libds/interfaces/allocator.h @@ -0,0 +1,33 @@ +#ifndef LIBDS_INTERFACES_ALLOCATOR_H +#define LIBDS_INTERFACES_ALLOCATOR_H + +#include +#include + +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 diff --git a/include/libds/interfaces/backend.h b/include/libds/interfaces/backend.h new file mode 100644 index 0000000..e960ac4 --- /dev/null +++ b/include/libds/interfaces/backend.h @@ -0,0 +1,36 @@ +#ifndef LIBDS_INTERFACES_BACKEND_H +#define LIBDS_INTERFACES_BACKEND_H + +#include + +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 diff --git a/include/libds/interfaces/buffer.h b/include/libds/interfaces/buffer.h new file mode 100644 index 0000000..3e288aa --- /dev/null +++ b/include/libds/interfaces/buffer.h @@ -0,0 +1,48 @@ +#ifndef LIBDS_INTERFACES_BUFFER_H +#define LIBDS_INTERFACES_BUFFER_H + +#include +#include + +#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 diff --git a/include/libds/interfaces/output.h b/include/libds/interfaces/output.h new file mode 100644 index 0000000..cacb927 --- /dev/null +++ b/include/libds/interfaces/output.h @@ -0,0 +1,76 @@ +#ifndef LIBDS_INTERFACES_OUTPUT_H +#define LIBDS_INTERFACES_OUTPUT_H + +#include +#include +#include +#include + +#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 diff --git a/include/libds/log.h b/include/libds/log.h new file mode 100644 index 0000000..5296d89 --- /dev/null +++ b/include/libds/log.h @@ -0,0 +1,59 @@ +#ifndef LIBDS_LOG_H +#define LIBDS_LOG_H + +#include +#include +#include +#include + +#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 diff --git a/include/libds/output.h b/include/libds/output.h new file mode 100644 index 0000000..ea8e276 --- /dev/null +++ b/include/libds/output.h @@ -0,0 +1,30 @@ +#ifndef LIBDS_OUTPUT_H +#define LIBDS_OUTPUT_H + +#include +#include + +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 diff --git a/include/libds/presentation.h b/include/libds/presentation.h new file mode 100644 index 0000000..2b496b4 --- /dev/null +++ b/include/libds/presentation.h @@ -0,0 +1,20 @@ +#ifndef LIBstruct ds_presentationIME_H +#define LIBstruct ds_presentationIME_H + +#include + +#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 diff --git a/include/libds/subsurface.h b/include/libds/subsurface.h new file mode 100644 index 0000000..72ed088 --- /dev/null +++ b/include/libds/subsurface.h @@ -0,0 +1,18 @@ +#ifndef LIBDS_SUBSURFACE_H +#define LIBDS_SUBSURFACE_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct ds_subsurface; + +bool ds_surface_is_subsurface(struct ds_surface *surface); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/libds/surface.h b/include/libds/surface.h new file mode 100644 index 0000000..9c26cea --- /dev/null +++ b/include/libds/surface.h @@ -0,0 +1,45 @@ +#ifndef LIBDS_SURFACE_H +#define LIBDS_SURFACE_H + +#include +#include + +#include + +#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 diff --git a/include/libds/swapchain.h b/include/libds/swapchain.h new file mode 100644 index 0000000..d78d428 --- /dev/null +++ b/include/libds/swapchain.h @@ -0,0 +1,22 @@ +#ifndef LIBDS_SWAPCHAIN_H +#define LIBDS_SWAPCHAIN_H + +#include + +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 diff --git a/include/libds/xdg_shell.h b/include/libds/xdg_shell.h new file mode 100644 index 0000000..e3b386f --- /dev/null +++ b/include/libds/xdg_shell.h @@ -0,0 +1,50 @@ +#ifndef LIBDS_XDG_SHELL_H +#define LIBDS_XDG_SHELL_H + +#include +#include + +#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 diff --git a/include/meson.build b/include/meson.build new file mode 100644 index 0000000..731f7d3 --- /dev/null +++ b/include/meson.build @@ -0,0 +1,3 @@ +install_subdir('libds', + install_dir: get_option('includedir') +) diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..24506b6 --- /dev/null +++ b/meson.build @@ -0,0 +1,38 @@ +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', +) diff --git a/packaging/libds.manifest b/packaging/libds.manifest new file mode 100644 index 0000000..75b0fa5 --- /dev/null +++ b/packaging/libds.manifest @@ -0,0 +1,5 @@ + + + + + diff --git a/packaging/libds.spec b/packaging/libds.spec new file mode 100644 index 0000000..8b7c5a7 --- /dev/null +++ b/packaging/libds.spec @@ -0,0 +1,56 @@ +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 diff --git a/src/libds/addon.c b/src/libds/addon.c new file mode 100644 index 0000000..9b34b11 --- /dev/null +++ b/src/libds/addon.c @@ -0,0 +1,60 @@ +#include + +#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; +} diff --git a/src/libds/addon.h b/src/libds/addon.h new file mode 100644 index 0000000..bb04e7b --- /dev/null +++ b/src/libds/addon.h @@ -0,0 +1,42 @@ +#ifndef DS_ADDON_H +#define DS_ADDON_H + +#include + +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 diff --git a/src/libds/allocator/allocator.c b/src/libds/allocator/allocator.c new file mode 100644 index 0000000..34c0aa7 --- /dev/null +++ b/src/libds/allocator/allocator.c @@ -0,0 +1,37 @@ +#include +#include +#include + +#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); +} diff --git a/src/libds/allocator/shm.c b/src/libds/allocator/shm.c new file mode 100644 index 0000000..3b9e219 --- /dev/null +++ b/src/libds/allocator/shm.c @@ -0,0 +1,174 @@ +#include +#include +#include +#include +#include +#include + +#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, +}; diff --git a/src/libds/backend/backend.c b/src/libds/backend/backend.c new file mode 100644 index 0000000..d52a8d2 --- /dev/null +++ b/src/libds/backend/backend.c @@ -0,0 +1,55 @@ +#include +#include + +#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); +} diff --git a/src/libds/backend/meson.build b/src/libds/backend/meson.build new file mode 100644 index 0000000..b135534 --- /dev/null +++ b/src/libds/backend/meson.build @@ -0,0 +1,5 @@ +libds_files += files( + 'backend.c', +) + +subdir('wayland') diff --git a/src/libds/backend/wayland/backend.c b/src/libds/backend/wayland/backend.c new file mode 100644 index 0000000..faa2c64 --- /dev/null +++ b/src/libds/backend/wayland/backend.c @@ -0,0 +1,281 @@ +#include +#include +#include +#include +#include + +#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; +} diff --git a/src/libds/backend/wayland/backend.h b/src/libds/backend/wayland/backend.h new file mode 100644 index 0000000..1c3cd5b --- /dev/null +++ b/src/libds/backend/wayland/backend.h @@ -0,0 +1,69 @@ +#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 diff --git a/src/libds/backend/wayland/meson.build b/src/libds/backend/wayland/meson.build new file mode 100644 index 0000000..db68fe9 --- /dev/null +++ b/src/libds/backend/wayland/meson.build @@ -0,0 +1,26 @@ +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), +] diff --git a/src/libds/backend/wayland/output.c b/src/libds/backend/wayland/output.c new file mode 100644 index 0000000..a995dbf --- /dev/null +++ b/src/libds/backend/wayland/output.c @@ -0,0 +1,309 @@ +#include +#include +#include + +#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, +}; diff --git a/src/libds/buffer.c b/src/libds/buffer.c new file mode 100644 index 0000000..96edec5 --- /dev/null +++ b/src/libds/buffer.c @@ -0,0 +1,149 @@ +#include +#include + +#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; +} diff --git a/src/libds/buffer.h b/src/libds/buffer.h new file mode 100644 index 0000000..08b4784 --- /dev/null +++ b/src/libds/buffer.h @@ -0,0 +1,8 @@ +#ifndef DS_BUFFER_H +#define DS_BUFFER_H + +#include + +#include "libds/buffer.h" + +#endif diff --git a/src/libds/client_buffer.h b/src/libds/client_buffer.h new file mode 100644 index 0000000..f21ea7c --- /dev/null +++ b/src/libds/client_buffer.h @@ -0,0 +1,31 @@ +#ifndef DS_CLIENT_BUFFER_H +#define DS_CLIENT_BUFFER_H + +#include + +#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 diff --git a/src/libds/client_buffer/shm_client_buffer.c b/src/libds/client_buffer/shm_client_buffer.c new file mode 100644 index 0000000..e2c4308 --- /dev/null +++ b/src/libds/client_buffer/shm_client_buffer.c @@ -0,0 +1,157 @@ +#include +#include +#include + +#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; +} diff --git a/src/libds/compositor.c b/src/libds/compositor.c new file mode 100644 index 0000000..a073405 --- /dev/null +++ b/src/libds/compositor.c @@ -0,0 +1,143 @@ +#include +#include + +#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); +} diff --git a/src/libds/log.c b/src/libds/log.c new file mode 100644 index 0000000..2777f9a --- /dev/null +++ b/src/libds/log.c @@ -0,0 +1,96 @@ +#include +#include +#include +#include +#include +#include +#include + +#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); +} diff --git a/src/libds/meson.build b/src/libds/meson.build new file mode 100644 index 0000000..2d09a91 --- /dev/null +++ b/src/libds/meson.build @@ -0,0 +1,74 @@ +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('.') ], +) diff --git a/src/libds/output.c b/src/libds/output.c new file mode 100644 index 0000000..21d4ff8 --- /dev/null +++ b/src/libds/output.c @@ -0,0 +1,116 @@ +#include +#include + +#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; +} diff --git a/src/libds/pixel_format.c b/src/libds/pixel_format.c new file mode 100644 index 0000000..29da532 --- /dev/null +++ b/src/libds/pixel_format.c @@ -0,0 +1,15 @@ +#include +#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; + } +} diff --git a/src/libds/pixel_format.h b/src/libds/pixel_format.h new file mode 100644 index 0000000..21eb0a6 --- /dev/null +++ b/src/libds/pixel_format.h @@ -0,0 +1,9 @@ +#ifndef DS_PIXEL_FORMAT_H +#define DS_PIXEL_FORMAT_H + +#include +#include + +uint32_t convert_wl_shm_format_to_drm(enum wl_shm_format fmt); + +#endif diff --git a/src/libds/presentation.c b/src/libds/presentation.c new file mode 100644 index 0000000..d94ad04 --- /dev/null +++ b/src/libds/presentation.c @@ -0,0 +1,114 @@ +#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); +} diff --git a/src/libds/region.c b/src/libds/region.c new file mode 100644 index 0000000..27d30da --- /dev/null +++ b/src/libds/region.c @@ -0,0 +1,196 @@ +#include +#include +#include +#include + +#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); +} diff --git a/src/libds/region.h b/src/libds/region.h new file mode 100644 index 0000000..b27963c --- /dev/null +++ b/src/libds/region.h @@ -0,0 +1,24 @@ +#ifndef DS_REGION_H +#define DS_REGION_H + +#include +#include + +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 diff --git a/src/libds/subcompositor.c b/src/libds/subcompositor.c new file mode 100644 index 0000000..483fefb --- /dev/null +++ b/src/libds/subcompositor.c @@ -0,0 +1,98 @@ +#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); +} diff --git a/src/libds/subcompositor.h b/src/libds/subcompositor.h new file mode 100644 index 0000000..362727e --- /dev/null +++ b/src/libds/subcompositor.h @@ -0,0 +1,20 @@ +#ifndef DS_SUBCOMPOSITOR_H +#define DS_SUBCOMPOSITOR_H + +#include + +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 diff --git a/src/libds/surface.h b/src/libds/surface.h new file mode 100644 index 0000000..8f3ceac --- /dev/null +++ b/src/libds/surface.h @@ -0,0 +1,49 @@ +#ifndef DS_SURFACE_H +#define DS_SURFACE_H + +#include + +#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 diff --git a/src/libds/surface/subsurface.c b/src/libds/surface/subsurface.c new file mode 100644 index 0000000..4cc1ad8 --- /dev/null +++ b/src/libds/surface/subsurface.c @@ -0,0 +1,333 @@ +#include +#include + +#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; +} diff --git a/src/libds/surface/surface-private.h b/src/libds/surface/surface-private.h new file mode 100644 index 0000000..570dcc2 --- /dev/null +++ b/src/libds/surface/surface-private.h @@ -0,0 +1,109 @@ +#ifndef DS_SURFACE_PRIVATE_H +#define DS_SURFACE_PRIVATE_H + +#include +#include +#include + +#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 diff --git a/src/libds/surface/surface.c b/src/libds/surface/surface.c new file mode 100644 index 0000000..32ab232 --- /dev/null +++ b/src/libds/surface/surface.c @@ -0,0 +1,740 @@ +#include +#include + +#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; +} diff --git a/src/libds/swapchain.c b/src/libds/swapchain.c new file mode 100644 index 0000000..54e64cf --- /dev/null +++ b/src/libds/swapchain.c @@ -0,0 +1,202 @@ +#include +#include +#include + +#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); +} diff --git a/src/libds/util.h b/src/libds/util.h new file mode 100644 index 0000000..3b7448b --- /dev/null +++ b/src/libds/util.h @@ -0,0 +1,13 @@ +#ifndef DS_UTIL_H +#define DS_UTIL_H + +#include +#include + +int64_t +timespec_to_msec(const struct timespec *a); + +int +allocate_shm_file(size_t size); + +#endif diff --git a/src/libds/util/shm.c b/src/libds/util/shm.c new file mode 100644 index 0000000..c8c84e3 --- /dev/null +++ b/src/libds/util/shm.c @@ -0,0 +1,174 @@ +/* + * 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 +#include +#include +#include +#include +#include + +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; +} diff --git a/src/libds/util/time.c b/src/libds/util/time.c new file mode 100644 index 0000000..1b17516 --- /dev/null +++ b/src/libds/util/time.c @@ -0,0 +1,8 @@ +#define _POSIX_C_SOURCE 200809L + +#include +#include + +int64_t timespec_to_msec(const struct timespec *a) { + return (int64_t)a->tv_sec * 1000 + a->tv_nsec / 1000000; +} diff --git a/src/libds/xdg_shell/xdg_shell.c b/src/libds/xdg_shell/xdg_shell.c new file mode 100644 index 0000000..b7cc8ff --- /dev/null +++ b/src/libds/xdg_shell/xdg_shell.c @@ -0,0 +1,214 @@ +#include +#include +#include + +#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); +} diff --git a/src/libds/xdg_shell/xdg_shell.h b/src/libds/xdg_shell/xdg_shell.h new file mode 100644 index 0000000..0f99d6d --- /dev/null +++ b/src/libds/xdg_shell/xdg_shell.h @@ -0,0 +1,187 @@ +#ifndef DS_XDG_SHELL_H +#define DS_XDG_SHELL_H + +#include + +#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 diff --git a/src/libds/xdg_shell/xdg_surface.c b/src/libds/xdg_shell/xdg_surface.c new file mode 100644 index 0000000..523438d --- /dev/null +++ b/src/libds/xdg_shell/xdg_surface.c @@ -0,0 +1,485 @@ +#include +#include + +#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); +} diff --git a/src/libds/xdg_shell/xdg_toplevel.c b/src/libds/xdg_shell/xdg_toplevel.c new file mode 100644 index 0000000..8e78fe7 --- /dev/null +++ b/src/libds/xdg_shell/xdg_toplevel.c @@ -0,0 +1,327 @@ +#include +#include +#include + +#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); +} diff --git a/src/meson.build b/src/meson.build new file mode 100644 index 0000000..2a6ba37 --- /dev/null +++ b/src/meson.build @@ -0,0 +1,14 @@ +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') diff --git a/src/tests/meson.build b/src/tests/meson.build new file mode 100644 index 0000000..dd52176 --- /dev/null +++ b/src/tests/meson.build @@ -0,0 +1,25 @@ +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 diff --git a/src/tests/test-backend.c b/src/tests/test-backend.c new file mode 100644 index 0000000..69417b6 --- /dev/null +++ b/src/tests/test-backend.c @@ -0,0 +1,80 @@ +#include +#include +#include + +#include +#include + +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; +} diff --git a/src/tests/test-compositor.c b/src/tests/test-compositor.c new file mode 100644 index 0000000..b5883a5 --- /dev/null +++ b/src/tests/test-compositor.c @@ -0,0 +1,63 @@ +#include +#include + +#include +#include +#include + +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; +} diff --git a/src/tests/test-subsurface.c b/src/tests/test-subsurface.c new file mode 100644 index 0000000..98e830d --- /dev/null +++ b/src/tests/test-subsurface.c @@ -0,0 +1,211 @@ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +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; +} diff --git a/src/tests/test-surface.c b/src/tests/test-surface.c new file mode 100644 index 0000000..eab2b77 --- /dev/null +++ b/src/tests/test-surface.c @@ -0,0 +1,162 @@ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +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; +} -- 2.7.4 From d142ab6d609f3b3d203f422cec0142eab06fd79b Mon Sep 17 00:00:00 2001 From: Seunghun Lee Date: Wed, 2 Mar 2022 11:03:37 +0900 Subject: [PATCH 04/10] Remove _t suffix from struct Change-Id: I36406c49b9be1cf2d0c0a79288a16bccd3c23286 --- include/libds/backend.h | 2 +- include/libds/buffer.h | 4 +-- src/libds/addon.c | 18 ++++++------- src/libds/addon.h | 32 ++++++++++------------- src/libds/allocator/shm.c | 24 ++++++++--------- src/libds/backend/wayland/backend.c | 30 +++++++++++----------- src/libds/backend/wayland/backend.h | 16 +++--------- src/libds/backend/wayland/output.c | 40 ++++++++++++++--------------- src/libds/buffer.c | 2 +- src/libds/client_buffer.h | 4 +-- src/libds/client_buffer/shm_client_buffer.c | 24 ++++++++--------- src/libds/compositor.c | 2 +- src/libds/subcompositor.c | 6 ++--- src/libds/subcompositor.h | 6 ++--- src/libds/surface/surface-private.h | 6 ++--- src/libds/xdg_shell/xdg_shell.h | 6 ++--- 16 files changed, 101 insertions(+), 121 deletions(-) diff --git a/include/libds/backend.h b/include/libds/backend.h index 71d9e63..6b587d2 100644 --- a/include/libds/backend.h +++ b/include/libds/backend.h @@ -20,7 +20,7 @@ ds_backend_destroy(struct ds_backend *backend); int ds_backend_get_drm_fd(struct ds_backend *backend); -ds_buffer_caps_t +enum ds_buffer_caps ds_backend_get_buffer_caps(struct ds_backend *backend); void diff --git a/include/libds/buffer.h b/include/libds/buffer.h index eac5146..c91cba7 100644 --- a/include/libds/buffer.h +++ b/include/libds/buffer.h @@ -12,12 +12,12 @@ extern "C" { struct ds_buffer; -typedef enum +enum ds_buffer_caps { 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 { diff --git a/src/libds/addon.c b/src/libds/addon.c index 9b34b11..e1858ff 100644 --- a/src/libds/addon.c +++ b/src/libds/addon.c @@ -9,9 +9,9 @@ ds_addon_set_init(struct ds_addon_set *set) } void -ds_addon_set_finish(ds_addon_set_t *set) +ds_addon_set_finish(struct ds_addon_set *set) { - ds_addon_t *addon, *tmp; + struct ds_addon *addon, *tmp; wl_list_for_each_safe(addon, tmp, &set->addons, link) { ds_addon_finish(addon); @@ -20,10 +20,10 @@ 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) +ds_addon_init(struct ds_addon *addon, struct ds_addon_set *set, + const void *owner, const struct ds_addon_interface *impl) { - ds_addon_t *iter; + struct ds_addon *iter; assert(owner && impl); @@ -39,15 +39,15 @@ ds_addon_init(ds_addon_t *addon, ds_addon_set_t *set, } void -ds_addon_finish(ds_addon_t *addon) +ds_addon_finish(struct ds_addon *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 * +ds_addon_find(struct ds_addon_set *set, const void *owner, + const struct ds_addon_interface *impl) { struct ds_addon *addon; diff --git a/src/libds/addon.h b/src/libds/addon.h index bb04e7b..2e7b0e1 100644 --- a/src/libds/addon.h +++ b/src/libds/addon.h @@ -3,40 +3,36 @@ #include -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 struct ds_addon_interface *impl; const void *owner; struct wl_list link; }; +struct ds_addon_interface { + const char *name; + void (*destroy)(struct ds_addon *addon); +}; + void -ds_addon_set_init(ds_addon_set_t *set); +ds_addon_set_init(struct ds_addon_set *set); void -ds_addon_set_finish(ds_addon_set_t *set); +ds_addon_set_finish(struct ds_addon_set *set); void -ds_addon_init(ds_addon_t *addon, ds_addon_set_t *set, - const void *owner, const ds_addon_interface_t *impl); +ds_addon_init(struct ds_addon *addon, struct ds_addon_set *set, + const void *owner, const struct ds_addon_interface *impl); void -ds_addon_finish(ds_addon_t *addon); +ds_addon_finish(struct ds_addon *addon); -ds_addon_t * -ds_addon_find(ds_addon_set_t *set, const void *owner, - const ds_addon_interface_t *impl); +struct ds_addon * +ds_addon_find(struct ds_addon_set *set, const void *owner, + const struct ds_addon_interface *impl); #endif diff --git a/src/libds/allocator/shm.c b/src/libds/allocator/shm.c index 3b9e219..a0acd0c 100644 --- a/src/libds/allocator/shm.c +++ b/src/libds/allocator/shm.c @@ -11,10 +11,6 @@ #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; @@ -33,7 +29,7 @@ static const struct ds_allocator_interface shm_allocator_iface; WL_EXPORT struct ds_allocator * ds_shm_allocator_create(void) { - ds_shm_allocator_t *alloc; + struct ds_shm_allocator *alloc; alloc = calloc(1, sizeof *alloc); if (!alloc) @@ -47,17 +43,17 @@ ds_shm_allocator_create(void) return &alloc->base; } -static ds_shm_allocator_t * +static struct ds_shm_allocator * shm_allocator_from_allocator(struct ds_allocator *ds_allocator) { assert(ds_allocator->iface == &shm_allocator_iface); - return (ds_shm_allocator_t *)ds_allocator; + return (struct ds_shm_allocator *)ds_allocator; } static void shm_allocator_destroy(struct ds_allocator *ds_allocator) { - ds_shm_allocator_t *alloc; + struct ds_shm_allocator *alloc; alloc = shm_allocator_from_allocator(ds_allocator); ds_dbg("Destroy Shm allocator(%p)", alloc); @@ -66,17 +62,17 @@ shm_allocator_destroy(struct ds_allocator *ds_allocator) static const struct ds_buffer_interface shm_buffer_interface; -static ds_shm_buffer_t * +static struct ds_shm_buffer * shm_buffer_from_buffer(struct ds_buffer *buffer) { assert(buffer->iface == &shm_buffer_interface); - return (ds_shm_buffer_t *)buffer; + return (struct ds_shm_buffer *)buffer; } static void shm_buffer_destroy(struct ds_buffer *ds_buffer) { - ds_shm_buffer_t *buffer; + struct ds_shm_buffer *buffer; buffer = shm_buffer_from_buffer(ds_buffer); @@ -90,7 +86,7 @@ shm_buffer_destroy(struct ds_buffer *ds_buffer) static bool shm_buffer_get_shm(struct ds_buffer *ds_buffer, struct ds_shm_attributes *shm) { - ds_shm_buffer_t *buffer; + struct ds_shm_buffer *buffer; buffer = shm_buffer_from_buffer(ds_buffer); memcpy(shm, &buffer->shm, sizeof *shm); @@ -101,7 +97,7 @@ 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; + struct ds_shm_buffer *buffer; buffer = shm_buffer_from_buffer(ds_buffer); *data = buffer->data; @@ -130,7 +126,7 @@ 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; + struct ds_shm_buffer *buffer; buffer = calloc(1, sizeof *buffer); if (!buffer) diff --git a/src/libds/backend/wayland/backend.c b/src/libds/backend/wayland/backend.c index faa2c64..9b6cd3a 100644 --- a/src/libds/backend/wayland/backend.c +++ b/src/libds/backend/wayland/backend.c @@ -13,16 +13,16 @@ 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, +static bool wl_backend_server_init(struct ds_wl_backend_server *server, const char *name); -static void wl_backend_server_finish(ds_wl_backend_server_t *server); +static void wl_backend_server_finish(struct ds_wl_backend_server *server); static int wl_backend_handle_dispatch_events(int fd, uint32_t mask, void *data); WL_EXPORT struct ds_backend * ds_wl_backend_create(struct wl_display *display, const char *server_name) { - ds_wl_backend_t *wl_backend; + struct ds_wl_backend *wl_backend; struct wl_event_loop *loop; int fd; @@ -71,18 +71,18 @@ err_server: return NULL; } -ds_wl_backend_t * +struct ds_wl_backend * wl_backend_from_backend(struct ds_backend *backend) { assert(backend->iface == &wl_backend_interface); - return (ds_wl_backend_t *)backend; + return (struct ds_wl_backend *)backend; } static void -wl_backend_destroy(ds_wl_backend_t *backend) +wl_backend_destroy(struct ds_wl_backend *backend) { - ds_wl_output_t *output, *tmp_output; - ds_wl_buffer_t *buffer, *tmp_buffer; + struct ds_wl_output *output, *tmp_output; + struct ds_wl_buffer *buffer, *tmp_buffer; ds_dbg("Destroy wayland backend(%p)", backend); @@ -108,7 +108,7 @@ wl_backend_destroy(ds_wl_backend_t *backend) static void wl_backend_iface_destroy(struct ds_backend *backend) { - ds_wl_backend_t *wl_backend; + struct ds_wl_backend *wl_backend; if (!backend) return; @@ -127,7 +127,7 @@ static const struct ds_backend_interface wl_backend_interface = { static void wl_backend_handle_display_destroy(struct wl_listener *listener, void *data) { - ds_wl_backend_t *wl_backend; + struct ds_wl_backend *wl_backend; wl_backend = wl_container_of(listener, wl_backend, display_destroy); wl_backend_destroy(wl_backend); @@ -158,7 +158,7 @@ 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; + struct ds_wl_backend_server *server = data; ds_log(DS_DBG, "Wayland global: %s v%d", iface, version); @@ -191,7 +191,7 @@ static const struct wl_registry_listener registry_listener = { }; static bool -wl_backend_server_init(ds_wl_backend_server_t *server, const char *name) +wl_backend_server_init(struct ds_wl_backend_server *server, const char *name) { server->display = wl_display_connect(name); if (!server->display) { @@ -236,7 +236,7 @@ err_reg: } static void -wl_backend_server_finish(ds_wl_backend_server_t *server) +wl_backend_server_finish(struct ds_wl_backend_server *server) { xdg_wm_base_destroy(server->xdg_wm_base); wl_compositor_destroy(server->compositor); @@ -247,8 +247,8 @@ wl_backend_server_finish(ds_wl_backend_server_t *server) 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; + struct ds_wl_backend *wl_backend = data; + struct ds_wl_backend_server *server = &wl_backend->server; int count = 0; if ((mask & WL_EVENT_HANGUP) || (mask & WL_EVENT_ERROR)) { diff --git a/src/libds/backend/wayland/backend.h b/src/libds/backend/wayland/backend.h index 1c3cd5b..ab37ec0 100644 --- a/src/libds/backend/wayland/backend.h +++ b/src/libds/backend/wayland/backend.h @@ -4,14 +4,6 @@ #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; @@ -33,7 +25,7 @@ struct ds_wl_backend struct wl_list buffers; // ds_wl_buffer.link struct wl_event_source *server_event_source; - ds_wl_backend_server_t server; + struct ds_wl_backend_server server; }; struct ds_wl_buffer @@ -50,7 +42,7 @@ struct ds_wl_output { struct ds_output base; - ds_wl_backend_t *backend; + struct ds_wl_backend *backend; struct wl_surface *surface; struct xdg_surface *xdg_surface; @@ -60,10 +52,10 @@ struct ds_wl_output struct wl_list link; }; -ds_wl_backend_t * +struct ds_wl_backend * wl_backend_from_backend(struct ds_backend *backend); void -destroy_wl_buffer(ds_wl_buffer_t *buffer); +destroy_wl_buffer(struct ds_wl_buffer *buffer); #endif diff --git a/src/libds/backend/wayland/output.c b/src/libds/backend/wayland/output.c index a995dbf..b411dad 100644 --- a/src/libds/backend/wayland/output.c +++ b/src/libds/backend/wayland/output.c @@ -15,8 +15,8 @@ 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; + struct ds_wl_backend *backend; + struct ds_wl_output *output; backend = wl_backend_from_backend(ds_backend); @@ -78,7 +78,7 @@ err: } void -destroy_wl_buffer(ds_wl_buffer_t *buffer) +destroy_wl_buffer(struct ds_wl_buffer *buffer) { if (buffer == NULL) return; @@ -89,17 +89,17 @@ destroy_wl_buffer(ds_wl_buffer_t *buffer) free(buffer); } -static ds_wl_output_t * +static struct ds_wl_output * wl_output_from_output(struct ds_output *ds_output) { assert(ds_output->iface == &wl_output_iface); - return (ds_wl_output_t *)ds_output; + return (struct ds_wl_output *)ds_output; } static void wl_output_iface_destroy(struct ds_output *ds_output) { - ds_wl_output_t *output; + struct ds_wl_output *output; output = wl_output_from_output(ds_output); @@ -125,7 +125,7 @@ wl_output_iface_destroy(struct ds_output *ds_output) } static struct wl_buffer * -import_shm(ds_wl_backend_t *backend, struct ds_shm_attributes *shm) +import_shm(struct ds_wl_backend *backend, struct ds_shm_attributes *shm) { enum wl_shm_format wl_shm_format = WL_SHM_FORMAT_XRGB8888; struct wl_shm_pool *pool; @@ -147,7 +147,7 @@ import_shm(ds_wl_backend_t *backend, struct ds_shm_attributes *shm) static void buffer_handle_release(void *data, struct wl_buffer *wl_buffer) { - ds_wl_buffer_t *buffer = data; + struct ds_wl_buffer *buffer = data; ds_dbg("Wayland output: Buffer(%p) released.", buffer->buffer); buffer->released = true; @@ -161,17 +161,17 @@ static const struct wl_buffer_listener buffer_listener = { static void buffer_handle_buffer_destroy(struct wl_listener *listener, void *data) { - ds_wl_buffer_t *buffer; + struct ds_wl_buffer *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) +static struct ds_wl_buffer * +create_wl_buffer(struct ds_wl_backend *backend, struct ds_buffer *ds_buffer) { struct ds_shm_attributes shm; - ds_wl_buffer_t *buffer; + struct ds_wl_buffer *buffer; struct wl_buffer *wl_buffer; if (ds_buffer_get_shm(ds_buffer, &shm)) { @@ -196,10 +196,10 @@ create_wl_buffer(ds_wl_backend_t *backend, struct ds_buffer *ds_buffer) return buffer; } -static ds_wl_buffer_t * -get_or_create_wl_buffer(ds_wl_backend_t *backend, struct ds_buffer *ds_buffer) +static struct ds_wl_buffer * +get_or_create_wl_buffer(struct ds_wl_backend *backend, struct ds_buffer *ds_buffer) { - ds_wl_buffer_t *buffer; + struct ds_wl_buffer *buffer; wl_list_for_each(buffer, &backend->buffers, link) { if (buffer->buffer == ds_buffer && buffer->released) { @@ -215,7 +215,7 @@ get_or_create_wl_buffer(ds_wl_backend_t *backend, struct ds_buffer *ds_buffer) static void surface_frame_callback(void *data, struct wl_callback *cb, uint32_t time) { - ds_wl_output_t *output = data; + struct ds_wl_output *output = data; wl_callback_destroy(cb); output->frame_callback = NULL; @@ -230,8 +230,8 @@ static const struct wl_callback_listener frame_listener = { static bool wl_output_iface_commit(struct ds_output *ds_output) { - ds_wl_output_t *output; - ds_wl_buffer_t *buffer; + struct ds_wl_output *output; + struct ds_wl_buffer *buffer; struct ds_buffer *ds_buffer; output = wl_output_from_output(ds_output); @@ -287,7 +287,7 @@ wl_output_xdg_toplevel_handle_configure(void *data, int32_t width, int32_t height, struct wl_array *states) { // TODO - // ds_wl_output_t *output = data; + // struct ds_wl_output *output = data; if (width == 0 || height == 0) return; @@ -297,7 +297,7 @@ static void wl_output_xdg_toplevel_handle_close(void *data, struct xdg_toplevel *xdg_toplevel) { - ds_wl_output_t *output = data; + struct ds_wl_output *output = data; ds_output_destroy(&output->base); } diff --git a/src/libds/buffer.c b/src/libds/buffer.c index 96edec5..70de23f 100644 --- a/src/libds/buffer.c +++ b/src/libds/buffer.c @@ -29,7 +29,7 @@ ds_buffer_from_resource(struct wl_resource *resource) assert(resource && ds_resource_is_buffer(resource)); if (wl_shm_buffer_get(resource) != NULL) { - ds_shm_client_buffer_t *shm_client_buffer = + struct ds_shm_client_buffer *shm_client_buffer = ds_shm_client_buffer_get_or_create(resource); if (!shm_client_buffer) { ds_err("Failed to create shm client buffer"); diff --git a/src/libds/client_buffer.h b/src/libds/client_buffer.h index f21ea7c..cf390aa 100644 --- a/src/libds/client_buffer.h +++ b/src/libds/client_buffer.h @@ -7,8 +7,6 @@ #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; @@ -25,7 +23,7 @@ struct ds_shm_client_buffer } listener; }; -ds_shm_client_buffer_t * +struct ds_shm_client_buffer * ds_shm_client_buffer_get_or_create(struct wl_resource *resource); #endif diff --git a/src/libds/client_buffer/shm_client_buffer.c b/src/libds/client_buffer/shm_client_buffer.c index e2c4308..14bb745 100644 --- a/src/libds/client_buffer/shm_client_buffer.c +++ b/src/libds/client_buffer/shm_client_buffer.c @@ -10,13 +10,13 @@ static void shm_client_buffer_resource_handle_destroy(struct wl_listener *listener, void *data); -static ds_shm_client_buffer_t * +static struct ds_shm_client_buffer * shm_client_buffer_create(struct wl_resource *resource); -ds_shm_client_buffer_t * +struct ds_shm_client_buffer * ds_shm_client_buffer_get_or_create(struct wl_resource *resource) { - ds_shm_client_buffer_t *buffer; + struct ds_shm_client_buffer *buffer; struct wl_listener *resource_destroy_listener; resource_destroy_listener = wl_resource_get_destroy_listener(resource, @@ -34,7 +34,7 @@ static void shm_client_buffer_resource_handle_destroy(struct wl_listener *listener, void *data) { - ds_shm_client_buffer_t *buffer; + struct ds_shm_client_buffer *buffer; buffer = wl_container_of(listener, buffer, listener.resource_destroy); @@ -48,17 +48,17 @@ shm_client_buffer_resource_handle_destroy(struct wl_listener *listener, static const struct ds_buffer_interface shm_client_buffer_iface; -static ds_shm_client_buffer_t * +static struct ds_shm_client_buffer * shm_client_buffer_from_buffer(struct ds_buffer *buffer) { assert(buffer->iface == &shm_client_buffer_iface); - return (ds_shm_client_buffer_t *)buffer; + return (struct ds_shm_client_buffer *)buffer; } static void shm_client_buffer_destroy(struct ds_buffer *ds_buffer) { - ds_shm_client_buffer_t *buffer; + struct ds_shm_client_buffer *buffer; buffer = shm_client_buffer_from_buffer(ds_buffer); @@ -74,7 +74,7 @@ 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; + struct ds_shm_client_buffer *buffer; buffer = shm_client_buffer_from_buffer(ds_buffer); *format = buffer->format; @@ -92,7 +92,7 @@ shm_client_buffer_begin_data_ptr_access(struct ds_buffer *ds_buffer, static void shm_client_buffer_end_data_ptr_access(struct ds_buffer *ds_buffer) { - ds_shm_client_buffer_t *buffer; + struct ds_shm_client_buffer *buffer; buffer = shm_client_buffer_from_buffer(ds_buffer); if (buffer->shm_buffer) @@ -108,17 +108,17 @@ static const struct ds_buffer_interface shm_client_buffer_iface = { static void shm_client_buffer_handle_release(struct wl_listener *listener, void *data) { - ds_shm_client_buffer_t *buffer; + struct ds_shm_client_buffer *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 * +static struct ds_shm_client_buffer * shm_client_buffer_create(struct wl_resource *resource) { - ds_shm_client_buffer_t *buffer; + struct ds_shm_client_buffer *buffer; struct wl_shm_buffer *shm_buffer; enum wl_shm_format wl_shm_format; int32_t width; diff --git a/src/libds/compositor.c b/src/libds/compositor.c index a073405..64dd363 100644 --- a/src/libds/compositor.c +++ b/src/libds/compositor.c @@ -11,7 +11,7 @@ struct ds_compositor { struct wl_global *global; - ds_subcompositor_t subcompositor; + struct ds_subcompositor subcompositor; struct { struct wl_signal new_surface; diff --git a/src/libds/subcompositor.c b/src/libds/subcompositor.c index 483fefb..717fd33 100644 --- a/src/libds/subcompositor.c +++ b/src/libds/subcompositor.c @@ -8,7 +8,7 @@ static void subcompositor_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id); bool -ds_subcompositor_init(ds_subcompositor_t *subcomp, +ds_subcompositor_init(struct ds_subcompositor *subcomp, struct wl_display *display) { subcomp->global = wl_global_create(display, &wl_subcompositor_interface, @@ -22,7 +22,7 @@ ds_subcompositor_init(ds_subcompositor_t *subcomp, } void -ds_subcompositor_finish(ds_subcompositor_t *subcomp) +ds_subcompositor_finish(struct ds_subcompositor *subcomp) { wl_global_destroy(subcomp->global); } @@ -84,7 +84,7 @@ static void subcompositor_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) { - ds_subcompositor_t *subcomp = data; + struct ds_subcompositor *subcomp = data; struct wl_resource *resource; resource = wl_resource_create(client, &wl_subcompositor_interface, 1, id); diff --git a/src/libds/subcompositor.h b/src/libds/subcompositor.h index 362727e..71265d0 100644 --- a/src/libds/subcompositor.h +++ b/src/libds/subcompositor.h @@ -3,18 +3,16 @@ #include -typedef struct ds_subcompositor ds_subcompositor_t; - struct ds_subcompositor { struct wl_global *global; }; bool -ds_subcompositor_init(ds_subcompositor_t *subcomp, +ds_subcompositor_init(struct ds_subcompositor *subcomp, struct wl_display *display); void -ds_subcompositor_finish(ds_subcompositor_t *subcomp); +ds_subcompositor_finish(struct ds_subcompositor *subcomp); #endif diff --git a/src/libds/surface/surface-private.h b/src/libds/surface/surface-private.h index 570dcc2..78b2900 100644 --- a/src/libds/surface/surface-private.h +++ b/src/libds/surface/surface-private.h @@ -9,7 +9,7 @@ #include "buffer.h" #include "surface.h" -typedef enum +enum ds_surface_state_field { DS_SURFACE_STATE_BUFFER = 1 << 0, DS_SURFACE_STATE_SURFACE_DAMAGE = 1 << 1, @@ -20,11 +20,11 @@ typedef enum 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; + enum ds_surface_state_field committed; struct ds_buffer *buffer; int32_t dx, dy; diff --git a/src/libds/xdg_shell/xdg_shell.h b/src/libds/xdg_shell/xdg_shell.h index 0f99d6d..6596ea2 100644 --- a/src/libds/xdg_shell/xdg_shell.h +++ b/src/libds/xdg_shell/xdg_shell.h @@ -8,12 +8,12 @@ #include "surface.h" #include "libds/output.h" -typedef enum +enum ds_xdg_surface_role { DS_XDG_SURFACE_ROLE_NONE, DS_XDG_SURFACE_ROLE_TOPLEVEL, DS_XDG_SURFACE_ROLE_POPUP, -} ds_xdg_surface_role_t; +}; struct ds_xdg_shell { @@ -117,7 +117,7 @@ struct ds_xdg_surface struct ds_xdg_client *client; struct ds_surface *ds_surface; - ds_xdg_surface_role_t role; + enum ds_xdg_surface_role role; union { struct ds_xdg_toplevel *toplevel; -- 2.7.4 From 2dcbbdaeb989750e1fce697f02205f57602a2066 Mon Sep 17 00:00:00 2001 From: Seunghun Lee Date: Wed, 23 Feb 2022 13:39:35 +0900 Subject: [PATCH 05/10] First draft for tdm backend Change-Id: I2f0deeeb9990495907f9e805e6e43401fb28e008 --- include/libds/interfaces/output.h | 13 ++- include/libds/output.h | 14 ++++ packaging/libds.spec | 2 + src/libds/backend/meson.build | 1 + src/libds/backend/tdm/backend.c | 162 ++++++++++++++++++++++++++++++++++++++ src/libds/backend/tdm/meson.build | 8 ++ src/libds/backend/tdm/output.c | 145 ++++++++++++++++++++++++++++++++++ src/libds/backend/tdm/tdm.h | 53 +++++++++++++ src/libds/output.c | 33 ++++++++ 9 files changed, 423 insertions(+), 8 deletions(-) create mode 100644 src/libds/backend/tdm/backend.c create mode 100644 src/libds/backend/tdm/meson.build create mode 100644 src/libds/backend/tdm/output.c create mode 100644 src/libds/backend/tdm/tdm.h diff --git a/include/libds/interfaces/output.h b/include/libds/interfaces/output.h index cacb927..9b95db1 100644 --- a/include/libds/interfaces/output.h +++ b/include/libds/interfaces/output.h @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -28,18 +29,11 @@ struct ds_output_interface 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; + const struct ds_output_mode *mode; bool enabled; }; @@ -54,8 +48,11 @@ struct ds_output struct wl_global *global; struct ds_buffer *back_buffer, *front_buffer; + const struct ds_output_mode *current_mode; struct ds_output_state pending; + struct wl_list modes; + struct wl_listener display_destroy; struct { diff --git a/include/libds/output.h b/include/libds/output.h index ea8e276..d2454fc 100644 --- a/include/libds/output.h +++ b/include/libds/output.h @@ -6,6 +6,13 @@ struct ds_output; +struct ds_output_mode { + int32_t width, height; + int32_t refresh; + bool preferred; + struct wl_list link; +}; + void ds_output_destroy(struct ds_output *output); @@ -15,6 +22,13 @@ ds_output_commit(struct ds_output *output); void ds_output_attach_buffer(struct ds_output *output, struct ds_buffer *buffer); +const struct ds_output_mode * +ds_output_preferred_mode(struct ds_output *output); + +void +ds_output_set_mode(struct ds_output *output, + const struct ds_output_mode *mode); + void ds_output_add_destroy_listener(struct ds_output *output, struct wl_listener *listener); diff --git a/packaging/libds.spec b/packaging/libds.spec index 8b7c5a7..f68b266 100644 --- a/packaging/libds.spec +++ b/packaging/libds.spec @@ -14,6 +14,8 @@ BuildRequires: pkgconfig(wayland-protocols) BuildRequires: pkgconfig(pixman-1) BuildRequires: pkgconfig(libdrm) +BuildRequires: pkgconfig(libtdm) + %description Wayland Compositor Library diff --git a/src/libds/backend/meson.build b/src/libds/backend/meson.build index b135534..e095efd 100644 --- a/src/libds/backend/meson.build +++ b/src/libds/backend/meson.build @@ -3,3 +3,4 @@ libds_files += files( ) subdir('wayland') +subdir('tdm') diff --git a/src/libds/backend/tdm/backend.c b/src/libds/backend/tdm/backend.c new file mode 100644 index 0000000..582fe62 --- /dev/null +++ b/src/libds/backend/tdm/backend.c @@ -0,0 +1,162 @@ +#include +#include + +#include "libds/log.h" +#include "tdm.h" + +static const struct ds_backend_interface tdm_backend_iface; +static void tdm_backend_handle_display_destroy(struct wl_listener *listener, + void *data); +static void tdm_backend_destroy(struct ds_tdm_backend *tdm); +static int tdm_backend_handle_tdm_event(int fd, uint32_t mask, void *data); + +WL_EXPORT struct ds_backend * +ds_tdm_backend_create(struct wl_display *display) +{ + struct ds_tdm_backend *tdm; + struct wl_event_loop *loop; + tdm_error err; + + ds_inf("Initializing TDM backend"); + + tdm = calloc(1, sizeof *tdm); + if (!tdm) + return NULL; + + ds_backend_init(&tdm->base, &tdm_backend_iface); + + tdm->wl_display = display; + tdm->clock = CLOCK_MONOTONIC; // FIXME + wl_list_init(&tdm->outputs); + + tdm->tdm_display = tdm_display_init(&err); + if (err != TDM_ERROR_NONE) { + ds_err("Could not initialize tdm_display"); + goto err_display; + } + + err = tdm_display_get_fd(tdm->tdm_display, &tdm->fd); + if (err != TDM_ERROR_NONE || tdm->fd < 0) { + ds_err("Could not get fd from tdm_display: err(%d)", err); + goto err_fd; + } + + loop = wl_display_get_event_loop(display); + tdm->tdm_event = wl_event_loop_add_fd(loop, tdm->fd, WL_EVENT_READABLE, + tdm_backend_handle_tdm_event, tdm); + if (!tdm->tdm_event) { + ds_err("could not add fd event handler for tdm event"); + goto err_event; + } + + tdm->display_destroy.notify = tdm_backend_handle_display_destroy; + wl_display_add_destroy_listener(display, &tdm->display_destroy); + + return &tdm->base; + +err_event: + close(tdm->fd); +err_fd: + tdm_display_deinit(tdm->tdm_display); +err_display: + free(tdm); + + return NULL; +} + +struct ds_tdm_backend * +tdm_backend_from_backend(struct ds_backend *backend) +{ + assert(backend->iface == &tdm_backend_iface); + return (struct ds_tdm_backend *)backend; +} + +static void +tdm_backend_destroy(struct ds_tdm_backend *tdm) +{ + wl_list_remove(&tdm->display_destroy.link); + wl_event_source_remove(tdm->tdm_event); + close(tdm->fd); + tdm_display_deinit(tdm->tdm_display); + free(tdm); +} + +static bool +tdm_backend_scan_outputs(struct ds_tdm_backend *tdm) +{ + struct ds_tdm_output *output; + tdm_output *tdm_output; + tdm_error err; + int num_outputs, i; + + err = tdm_display_get_output_count(tdm->tdm_display, &num_outputs); + if (err != TDM_ERROR_NONE) { + ds_err("Could not get number of outputs: err(%d)", err); + return false; + } + + for (i = 0; i < num_outputs; i++) { + tdm_output = tdm_display_get_output(tdm->tdm_display, i, &err); + if (err != TDM_ERROR_NONE || !tdm_output) { + ds_err("Could not get output from tdm_display: index(%d) err(%d)", + i, err); + continue; + } + + output = create_tdm_output(tdm, tdm_output); + if (!output) { + ds_err("Could not create output: index(%d)", i); + continue; + } + + wl_list_insert(&tdm->outputs, &output->link); + + wl_signal_emit(&tdm->base.events.new_output, &output->base); + } + + return true; +} + +static bool +tdm_backend_iface_start(struct ds_backend *backend) +{ + struct ds_tdm_backend *tdm; + + tdm = tdm_backend_from_backend(backend); + return tdm_backend_scan_outputs(tdm); +} + +static void +tdm_backend_iface_destroy(struct ds_backend *backend) +{ + struct ds_tdm_backend *tdm; + + tdm = tdm_backend_from_backend(backend); + tdm_backend_destroy(tdm); +} + +static const struct ds_backend_interface tdm_backend_iface = { + .start = tdm_backend_iface_start, + .destroy = tdm_backend_iface_destroy, + .get_drm_fd = NULL, +}; + +static void +tdm_backend_handle_display_destroy(struct wl_listener *listener, void *data) +{ + struct ds_tdm_backend *tdm; + + tdm = wl_container_of(listener, tdm, display_destroy); + tdm_backend_destroy(tdm); +} + +static int +tdm_backend_handle_tdm_event(int fd, uint32_t mask, void *data) +{ + struct ds_tdm_backend *tdm; + + tdm = data; + tdm_display_handle_events(tdm->tdm_display); + + return 0; +} diff --git a/src/libds/backend/tdm/meson.build b/src/libds/backend/tdm/meson.build new file mode 100644 index 0000000..9a0b041 --- /dev/null +++ b/src/libds/backend/tdm/meson.build @@ -0,0 +1,8 @@ +libds_files += files( + 'backend.c', + 'output.c', +) + +libds_deps += [ + dependency('libtdm', required: true), +] diff --git a/src/libds/backend/tdm/output.c b/src/libds/backend/tdm/output.c new file mode 100644 index 0000000..74a0e29 --- /dev/null +++ b/src/libds/backend/tdm/output.c @@ -0,0 +1,145 @@ +#include +#include + +#include "libds/log.h" +#include "tdm.h" + +static const struct ds_output_interface tdm_output_iface; +static bool tdm_output_init_modes(struct ds_tdm_output *output); +static void tdm_output_destroy(struct ds_tdm_output *output); + +struct ds_tdm_output * +create_tdm_output(struct ds_tdm_backend *tdm, tdm_output *tdm_output) +{ + struct ds_tdm_output *output; + tdm_output_conn_status conn; + tdm_error err; + + output = calloc(1, sizeof *output); + if (!output) + return NULL; + + ds_output_init(&output->base, &tdm->base, &tdm_output_iface, + tdm->wl_display); + + output->tdm = tdm; + output->tdm_output = tdm_output; + + err = tdm_output_get_conn_status(tdm_output, &conn); + if (err != TDM_ERROR_NONE) { + ds_err("Could not get connection status of tdm output(%p): err(%d)", + tdm_output, err); + goto err_status; + } + + if (conn == TDM_OUTPUT_CONN_STATUS_CONNECTED) { + output->status = DS_TDM_OUTPUT_CONNECTED; + + if (!tdm_output_init_modes(output)) { + ds_err("Could not initialize modes of tdm output(%p)", + tdm_output); + goto err_status; + } + } + else if (conn == TDM_OUTPUT_CONN_STATUS_DISCONNECTED) { + output->status = DS_TDM_OUTPUT_DISCONNECTED; + } + + ds_inf("TDM output(%p) created.", output); + + return output; + +err_status: + free(output); + + return NULL; +} + +static struct ds_tdm_output * +tdm_output_from_output(struct ds_output *ds_output) +{ + assert(ds_output->iface == &tdm_output_iface); + return (struct ds_tdm_output *)ds_output; +} + +static void +tdm_output_iface_destroy(struct ds_output *ds_output) +{ + struct ds_tdm_output *output; + + output = tdm_output_from_output(ds_output); + tdm_output_destroy(output); +} + +static bool +tdm_output_iface_commit(struct ds_output *ds_output) +{ + // TODO + return true; +} + +static const struct ds_output_interface tdm_output_iface = +{ + .destroy = tdm_output_iface_destroy, + .commit = tdm_output_iface_commit, +}; + +static void +tdm_output_destroy(struct ds_tdm_output *output) +{ + struct ds_tdm_output_mode *mode, *mode_tmp; + + wl_list_for_each_safe(mode, mode_tmp, &output->base.modes, base.link) { + wl_list_remove(&mode->base.link); + free(mode); + } + + free(output); +} + +static bool +tdm_output_init_modes(struct ds_tdm_output *output) +{ + struct ds_tdm_output_mode *mode; + const tdm_output_mode *tdm_modes, *tdm_mode; + tdm_error err; + int num_modes, i; + + err = tdm_output_get_available_modes(output->tdm_output, &tdm_modes, + &num_modes); + if (err != TDM_ERROR_NONE) { + ds_err("Could not get available modes: output(%p)", output); + return false; + } + + ds_inf("Detected modes:"); + + for (i = 0; i < num_modes; i++) { + tdm_mode = &tdm_modes[i]; + + mode = calloc(1, sizeof *mode); + if (!mode) { + ds_err("Could not allocate memory"); + continue; + } + + mode->tdm_mode = tdm_mode; + mode->base.width = tdm_mode->hdisplay; + mode->base.height = tdm_mode->vdisplay; + mode->base.refresh = (int32_t)tdm_mode->vrefresh; // FIXME + + if (tdm_mode->type & TDM_OUTPUT_MODE_TYPE_PREFERRED) + mode->base.preferred = true; + + ds_inf(" %dx%d@%d %s", mode->base.width, mode->base.height, + mode->base.refresh, + mode->base.preferred ? "(preferred)" : ""); + + if (tdm_mode->type & TDM_OUTPUT_MODE_TYPE_DEFAULT) + wl_list_insert(&output->base.modes, &mode->base.link); + else + wl_list_insert(output->base.modes.prev, &mode->base.link); + } + + return true; +} diff --git a/src/libds/backend/tdm/tdm.h b/src/libds/backend/tdm/tdm.h new file mode 100644 index 0000000..3a8990b --- /dev/null +++ b/src/libds/backend/tdm/tdm.h @@ -0,0 +1,53 @@ +#ifndef DS_BACKEND_TDM_H +#define DS_BACKEND_TDM_H + +#include +#include + +#include "libds/interfaces/backend.h" +#include "libds/interfaces/output.h" + +enum ds_tdm_output_status { + DS_TDM_OUTPUT_DISCONNECTED, + DS_TDM_OUTPUT_CONNECTED, +}; + +struct ds_tdm_output_mode { + struct ds_output_mode base; + const tdm_output_mode *tdm_mode; +}; + +struct ds_tdm_backend +{ + struct ds_backend base; + + tdm_display *tdm_display; + struct wl_display *wl_display; + struct wl_event_source *tdm_event; + + struct wl_list outputs; // ds_tdm_output.link + + struct wl_listener display_destroy; + + clockid_t clock; + int fd; +}; + +struct ds_tdm_output +{ + struct ds_output base; + + struct ds_tdm_backend *tdm; + tdm_output *tdm_output; + + struct wl_list link; + + enum ds_tdm_output_status status; +}; + +struct ds_tdm_backend *tdm_backend_from_backend(struct ds_backend *backend); + +struct ds_tdm_output *create_tdm_output(struct ds_tdm_backend *tdm, + tdm_output *tdm_output); + +#endif diff --git a/src/libds/output.c b/src/libds/output.c index 21d4ff8..f5cd356 100644 --- a/src/libds/output.c +++ b/src/libds/output.c @@ -20,6 +20,8 @@ ds_output_init(struct ds_output *output, struct ds_backend *backend, output->iface = iface; output->display = display; + wl_list_init(&output->modes); + wl_signal_init(&output->events.destroy); wl_signal_init(&output->events.frame); wl_signal_init(&output->events.commit); @@ -65,6 +67,37 @@ ds_output_attach_buffer(struct ds_output *output, struct ds_buffer *buffer) output->pending.buffer = ds_buffer_lock(buffer); } +WL_EXPORT const struct ds_output_mode * +ds_output_preferred_mode(struct ds_output *output) +{ + struct ds_output_mode *mode; + + if (wl_list_empty(&output->modes)) + return NULL; + + wl_list_for_each(mode, &output->modes, link) { + if (mode->preferred) + return mode; + } + + // No preferred mode, choose the first one + return wl_container_of(output->modes.next, mode, link); +} + +WL_EXPORT void +ds_output_set_mode(struct ds_output *output, const struct ds_output_mode *mode) +{ + output->pending.mode = NULL; + output->pending.committed &= ~DS_OUTPUT_STATE_MODE; + + if (output->current_mode == mode) { + return; + } + + output->pending.committed |= DS_OUTPUT_STATE_MODE; + output->pending.mode = mode; +} + WL_EXPORT void ds_output_add_destroy_listener(struct ds_output *output, struct wl_listener *listener) -- 2.7.4 From ac2ee055e3e93cd8eddf8eadec090a7c6b850ca0 Mon Sep 17 00:00:00 2001 From: Seunghun Lee Date: Thu, 24 Feb 2022 14:58:44 +0900 Subject: [PATCH 06/10] Add tbm allocator Change-Id: If58f8810ce84cd6ac6fe5cb36127fce6f2e3e655 --- include/libds/allocator/tbm.h | 12 +++ packaging/libds.spec | 1 + src/libds/allocator/tbm.c | 177 ++++++++++++++++++++++++++++++++++++++++++ src/libds/meson.build | 2 + 4 files changed, 192 insertions(+) create mode 100644 include/libds/allocator/tbm.h create mode 100644 src/libds/allocator/tbm.c diff --git a/include/libds/allocator/tbm.h b/include/libds/allocator/tbm.h new file mode 100644 index 0000000..1457e3f --- /dev/null +++ b/include/libds/allocator/tbm.h @@ -0,0 +1,12 @@ +#ifndef LIBDS_ALLOCATOR_TBM_H +#define LIBDS_ALLOCATOR_TBM_H + +#include + +struct ds_allocator * +ds_tbm_allocator_create(void); + +WL_EXPORT void * +ds_tbm_buffer_get_surface(struct ds_buffer *ds_buffer); + +#endif diff --git a/packaging/libds.spec b/packaging/libds.spec index f68b266..aaf79b3 100644 --- a/packaging/libds.spec +++ b/packaging/libds.spec @@ -15,6 +15,7 @@ BuildRequires: pkgconfig(pixman-1) BuildRequires: pkgconfig(libdrm) BuildRequires: pkgconfig(libtdm) +BuildRequires: pkgconfig(libtbm) %description Wayland Compositor Library diff --git a/src/libds/allocator/tbm.c b/src/libds/allocator/tbm.c new file mode 100644 index 0000000..f4d0702 --- /dev/null +++ b/src/libds/allocator/tbm.c @@ -0,0 +1,177 @@ +#include +#include +#include +#include + +#include +#include + +#include "libds/interfaces/allocator.h" +#include "libds/interfaces/buffer.h" +#include "libds/log.h" + +struct ds_tbm_allocator +{ + struct ds_allocator base; + tbm_bufmgr bufmgr; +}; + +struct ds_tbm_buffer +{ + struct ds_buffer base; + tbm_surface_h surface; +}; + +static const struct ds_allocator_interface tbm_allocator_iface; +static struct ds_tbm_buffer *tbm_buffer_from_buffer(struct ds_buffer *buffer); + +WL_EXPORT struct ds_allocator * +ds_tbm_allocator_create(void) +{ + struct ds_tbm_allocator *alloc; + + alloc = calloc(1, sizeof *alloc); + if (!alloc) + return NULL; + + alloc->bufmgr = tbm_bufmgr_init(-1); + if (!alloc->bufmgr) { + ds_err("Could not initialize tbm_bufmgr"); + free(alloc); + return NULL; + } + + ds_allocator_init(&alloc->base, &tbm_allocator_iface, + DS_BUFFER_CAP_DATA_PTR); + + ds_inf("TBM allocator(%p) created", alloc); + + return &alloc->base; +} + +WL_EXPORT void * +ds_tbm_buffer_get_surface(struct ds_buffer *ds_buffer) +{ + struct ds_tbm_buffer *buffer; + + buffer = tbm_buffer_from_buffer(ds_buffer); + if (!buffer) + return NULL; + + return buffer->surface; +} + +static struct ds_tbm_allocator * +tbm_allocator_from_allocator(struct ds_allocator *ds_allocator) +{ + assert(ds_allocator->iface == &tbm_allocator_iface); + return (struct ds_tbm_allocator *)ds_allocator; +} + +static const struct ds_buffer_interface tbm_buffer_iface; + +static struct ds_tbm_buffer * +tbm_buffer_from_buffer(struct ds_buffer *buffer) +{ + assert(buffer->iface == &tbm_buffer_iface); + return (struct ds_tbm_buffer *)buffer; +} + +static void +tbm_buffer_destroy(struct ds_buffer *ds_buffer) +{ + struct ds_tbm_buffer *buffer; + + buffer = tbm_buffer_from_buffer(ds_buffer); + + ds_dbg("Destroy tbm buffer(%p)", buffer); + + tbm_surface_destroy(buffer->surface); + free(buffer); +} + +static bool +tbm_buffer_begin_data_ptr_access(struct ds_buffer *ds_buffer, uint32_t flags, + void **data, uint32_t *format, size_t *stride) +{ + struct ds_tbm_buffer *buffer; + tbm_surface_info_s info; + int tbm_access_flags = 0; + int ret; + + buffer = tbm_buffer_from_buffer(ds_buffer); + + if (flags & DS_BUFFER_DATA_PTR_ACCESS_READ) + tbm_access_flags |= TBM_OPTION_READ; + else if (flags & DS_BUFFER_DATA_PTR_ACCESS_WRITE) + tbm_access_flags |= TBM_OPTION_WRITE; + + ret = tbm_surface_map(buffer->surface, tbm_access_flags, &info); + if (ret != TBM_SURFACE_ERROR_NONE) { + ds_err("Could not map tbm_surface of buffer(%p)", buffer); + return false; + } + + *data = info.planes[0].ptr; + *format = info.format; + *stride = info.planes[0].stride; + + return true; +} + +static void +tbm_buffer_end_data_ptr_access(struct ds_buffer *ds_buffer) +{ + struct ds_tbm_buffer *buffer; + + buffer = tbm_buffer_from_buffer(ds_buffer); + + tbm_surface_unmap(buffer->surface); +} + +static const struct ds_buffer_interface tbm_buffer_iface = +{ + .destroy = tbm_buffer_destroy, + .begin_data_ptr_access = tbm_buffer_begin_data_ptr_access, + .end_data_ptr_access = tbm_buffer_end_data_ptr_access, +}; + + +static void +tbm_allocator_destroy(struct ds_allocator *ds_allocator) +{ + struct ds_tbm_allocator *alloc; + + alloc = tbm_allocator_from_allocator(ds_allocator); + + ds_inf("Destroy TBM allocator(%p)", alloc); + + tbm_bufmgr_deinit(alloc->bufmgr); + free(alloc); +} + +static struct ds_buffer * +tbm_allocator_create_buffer(struct ds_allocator *ds_allocator, + int width, int height, uint32_t format) +{ + static int num_buffers = 0; + struct ds_tbm_buffer *buffer; + + buffer = calloc(1, sizeof *buffer); + if (!buffer) + return NULL; + + ds_buffer_init(&buffer->base, &tbm_buffer_iface, width, height); + + buffer->surface = tbm_surface_create(width, height, TBM_FORMAT_XRGB8888); + + ds_dbg("tbm buffer(%p) created: size(%dx%d) number of buffers: %d", + buffer, width, height, ++num_buffers); + + return &buffer->base; +} + +static const struct ds_allocator_interface tbm_allocator_iface = { + .destroy = tbm_allocator_destroy, + .create_buffer = tbm_allocator_create_buffer, +}; diff --git a/src/libds/meson.build b/src/libds/meson.build index 2d09a91..8a88b62 100644 --- a/src/libds/meson.build +++ b/src/libds/meson.build @@ -4,6 +4,7 @@ libds_files = [ 'buffer.c', 'allocator/allocator.c', 'allocator/shm.c', + 'allocator/tbm.c', 'swapchain.c', 'output.c', 'compositor.c', @@ -50,6 +51,7 @@ 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) +libtbm = dependency('libtbm', required: true) libds_deps = [ math, -- 2.7.4 From d89f28c7e44df10f3c70f5097acf44014601266e Mon Sep 17 00:00:00 2001 From: Seunghun Lee Date: Thu, 24 Feb 2022 15:01:07 +0900 Subject: [PATCH 07/10] Flesh out tdm backend Change-Id: Idf59ff423465f1f44102058ff017c12b5f54f042 --- include/libds/backend/tdm.h | 9 ++ src/libds/backend/tdm/backend.c | 13 +++ src/libds/backend/tdm/output.c | 180 ++++++++++++++++++++++++++++++++++++++-- src/libds/backend/tdm/tdm.h | 23 ++++- 4 files changed, 218 insertions(+), 7 deletions(-) create mode 100644 include/libds/backend/tdm.h diff --git a/include/libds/backend/tdm.h b/include/libds/backend/tdm.h new file mode 100644 index 0000000..ec5f9cc --- /dev/null +++ b/include/libds/backend/tdm.h @@ -0,0 +1,9 @@ +#ifndef LIBDS_BACKEND_TDM_H +#define LIBDS_BACKEND_TDM_H + +#include + +struct ds_backend * +ds_tdm_backend_create(struct wl_display *display); + +#endif diff --git a/src/libds/backend/tdm/backend.c b/src/libds/backend/tdm/backend.c index 582fe62..2772f84 100644 --- a/src/libds/backend/tdm/backend.c +++ b/src/libds/backend/tdm/backend.c @@ -27,7 +27,9 @@ ds_tdm_backend_create(struct wl_display *display) tdm->wl_display = display; tdm->clock = CLOCK_MONOTONIC; // FIXME + wl_list_init(&tdm->outputs); + wl_list_init(&tdm->buffers); tdm->tdm_display = tdm_display_init(&err); if (err != TDM_ERROR_NONE) { @@ -74,6 +76,17 @@ tdm_backend_from_backend(struct ds_backend *backend) static void tdm_backend_destroy(struct ds_tdm_backend *tdm) { + struct ds_tdm_output *output, *tmp_output; + struct ds_tdm_buffer *buffer, *tmp_buffer; + + wl_list_for_each_safe(output, tmp_output, &tdm->outputs, link) { + ds_output_destroy(&output->base); + } + + wl_list_for_each_safe(buffer, tmp_buffer, &tdm->buffers, link) { + destroy_tdm_buffer(buffer); + } + wl_list_remove(&tdm->display_destroy.link); wl_event_source_remove(tdm->tdm_event); close(tdm->fd); diff --git a/src/libds/backend/tdm/output.c b/src/libds/backend/tdm/output.c index 74a0e29..08b56b5 100644 --- a/src/libds/backend/tdm/output.c +++ b/src/libds/backend/tdm/output.c @@ -2,11 +2,14 @@ #include #include "libds/log.h" +#include "libds/allocator/tbm.h" #include "tdm.h" +#include static const struct ds_output_interface tdm_output_iface; static bool tdm_output_init_modes(struct ds_tdm_output *output); static void tdm_output_destroy(struct ds_tdm_output *output); +static void tdm_output_init_hwc(struct ds_tdm_output *output); struct ds_tdm_output * create_tdm_output(struct ds_tdm_backend *tdm, tdm_output *tdm_output) @@ -22,8 +25,8 @@ create_tdm_output(struct ds_tdm_backend *tdm, tdm_output *tdm_output) ds_output_init(&output->base, &tdm->base, &tdm_output_iface, tdm->wl_display); - output->tdm = tdm; - output->tdm_output = tdm_output; + output->backend = tdm; + output->tdm.output = tdm_output; err = tdm_output_get_conn_status(tdm_output, &conn); if (err != TDM_ERROR_NONE) { @@ -45,7 +48,10 @@ create_tdm_output(struct ds_tdm_backend *tdm, tdm_output *tdm_output) output->status = DS_TDM_OUTPUT_DISCONNECTED; } - ds_inf("TDM output(%p) created.", output); + if (tdm_output_get_hwc(output->tdm.output, NULL) != NULL) + tdm_output_init_hwc(output); + + ds_inf("TDM output(%p) created: hwc(%p)", output, output->tdm.hwc); return output; @@ -71,10 +77,148 @@ tdm_output_iface_destroy(struct ds_output *ds_output) tdm_output_destroy(output); } +void +destroy_tdm_buffer(struct ds_tdm_buffer *buffer) +{ + if (buffer == NULL) + return; + + wl_list_remove(&buffer->buffer_destroy.link); + wl_list_remove(&buffer->link); + free(buffer); +} + +static void +buffer_handle_buffer_destroy(struct wl_listener *listener, void *data) +{ + struct ds_tdm_buffer *buffer; + + buffer = wl_container_of(listener, buffer, buffer_destroy); + destroy_tdm_buffer(buffer); +} + +static struct ds_tdm_buffer * +create_tdm_buffer(struct ds_tdm_backend *backend, struct ds_buffer *ds_buffer) +{ + struct ds_tdm_buffer *buffer; + tbm_surface_h surface; + + surface = ds_tbm_buffer_get_surface(ds_buffer); + if (!surface) { + ds_err("Could not get tbm_surface_h"); + return NULL; + } + + buffer = calloc(1, sizeof *buffer); + if (!buffer) { + return NULL; + } + + buffer->surface = surface; + buffer->buffer = ds_buffer_lock(ds_buffer); + wl_list_insert(&backend->buffers, &buffer->link); + + buffer->buffer_destroy.notify = buffer_handle_buffer_destroy; + ds_buffer_add_destroy_listener(ds_buffer, &buffer->buffer_destroy); + + return buffer; +} + +static struct ds_tdm_buffer * +get_or_create_tdm_buffer(struct ds_tdm_backend *backend, + struct ds_buffer *ds_buffer) +{ + struct ds_tdm_buffer *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_tdm_buffer(backend, ds_buffer); +} + +static void +tdm_output_hwc_commit_handler(tdm_hwc *hwc, unsigned int sequence, + unsigned int tv_sec, unsigned int tv_usec, void *user_data) +{ + struct ds_tdm_output *output; + + output = user_data; + + if (output->front_buffer) { + output->front_buffer->released = true; + ds_buffer_unlock(output->front_buffer->buffer); + } + + output->front_buffer = output->back_buffer; + output->back_buffer = NULL; + + wl_signal_emit(&output->base.events.frame, &output->base); +} + static bool tdm_output_iface_commit(struct ds_output *ds_output) { - // TODO + struct ds_tdm_output *output; + struct ds_tdm_buffer *buffer; + struct ds_buffer *ds_buffer; + + output = tdm_output_from_output(ds_output); + + ds_buffer = ds_output->pending.buffer; + buffer = get_or_create_tdm_buffer(output->backend, ds_buffer); + if (!buffer) + return false; + + if (ds_output->pending.committed & DS_OUTPUT_STATE_BUFFER) { + tdm_region fb_damage; + tdm_error err; + uint32_t num_changes; + + memset(&fb_damage, 0, sizeof(fb_damage)); + err = tdm_hwc_set_client_target_buffer(output->tdm.hwc, + buffer->surface, fb_damage); + if (err != TDM_ERROR_NONE) { + ds_err("Could not set hwc client target buffer"); + ds_buffer_unlock(buffer->buffer); + return false; + } + + err = tdm_hwc_validate(output->tdm.hwc, NULL, 0, &num_changes); + if (err != TDM_ERROR_NONE) { + ds_err("Could not hwc validate"); + ds_buffer_unlock(buffer->buffer); + return false; + } + + err = tdm_hwc_accept_validation(output->tdm.hwc); + if (err != TDM_ERROR_NONE) { + ds_err("Could not hwc accept validation"); + ds_buffer_unlock(buffer->buffer); + return false; + } + + err = tdm_hwc_commit(output->tdm.hwc, 0, tdm_output_hwc_commit_handler, output); + if (err != TDM_ERROR_NONE) { + ds_err("Could not hwc commit"); + ds_buffer_unlock(buffer->buffer); + return false; + } + + if (output->back_buffer) { + output->back_buffer->released = true; + ds_buffer_unlock(output->back_buffer->buffer); + } + + output->back_buffer = buffer; + + ds_dbg("Swap Buffer!!!!!"); + } + return true; } @@ -94,6 +238,12 @@ tdm_output_destroy(struct ds_tdm_output *output) free(mode); } + if (output->back_buffer) + ds_buffer_unlock(output->back_buffer->buffer); + + if (output->front_buffer) + ds_buffer_unlock(output->front_buffer->buffer); + free(output); } @@ -105,7 +255,7 @@ tdm_output_init_modes(struct ds_tdm_output *output) tdm_error err; int num_modes, i; - err = tdm_output_get_available_modes(output->tdm_output, &tdm_modes, + err = tdm_output_get_available_modes(output->tdm.output, &tdm_modes, &num_modes); if (err != TDM_ERROR_NONE) { ds_err("Could not get available modes: output(%p)", output); @@ -139,7 +289,27 @@ tdm_output_init_modes(struct ds_tdm_output *output) wl_list_insert(&output->base.modes, &mode->base.link); else wl_list_insert(output->base.modes.prev, &mode->base.link); + + // FIXME + if (mode->base.preferred) { + err = tdm_output_set_mode(output->tdm.output, tdm_mode); + if (err != TDM_ERROR_NONE) { + ds_err("Could not set mode"); + } + } } return true; } + +static void +tdm_output_init_hwc(struct ds_tdm_output *output) +{ + tdm_error err; + + output->tdm.hwc = tdm_output_get_hwc(output->tdm.output, &err); + if (err != TDM_ERROR_NONE || !output->tdm.hwc) { + ds_err("Could not get tdm_hwc: output(%p)", output); + return; + } +} diff --git a/src/libds/backend/tdm/tdm.h b/src/libds/backend/tdm/tdm.h index 3a8990b..2b1c1e4 100644 --- a/src/libds/backend/tdm/tdm.h +++ b/src/libds/backend/tdm/tdm.h @@ -26,6 +26,7 @@ struct ds_tdm_backend struct wl_event_source *tdm_event; struct wl_list outputs; // ds_tdm_output.link + struct wl_list buffers; // ds_tdm_buffer.link struct wl_listener display_destroy; @@ -37,17 +38,35 @@ struct ds_tdm_output { struct ds_output base; - struct ds_tdm_backend *tdm; - tdm_output *tdm_output; + struct ds_tdm_backend *backend; + struct ds_tdm_buffer *front_buffer, *back_buffer; + + struct { + tdm_output *output; + tdm_hwc *hwc; + } tdm; struct wl_list link; enum ds_tdm_output_status status; }; +struct ds_tdm_buffer +{ + struct ds_buffer *buffer; + tbm_surface_h surface; + struct wl_list link; // ds_wl_backend.buffers + struct wl_listener buffer_destroy; + + bool released; +}; + struct ds_tdm_backend *tdm_backend_from_backend(struct ds_backend *backend); struct ds_tdm_output *create_tdm_output(struct ds_tdm_backend *tdm, tdm_output *tdm_output); +void destroy_tdm_buffer(struct ds_tdm_buffer *buffer); + + #endif -- 2.7.4 From e884340a9a401eb3928ae0e0e6da5c56040f8bbd Mon Sep 17 00:00:00 2001 From: Seunghun Lee Date: Thu, 24 Feb 2022 15:01:35 +0900 Subject: [PATCH 08/10] Add an example of tdm backend Change-Id: I836ba2b5b88d3799c7b095051e255779d653fa10 --- examples/meson.build | 14 ++- examples/tdm-backend.c | 248 +++++++++++++++++++++++++++++++++++++++++++++++++ meson.build | 3 + packaging/libds.spec | 1 + 4 files changed, 261 insertions(+), 5 deletions(-) create mode 100644 examples/tdm-backend.c diff --git a/examples/meson.build b/examples/meson.build index a107a77..530cbdd 100644 --- a/examples/meson.build +++ b/examples/meson.build @@ -1,15 +1,18 @@ -project('libds-samples', 'c', - version : '0.1', - default_options : ['warning_level=3']) - common_deps = [ + dep_libds, dependency('wayland-server', required: true), - dependency('libds', required: true), ] executable('wl-backend', 'wl-backend.c', dependencies: common_deps, + install_dir: libds_bindir, + install : true) + +executable('tdm-backend', + 'tdm-backend.c', + dependencies: common_deps, + install_dir: libds_bindir, install : true) executable('tinyds', @@ -19,4 +22,5 @@ executable('tinyds', dependency('pixman-1', required: true), dependency('libdrm', required: true), ], + install_dir: libds_bindir, install : true) diff --git a/examples/tdm-backend.c b/examples/tdm-backend.c new file mode 100644 index 0000000..15f5711 --- /dev/null +++ b/examples/tdm-backend.c @@ -0,0 +1,248 @@ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define WIDTH 1280 +#define HEIGHT 720 + +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 wl_event_source *stdin_source; + + struct wl_listener new_output; + struct wl_listener output_destroy; + struct wl_listener output_frame; + + 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); +static int stdin_dispatch(int fd, uint32_t mask, void *data); + +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); + + ds_backend_start(server->backend); + + draw_output(server); + + struct wl_event_loop *loop = wl_display_get_event_loop(display); + server->stdin_source = wl_event_loop_add_fd(loop, STDIN_FILENO, + WL_EVENT_READABLE, stdin_dispatch, server); + + wl_display_run(display); + + fini_server(server); + wl_display_destroy(display); + return 0; +} + +static void +backend_handle_new_output(struct wl_listener *listener, void *data) +{ + struct server *server; + struct ds_output *output; + + server = wl_container_of(listener, server, new_output); + output = data; + ds_inf("New output(%p)", output); + + + if (server->output) + return; + + server->output = output; + + server->output_destroy.notify = output_handle_destroy; + ds_output_add_destroy_listener(server->output, + &server->output_destroy); + + server->output_frame.notify = output_handle_frame; + ds_output_add_frame_listener(server->output, + &server->output_frame); +} + +static void +init_server(struct server *server, struct wl_display *display) +{ + server->display = display; + server->front_buffer = NULL; + + server->backend = ds_tdm_backend_create(display); + assert(server->backend); + + + server->new_output.notify = backend_handle_new_output; + ds_backend_add_new_output_listener(server->backend, + &server->new_output); + + server->allocator = ds_tbm_allocator_create(); + assert(server->allocator); + + server->swapchain = ds_swapchain_create(server->allocator, + server->width, server->height, WL_SHM_FORMAT_XRGB8888); + assert(server->swapchain); +} + +static void +fini_server(struct server *server) +{ + wl_list_remove(&server->new_output.link); + wl_list_remove(&server->output_destroy.link); + wl_list_remove(&server->output_frame.link); + if (server->front_buffer) + ds_buffer_unlock(server->front_buffer); + ds_swapchain_destroy(server->swapchain); + ds_allocator_destroy(server->allocator); +} + +static void +output_handle_destroy(struct wl_listener *listener, + void *data __attribute__((unused))) +{ + struct server *server = + wl_container_of(listener, server, output_destroy); + wl_display_terminate(server->display); +} + +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_frame(struct wl_listener *listener, + void *data __attribute__((unused))) +{ + struct server *server = + wl_container_of(listener, server, 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; +} + +static int +stdin_dispatch(int fd, uint32_t mask, void *data) +{ + struct server *server = data; + + wl_display_terminate(server->display); + + return 1; +} diff --git a/meson.build b/meson.build index 24506b6..36bfda9 100644 --- a/meson.build +++ b/meson.build @@ -15,6 +15,7 @@ libds_version_minor = version_arr[1] libds_version_patch = version_arr[2] dir_prefix = get_option('prefix') +libds_bindir = join_paths(dir_prefix, get_option('bindir')) libds_inc = include_directories('include') common_inc = [ include_directories('.'), libds_inc ] @@ -24,8 +25,10 @@ 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') +subdir('examples') configure_file(output: 'config.h', install: false, configuration: cdata) diff --git a/packaging/libds.spec b/packaging/libds.spec index aaf79b3..69d58f6 100644 --- a/packaging/libds.spec +++ b/packaging/libds.spec @@ -57,3 +57,4 @@ ninja -C builddir install %{_includedir}/* %{_libdir}/pkgconfig/libds.pc %{_libdir}/libds.so +%{_bindir}/* -- 2.7.4 From 4675302df43ece109a784a0ddfcf4064eb377955 Mon Sep 17 00:00:00 2001 From: Seunghun Lee Date: Thu, 24 Feb 2022 15:59:36 +0900 Subject: [PATCH 09/10] Move ds_backend implementation no functional changes Change-Id: If598451fc62155396478358b3c617c14112c8449 --- src/libds/{backend => }/backend.c | 0 src/libds/backend/meson.build | 4 ---- src/libds/meson.build | 1 + 3 files changed, 1 insertion(+), 4 deletions(-) rename src/libds/{backend => }/backend.c (100%) diff --git a/src/libds/backend/backend.c b/src/libds/backend.c similarity index 100% rename from src/libds/backend/backend.c rename to src/libds/backend.c diff --git a/src/libds/backend/meson.build b/src/libds/backend/meson.build index e095efd..c24f074 100644 --- a/src/libds/backend/meson.build +++ b/src/libds/backend/meson.build @@ -1,6 +1,2 @@ -libds_files += files( - 'backend.c', -) - subdir('wayland') subdir('tdm') diff --git a/src/libds/meson.build b/src/libds/meson.build index 8a88b62..e7f2970 100644 --- a/src/libds/meson.build +++ b/src/libds/meson.build @@ -19,6 +19,7 @@ libds_files = [ 'xdg_shell/xdg_surface.c', 'xdg_shell/xdg_toplevel.c', 'pixel_format.c', + 'backend.c', ] protocols = { -- 2.7.4 From 16a756160fbfa67eabb7c3880c533fd6607ecbf5 Mon Sep 17 00:00:00 2001 From: Seunghun Lee Date: Thu, 24 Feb 2022 17:36:08 +0900 Subject: [PATCH 10/10] Flesh out ds_buffer Change-Id: I3c8bb8706b3a1bfdb05eeb31eb9715fd96933ed5 --- include/libds/buffer.h | 11 ++++++++++ src/libds/buffer.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 65 insertions(+), 2 deletions(-) diff --git a/include/libds/buffer.h b/include/libds/buffer.h index c91cba7..b3d38fa 100644 --- a/include/libds/buffer.h +++ b/include/libds/buffer.h @@ -33,6 +33,13 @@ struct ds_shm_attributes off_t offset; }; +struct ds_buffer_resource_interface +{ + const char *name; + bool (*is_instance)(struct wl_resource *resource); + struct ds_buffer *(*from_resource)(struct wl_resource *resource); +}; + struct ds_buffer * ds_buffer_from_resource(struct wl_resource *resource); @@ -67,6 +74,10 @@ ds_buffer_begin_data_ptr_access(struct ds_buffer *buffer, void ds_buffer_end_data_ptr_access(struct ds_buffer *buffer); +void +ds_buffer_register_resource_interface( + const struct ds_buffer_resource_interface *iface); + #ifdef __cplusplus } #endif diff --git a/src/libds/buffer.c b/src/libds/buffer.c index 70de23f..167d3a7 100644 --- a/src/libds/buffer.c +++ b/src/libds/buffer.c @@ -6,8 +6,12 @@ #include "libds/log.h" #include "libds/interfaces/buffer.h" +static struct wl_array buffer_resource_interfaces = {0}; + static void buffer_consider_destroy(struct ds_buffer *buffer); static bool ds_resource_is_buffer(struct wl_resource *resource); +static const struct ds_buffer_resource_interface * +get_buffer_resource_iface(struct wl_resource *resource); WL_EXPORT void ds_buffer_init(struct ds_buffer *buffer, const struct ds_buffer_interface *iface, @@ -25,6 +29,7 @@ WL_EXPORT struct ds_buffer * ds_buffer_from_resource(struct wl_resource *resource) { struct ds_buffer *buffer; + const struct ds_buffer_resource_interface *iface; assert(resource && ds_resource_is_buffer(resource)); @@ -39,8 +44,19 @@ ds_buffer_from_resource(struct wl_resource *resource) buffer = ds_buffer_lock(&shm_client_buffer->base); } else { - // TODO; - buffer = NULL; + iface = get_buffer_resource_iface(resource); + if (!iface) { + ds_err("Unknown buffer type"); + return NULL; + } + + buffer = iface->from_resource(resource); + if (!buffer) { + ds_err("Failed to create %s buffer", iface->name); + return NULL; + } + + buffer = ds_buffer_lock(buffer); } return buffer; @@ -129,6 +145,28 @@ ds_buffer_get_size(struct ds_buffer *buffer, int *out_width, int *out_height) *out_height = buffer->height; } +WL_EXPORT void +ds_buffer_register_resource_interface( + const struct ds_buffer_resource_interface *iface) +{ + const struct ds_buffer_resource_interface **iface_ptr; + + assert(iface); + assert(iface->is_instance); + assert(iface->from_resource); + + wl_array_for_each(iface_ptr, &buffer_resource_interfaces) { + if (*iface_ptr == iface) { + ds_dbg("ds_buffer_resource_interface %s has already " + "been registered", iface->name); + return; + } + } + + iface_ptr = wl_array_add(&buffer_resource_interfaces, sizeof(iface)); + *iface_ptr = iface; +} + static void buffer_consider_destroy(struct ds_buffer *buffer) { @@ -147,3 +185,17 @@ ds_resource_is_buffer(struct wl_resource *resource) return strcmp(wl_resource_get_class(resource), wl_buffer_interface.name) == 0; } + +static const struct ds_buffer_resource_interface * +get_buffer_resource_iface(struct wl_resource *resource) +{ + struct ds_buffer_resource_interface **iface_ptr; + + wl_array_for_each(iface_ptr, &buffer_resource_interfaces) { + if ((*iface_ptr)->is_instance(resource)) { + return *iface_ptr; + } + } + + return NULL; +} -- 2.7.4