From 7eeb8e9a2c70f7b3d604bf1af1d23bf3cfc097b9 Mon Sep 17 00:00:00 2001 From: Seunghun Lee Date: Wed, 2 Mar 2022 18:09:39 +0900 Subject: [PATCH] Add tdm backend server and client as an example Change-Id: If5305c3ee0ae11d8e0c9d2aed581e4142a2e4c2a --- clients/meson.build | 45 ++++ clients/simple-tbm.c | 502 +++++++++++++++++++++++++++++++++++++++++++ examples/meson.build | 14 +- examples/pixman-helper.c | 89 ++++++++ examples/pixman-helper.h | 15 ++ examples/tbm-server-helper.c | 222 +++++++++++++++++++ examples/tbm-server-helper.h | 35 +++ examples/tinyds-helper.c | 18 -- examples/tinyds-helper.h | 15 -- examples/tinyds-tdm.c | 173 ++++++--------- meson.build | 1 + packaging/libds.spec | 2 + 12 files changed, 988 insertions(+), 143 deletions(-) create mode 100644 clients/meson.build create mode 100644 clients/simple-tbm.c create mode 100644 examples/pixman-helper.c create mode 100644 examples/pixman-helper.h create mode 100644 examples/tbm-server-helper.c create mode 100644 examples/tbm-server-helper.h delete mode 100644 examples/tinyds-helper.c delete mode 100644 examples/tinyds-helper.h diff --git a/clients/meson.build b/clients/meson.build new file mode 100644 index 0000000..360c425 --- /dev/null +++ b/clients/meson.build @@ -0,0 +1,45 @@ +wayland_tbm_client = dependency('wayland-tbm-client', required: false) +libtbm = dependency('libtbm', required: false) + +if not wayland_tbm_client.found() or not libtbm.found() + subdir_done() +endif + +simple_tbm_files = ['simple-tbm.c'] +simple_tbm_deps = [ + dependency('wayland-client', required: true), + wayland_tbm_client, + libtbm, +] + +protocols = { + 'xdg-shell': wl_protocol_dir / 'stable/xdg-shell/xdg-shell.xml', +} + +protocols_code = {} +protocols_client_header = {} +foreach name, path : protocols + code = custom_target( + name.underscorify() + '_c', + input: path, + output: '@BASENAME@-protocol.c', + command: [wayland_scanner, 'private-code', '@INPUT@', '@OUTPUT@'], + ) + simple_tbm_files += code + + 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, + ) + simple_tbm_files += client_header +endforeach + +executable('ds-simple-tbm', + simple_tbm_files, + dependencies: simple_tbm_deps, + install_dir: libds_bindir, + install: true, +) diff --git a/clients/simple-tbm.c b/clients/simple-tbm.c new file mode 100644 index 0000000..f572b60 --- /dev/null +++ b/clients/simple-tbm.c @@ -0,0 +1,502 @@ +/* + * Copyright © 2011 Benjamin Franzke + * Copyright © 2010 Intel Corporation + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include "xdg-shell-client-protocol.h" + +static uint64_t buffer_info_key; +#define BUFFER_INFO_KEY (unsigned long)(&buffer_info_key) + +struct display { + struct wl_display *display; + struct wl_registry *registry; + struct wl_compositor *compositor; + struct xdg_wm_base *wm_base; + struct wl_shm *shm; + struct wayland_tbm_client *wl_tbm; + bool has_xrgb; +}; + +struct window { + struct display *display; + int width, height; + struct wl_surface *surface; + struct xdg_surface *xdg_surface; + struct xdg_toplevel *xdg_toplevel; + struct wl_callback *callback; + tbm_surface_queue_h surface_queue; + bool wait_for_configure; +}; + +struct buffer_info { + struct window *window; + struct wl_buffer *wl_buffer; +}; + +static int running = 1; + +static void +redraw(void *data, struct wl_callback *callback, uint32_t time); + +static void +handle_xdg_surface_configure(void *data, struct xdg_surface *surface, + uint32_t serial) +{ + struct window *window = data; + + xdg_surface_ack_configure(surface, serial); + + if (window->wait_for_configure) { + redraw(window, NULL, 0); + window->wait_for_configure = false; + } +} + +static const struct xdg_surface_listener xdg_surface_listener = { + handle_xdg_surface_configure, +}; + +static void +handle_xdg_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel, + int32_t width, int32_t height, + struct wl_array *state) +{ +} + +static void +handle_xdg_toplevel_close(void *data, struct xdg_toplevel *xdg_toplevel) +{ + running = 0; +} + +static const struct xdg_toplevel_listener xdg_toplevel_listener = { + handle_xdg_toplevel_configure, + handle_xdg_toplevel_close, +}; + +static struct window * +create_window(struct display *display, int width, int height) +{ + struct window *window; + + window = calloc(1, sizeof *window); + if (!window) + return NULL; + + window->callback = NULL; + window->display = display; + window->width = width; + window->height = height; + window->surface = wl_compositor_create_surface(display->compositor); + + if (display->wm_base) { + window->xdg_surface = + xdg_wm_base_get_xdg_surface(display->wm_base, + window->surface); + assert(window->xdg_surface); + xdg_surface_add_listener(window->xdg_surface, + &xdg_surface_listener, window); + + window->xdg_toplevel = + xdg_surface_get_toplevel(window->xdg_surface); + assert(window->xdg_toplevel); + xdg_toplevel_add_listener(window->xdg_toplevel, + &xdg_toplevel_listener, window); + + xdg_toplevel_set_title(window->xdg_toplevel, "simple-tbm"); + wl_surface_commit(window->surface); + window->wait_for_configure = true; + } else { + assert(0); + } + + window->surface_queue = + wayland_tbm_client_create_surface_queue(display->wl_tbm, + window->surface, + 3, + width, + height, + TBM_FORMAT_XRGB8888); + assert(window->surface_queue); + + return window; +} + +static void +destroy_window(struct window *window) +{ + tbm_surface_queue_destroy(window->surface_queue); + + if (window->callback) + wl_callback_destroy(window->callback); + + if (window->xdg_toplevel) + xdg_toplevel_destroy(window->xdg_toplevel); + if (window->xdg_surface) + xdg_surface_destroy(window->xdg_surface); + wl_surface_destroy(window->surface); + free(window); +} + +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 void +buffer_info_free_cb(void *data) +{ + struct buffer_info *buffer_info = data; + + if (!buffer_info) + return; + + wayland_tbm_client_destroy_buffer(buffer_info->window->display->wl_tbm, + buffer_info->wl_buffer); + free(buffer_info); +} + +static void +buffer_handle_release(void *data, struct wl_buffer *wl_buffer) +{ + tbm_surface_h surface = data; + struct buffer_info *buffer_info; + + tbm_surface_internal_get_user_data(surface, BUFFER_INFO_KEY, + (void **)&buffer_info); + if (buffer_info) + tbm_surface_queue_release(buffer_info->window->surface_queue, surface); +} + +static const struct wl_buffer_listener buffer_listener = { + .release = buffer_handle_release, +}; + +static const struct wl_callback_listener frame_listener; + +static void +redraw(void *data, struct wl_callback *callback, uint32_t time) +{ + struct window *window = data; + struct buffer_info *buffer_info = NULL; + tbm_surface_h surface = NULL; + tbm_surface_info_s surface_info; + + if (!tbm_surface_queue_can_dequeue(window->surface_queue, 0)) + return; + + tbm_surface_queue_dequeue(window->surface_queue, &surface); + assert(surface); + + tbm_surface_internal_get_user_data(surface, BUFFER_INFO_KEY, + (void **)&buffer_info); + if (!buffer_info) { + buffer_info = calloc(1, sizeof *buffer_info); + assert(buffer_info); + + tbm_surface_internal_add_user_data(surface, BUFFER_INFO_KEY, buffer_info_free_cb); + tbm_surface_internal_set_user_data(surface, BUFFER_INFO_KEY, buffer_info); + + buffer_info->wl_buffer = + wayland_tbm_client_create_buffer(window->display->wl_tbm, surface); + assert(buffer_info->wl_buffer); + + wl_buffer_add_listener(buffer_info->wl_buffer, &buffer_listener, + surface); + + buffer_info->window = window; + } + + tbm_surface_map(surface, TBM_SURF_OPTION_WRITE, &surface_info); + + paint_pixels(surface_info.planes[0].ptr, 20, + (surface_info.planes[0].stride/4), surface_info.height, time); + + tbm_surface_unmap(surface); + + wl_surface_attach(window->surface, buffer_info->wl_buffer, 0, 0); + wl_surface_damage(window->surface, + 20, 20, window->width - 40, window->height - 40); + + if (callback) + wl_callback_destroy(callback); + + window->callback = wl_surface_frame(window->surface); + wl_callback_add_listener(window->callback, &frame_listener, window); + wl_surface_commit(window->surface); +} + +static const struct wl_callback_listener frame_listener = { + redraw +}; + +static void +shm_format(void *data, struct wl_shm *wl_shm, uint32_t format) +{ + struct display *d = data; + + if (format == WL_SHM_FORMAT_XRGB8888) + d->has_xrgb = true; +} + +struct wl_shm_listener shm_listener = { + shm_format +}; + +static void +xdg_wm_base_ping(void *data, struct xdg_wm_base *shell, uint32_t serial) +{ + xdg_wm_base_pong(shell, serial); +} + +static const struct xdg_wm_base_listener xdg_wm_base_listener = { + xdg_wm_base_ping, +}; + +static void +registry_handle_global(void *data, struct wl_registry *registry, + uint32_t id, const char *interface, uint32_t version) +{ + struct display *d = data; + + if (strcmp(interface, "wl_compositor") == 0) { + d->compositor = + wl_registry_bind(registry, + id, &wl_compositor_interface, 1); + } else if (strcmp(interface, "xdg_wm_base") == 0) { + d->wm_base = wl_registry_bind(registry, + id, &xdg_wm_base_interface, 1); + xdg_wm_base_add_listener(d->wm_base, &xdg_wm_base_listener, d); + } else if (strcmp(interface, "wl_shm") == 0) { + d->shm = wl_registry_bind(registry, + id, &wl_shm_interface, 1); + wl_shm_add_listener(d->shm, &shm_listener, d); + } +} + +static void +registry_handle_global_remove(void *data, struct wl_registry *registry, + uint32_t name) +{ +} + +static const struct wl_registry_listener registry_listener = { + registry_handle_global, + registry_handle_global_remove +}; + +static struct display * +create_display(void) +{ + struct display *display; + + display = calloc(1, sizeof *display); + if (display == NULL) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + display->display = wl_display_connect(NULL); + assert(display->display); + + display->has_xrgb = false; + display->registry = wl_display_get_registry(display->display); + wl_registry_add_listener(display->registry, + ®istry_listener, display); + wl_display_roundtrip(display->display); + if (display->shm == NULL) { + fprintf(stderr, "No wl_shm global\n"); + exit(1); + } + + wl_display_roundtrip(display->display); + + /* + * Why do we need two roundtrips here? + * + * wl_display_get_registry() sends a request to the server, to which + * the server replies by emitting the wl_registry.global events. + * The first wl_display_roundtrip() sends wl_display.sync. The server + * first processes the wl_display.get_registry which includes sending + * the global events, and then processes the sync. Therefore when the + * sync (roundtrip) returns, we are guaranteed to have received and + * processed all the global events. + * + * While we are inside the first wl_display_roundtrip(), incoming + * events are dispatched, which causes registry_handle_global() to + * be called for each global. One of these globals is wl_shm. + * registry_handle_global() sends wl_registry.bind request for the + * wl_shm global. However, wl_registry.bind request is sent after + * the first wl_display.sync, so the reply to the sync comes before + * the initial events of the wl_shm object. + * + * The initial events that get sent as a reply to binding to wl_shm + * include wl_shm.format. These tell us which pixel formats are + * supported, and we need them before we can create buffers. They + * don't change at runtime, so we receive them as part of init. + * + * When the reply to the first sync comes, the server may or may not + * have sent the initial wl_shm events. Therefore we need the second + * wl_display_roundtrip() call here. + * + * The server processes the wl_registry.bind for wl_shm first, and + * the second wl_display.sync next. During our second call to + * wl_display_roundtrip() the initial wl_shm events are received and + * processed. Finally, when the reply to the second wl_display.sync + * arrives, it guarantees we have processed all wl_shm initial events. + * + * This sequence contains two examples on how wl_display_roundtrip() + * can be used to guarantee, that all reply events to a request + * have been received and processed. This is a general Wayland + * technique. + */ + + if (!display->has_xrgb) { + fprintf(stderr, "WL_SHM_FORMAT_XRGB32 not available\n"); + exit(1); + } + + display->wl_tbm = wayland_tbm_client_init(display->display); + if (!display->wl_tbm) { + fprintf(stderr, "failed wayland_tbm_client_init()\n"); + exit(1); + } + + return display; +} + +static void +destroy_display(struct display *display) +{ + if (display->shm) + wl_shm_destroy(display->shm); + + if (display->wm_base) + xdg_wm_base_destroy(display->wm_base); + + if (display->compositor) + wl_compositor_destroy(display->compositor); + + wayland_tbm_client_deinit(display->wl_tbm); + wl_registry_destroy(display->registry); + wl_display_flush(display->display); + wl_display_disconnect(display->display); + free(display); +} + +static void +signal_int(int signum) +{ + running = 0; +} + +int +main(int argc, char **argv) +{ + struct sigaction sigint; + struct display *display; + struct window *window; + int ret = 0; + + display = create_display(); + window = create_window(display, 250, 250); + if (!window) + return 1; + + sigint.sa_handler = signal_int; + sigemptyset(&sigint.sa_mask); + sigint.sa_flags = SA_RESETHAND; + sigaction(SIGINT, &sigint, NULL); + + /* Initialise damage to full surface, so the padding gets painted */ + wl_surface_damage(window->surface, 0, 0, + window->width, window->height); + + if (!window->wait_for_configure) + redraw(window, NULL, 0); + + while (running && ret != -1) + ret = wl_display_dispatch(display->display); + + fprintf(stderr, "simple-shm exiting\n"); + + destroy_window(window); + destroy_display(display); + + return 0; +} diff --git a/examples/meson.build b/examples/meson.build index 66336f5..7a841e4 100644 --- a/examples/meson.build +++ b/examples/meson.build @@ -24,16 +24,24 @@ if features.get('tdm-backend') 'tdm-backend.c', dependencies: common_deps, install_dir: libds_bindir, - install : true) + install : true + ) + tinyds_tdm_files = [ + 'tinyds-tdm.c', + 'tbm-server-helper.c', + 'pixman-helper.c', + ] executable('tinyds-tdm', - ['tinyds-tdm.c', 'tinyds-helper.c'], + tinyds_tdm_files, dependencies: [ common_deps, dependency('pixman-1', required: true), dependency('libdrm', required: true), dependency('libtbm', required: true), + dependency('wayland-tbm-server', required: true), ], install_dir: libds_bindir, - install : true) + install : true + ) endif diff --git a/examples/pixman-helper.c b/examples/pixman-helper.c new file mode 100644 index 0000000..5d42ec6 --- /dev/null +++ b/examples/pixman-helper.c @@ -0,0 +1,89 @@ +#include "pixman-helper.h" + +#include +#include + +static void destroy_pixman_image(pixman_image_t *image, void *data); +static uint32_t convert_drm_format_to_pixman(uint32_t fmt); +static pixman_color_t *color_rgb888(pixman_color_t *tmp, + uint8_t r, uint8_t g, uint8_t b); + +pixman_image_t * +pixman_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; +} + + +void +pixman_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 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 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 void +destroy_pixman_image(pixman_image_t *image, void *data) +{ + struct ds_buffer *buffer = data; + ds_buffer_end_data_ptr_access(buffer); + ds_buffer_unlock(buffer); +} diff --git a/examples/pixman-helper.h b/examples/pixman-helper.h new file mode 100644 index 0000000..aa039ff --- /dev/null +++ b/examples/pixman-helper.h @@ -0,0 +1,15 @@ +#ifndef EXAMPLES_PIXMAN_HELPER_H +#define EXAMPLES_PIXMAN_HELPER_H + +#include +#include + +pixman_image_t * +pixman_image_from_buffer(struct ds_buffer *buffer, + enum ds_buffer_data_ptr_access_flag access_flag); + +void +pixman_image_fill_color(pixman_image_t *image, + uint8_t r, uint8_t g, uint8_t b); + +#endif diff --git a/examples/tbm-server-helper.c b/examples/tbm-server-helper.c new file mode 100644 index 0000000..bd3a34d --- /dev/null +++ b/examples/tbm-server-helper.c @@ -0,0 +1,222 @@ +#include "tbm-server-helper.h" + +#include +#include +#include +#include +#include +#include + +static const struct ds_buffer_resource_interface tbm_buffer_resource_iface; +static void tbm_server_handle_display_destroy(struct wl_listener *listener, + void *data); +static struct tbm_client_buffer * +tbm_client_buffer_create(struct wl_resource *resource); + +bool +tbm_server_init_display(struct tbm_server *tbm, struct wl_display *display) +{ + tbm_bufmgr bufmgr; + + tbm->wl_tbm = wayland_tbm_server_init(display, NULL, -1, 0); + if (!tbm->wl_tbm) { + return false; + } + + bufmgr = wayland_tbm_server_get_bufmgr(tbm->wl_tbm); + if (!bufmgr) { + wayland_tbm_server_deinit(tbm->wl_tbm); + return false; + } + + if (!tbm_bufmgr_bind_native_display(bufmgr, (void *)display)) { + wayland_tbm_server_deinit(tbm->wl_tbm); + return false; + } + + ds_buffer_register_resource_interface(&tbm_buffer_resource_iface); + + tbm->display_destroy.notify = tbm_server_handle_display_destroy; + wl_display_add_destroy_listener(display, &tbm->display_destroy); + + return true; +} + +static void +tbm_server_handle_display_destroy(struct wl_listener *listener, void *data) +{ + struct tbm_server *tbm; + + tbm = wl_container_of(listener, tbm, display_destroy); + wayland_tbm_server_deinit(tbm->wl_tbm); +} + +static bool +tbm_buffer_resource_iface_is_instance(struct wl_resource *resource) +{ + return !!wayland_tbm_server_get_surface(NULL, resource); +} + +static void +tbm_client_buffer_handle_resource_destroy(struct wl_listener *listener, + void *data) +{ + struct tbm_client_buffer *buffer; + + buffer = wl_container_of(listener, buffer, resource_destroy); + + buffer->resource = NULL; + buffer->surface = NULL; + wl_list_remove(&buffer->resource_destroy.link); + wl_list_init(&buffer->resource_destroy.link); + + ds_buffer_drop(&buffer->base); +} + +static struct tbm_client_buffer * +tbm_client_buffer_get_or_create(struct wl_resource *resource) +{ + struct tbm_client_buffer *buffer; + struct wl_listener *resource_destroy_listener; + + resource_destroy_listener = wl_resource_get_destroy_listener(resource, + tbm_client_buffer_handle_resource_destroy);; + if (resource_destroy_listener) { + buffer = wl_container_of(resource_destroy_listener, + buffer, resource_destroy); + return buffer; + } + + return tbm_client_buffer_create(resource); +} + +static struct ds_buffer * +tbm_buffer_resource_iface_from_resource(struct wl_resource *resource) +{ + struct tbm_client_buffer *buffer; + + buffer = tbm_client_buffer_get_or_create(resource); + assert(buffer); + + return &buffer->base; +} + +static const struct ds_buffer_resource_interface tbm_buffer_resource_iface = { + .name = "tbm", + .is_instance = tbm_buffer_resource_iface_is_instance, + .from_resource = tbm_buffer_resource_iface_from_resource, +}; + +static const struct ds_buffer_interface tbm_client_buffer_iface; + +static struct tbm_client_buffer * +tbm_client_buffer_from_buffer(struct ds_buffer *ds_buffer) +{ + assert(ds_buffer->iface == &tbm_client_buffer_iface); + return (struct tbm_client_buffer *)ds_buffer; +} + +static void +tbm_client_buffer_destroy(struct ds_buffer *ds_buffer) +{ + struct tbm_client_buffer *buffer; + + buffer = tbm_client_buffer_from_buffer(ds_buffer); + + ds_inf("Destroy TBM client buffer(%p)", buffer); + + wl_list_remove(&buffer->resource_destroy.link); + wl_list_remove(&buffer->buffer_release.link); + free(buffer); +} + +static bool +tbm_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) +{ + struct tbm_client_buffer *buffer; + tbm_surface_info_s info; + tbm_bo_access_option op = TBM_OPTION_NONE; + int err; + + buffer = tbm_client_buffer_from_buffer(ds_buffer); + + if (flags & DS_BUFFER_DATA_PTR_ACCESS_READ) + op |= TBM_OPTION_READ; + + if (flags & DS_BUFFER_DATA_PTR_ACCESS_WRITE) + op |= TBM_OPTION_WRITE; + + err = tbm_surface_map(buffer->surface, op, &info); + if (err != TBM_SURFACE_ERROR_NONE) { + ds_err("Failed tbm_surface_map()"); + return false; + } + + *format = DRM_FORMAT_XRGB8888; // FIXME + *stride = info.planes[0].stride; + *data = info.planes[0].ptr; + + return true; +} + +static void +tbm_client_buffer_end_data_ptr_access(struct ds_buffer *ds_buffer) +{ + struct tbm_client_buffer *buffer; + + buffer = tbm_client_buffer_from_buffer(ds_buffer); + + tbm_surface_unmap(buffer->surface); +} + +static const struct ds_buffer_interface tbm_client_buffer_iface = { + .destroy = tbm_client_buffer_destroy, + .begin_data_ptr_access = tbm_client_buffer_begin_data_ptr_access, + .end_data_ptr_access = tbm_client_buffer_end_data_ptr_access, +}; + +static void +tbm_client_buffer_handle_release(struct wl_listener *listener, void *data) +{ + struct tbm_client_buffer *buffer; + + buffer = wl_container_of(listener, buffer, buffer_release); + if (buffer->resource) + wl_buffer_send_release(buffer->resource); +} + +static struct tbm_client_buffer * +tbm_client_buffer_create(struct wl_resource *resource) +{ + struct tbm_client_buffer *buffer; + tbm_surface_h surface; + int32_t width, height; + + surface = wayland_tbm_server_get_surface(NULL, resource); + assert(surface); + + width = tbm_surface_get_width(surface); + height = tbm_surface_get_height(surface); + + buffer = calloc(1, sizeof *buffer); + assert(buffer); + + ds_buffer_init(&buffer->base, &tbm_client_buffer_iface, width, height); + + buffer->resource = resource; + buffer->surface = surface; + buffer->format = tbm_surface_get_format(surface); + + buffer->buffer_release.notify = tbm_client_buffer_handle_release; + ds_buffer_add_release_listener(&buffer->base, &buffer->buffer_release); + + buffer->resource_destroy.notify = + tbm_client_buffer_handle_resource_destroy; + wl_resource_add_destroy_listener(resource, &buffer->resource_destroy); + + ds_inf("TBM client buffer(%p) created", buffer); + + return buffer; +} diff --git a/examples/tbm-server-helper.h b/examples/tbm-server-helper.h new file mode 100644 index 0000000..a82ad5b --- /dev/null +++ b/examples/tbm-server-helper.h @@ -0,0 +1,35 @@ +#ifndef EXAMPLES_TBM_SERVER_H +#define EXAMPLES_TBM_SERVER_H + +#include +#include +#include +#include +#include + +struct tbm_server +{ + struct wayland_tbm_server *wl_tbm; + + struct wl_listener display_destroy; +}; + +struct tbm_client_buffer +{ + struct ds_buffer base; + + tbm_surface_h surface; + struct wl_resource *resource; + + struct wl_listener buffer_release; + struct wl_listener resource_destroy; + + uint32_t format; + size_t stride; +}; + +bool +tbm_server_init_display(struct tbm_server *tbm_server, + struct wl_display *display); + +#endif diff --git a/examples/tinyds-helper.c b/examples/tinyds-helper.c deleted file mode 100644 index ef077b3..0000000 --- a/examples/tinyds-helper.c +++ /dev/null @@ -1,18 +0,0 @@ -#include "tinyds-helper.h" - -#include -#include - -bool -tinyds_renderer_init_display(struct tinyds_renderer *renderer, - struct wl_display *display) -{ - if (wl_display_init_shm(display)) { - ds_err("Could not initialize shm"); - return false; - } - - renderer->dummy = 1; - - return true; -} diff --git a/examples/tinyds-helper.h b/examples/tinyds-helper.h deleted file mode 100644 index 44415d6..0000000 --- a/examples/tinyds-helper.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef TINYDS_HELPER_H -#define TINYDS_HELPER_H - -#include -#include - -struct tinyds_renderer { - int dummy; -}; - -bool -tinyds_renderer_init_display(struct tinyds_renderer *renderer, - struct wl_display *display); - -#endif diff --git a/examples/tinyds-tdm.c b/examples/tinyds-tdm.c index 23ff48e..4207fb1 100644 --- a/examples/tinyds-tdm.c +++ b/examples/tinyds-tdm.c @@ -1,4 +1,5 @@ -#include "tinyds-helper.h" +#include "tbm-server-helper.h" +#include "pixman-helper.h" #include #include @@ -43,6 +44,8 @@ struct tinyds_output struct tinyds_server { + struct tbm_server tbm_server; + struct wl_display *display; struct ds_backend *backend; @@ -50,7 +53,7 @@ struct tinyds_server struct ds_xdg_shell *xdg_shell; struct tinyds_output *output; - struct tinyds_renderer renderer; + struct wl_event_source *stdin_source; struct wl_list views; @@ -74,12 +77,12 @@ struct tinyds_view bool mapped; }; -struct tinyds_server _tinyds; +struct tinyds_server tinyds; static bool init_server(struct tinyds_server *server, struct wl_display *display); +static int server_dispatch_stdin(int fd, uint32_t mask, void *data); 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); @@ -87,18 +90,22 @@ static void draw_view(struct tinyds_view *view, pixman_image_t *dst_image); int main(void) { - struct tinyds_server *server = &_tinyds; + struct tinyds_server *server = &tinyds; struct wl_display *display; + struct wl_event_loop *loop; const char *socket; + bool res; - ds_log_init(DS_DBG, NULL); + ds_log_init(DS_INF, NULL); display = wl_display_create(); assert(display); - assert(init_server(server, display) == true); + res = init_server(server, display); + assert(res); - assert(tinyds_renderer_init_display(&server->renderer, display)); + res = tbm_server_init_display(&server->tbm_server, display); + assert(res); socket = wl_display_add_socket_auto(display); assert(socket); @@ -109,6 +116,13 @@ main(void) ds_inf("Running Wayland compositor on WAYLAND_DISPLAY=%s", socket); + loop = wl_display_get_event_loop(display); + server->stdin_source = wl_event_loop_add_fd(loop, STDIN_FILENO, + WL_EVENT_READABLE, server_dispatch_stdin, server); + + wl_display_run(display); + + wl_display_destroy_clients(display); wl_display_destroy(display); return 0; @@ -175,6 +189,8 @@ server_new_xdg_surface(struct wl_listener *listener, void *data) ds_inf("New xdg_surface(%p)", (void *)xdg_surface); view = calloc(1, sizeof *view); + assert(view); + view->server = server; view->xdg_surface = xdg_surface; @@ -196,6 +212,9 @@ server_new_xdg_surface(struct wl_listener *listener, void *data) &view->surface_commit); wl_list_insert(server->views.prev, &view->link); + + view->x = rand() % 1000; + view->y = rand() % 500; } static void @@ -217,8 +236,24 @@ backend_handle_new_output(struct wl_listener *listener, void *data) if (!output) return; + output->allocator = ds_tbm_allocator_create(); + if (!output->allocator) { + free(output); + return; + } + + output->swapchain = ds_swapchain_create(output->allocator, + OUTPUT_WIDTH, OUTPUT_HEIGHT, DRM_FORMAT_XRGB8888); // FIXME output mode + if (!output->swapchain) { + ds_allocator_destroy(output->allocator); + free(output); + return; + } + output->server = server; output->ds_output = ds_output; + output->drawable = true; + output->damaged = true; output->output_destroy.notify = output_handle_destroy; ds_output_add_destroy_listener(ds_output, @@ -229,6 +264,8 @@ backend_handle_new_output(struct wl_listener *listener, void *data) &output->output_frame); server->output = output; + + draw_output(output); } static bool @@ -299,12 +336,8 @@ output_handle_frame(struct wl_listener *listener, void *data TINYDS_UNUSED) struct tinyds_output *output = wl_container_of(listener, output, output_frame); -} - -static void -draw_server(struct tinyds_server *server) -{ - draw_output(server->output); + output->drawable = true; + draw_output(output); } static void @@ -314,12 +347,6 @@ draw_server_with_damage(struct tinyds_server *server) draw_output(server->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) { @@ -334,14 +361,14 @@ draw_output(struct tinyds_output *output) if (!output_buffer) return; - output_image = image_from_buffer(output_buffer, + output_image = pixman_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); + pixman_image_fill_color(output_image, 80, 80, 80); wl_list_for_each(view, &output->server->views, link) { if (!view->mapped) @@ -362,6 +389,15 @@ draw_output(struct tinyds_output *output) } 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); +} + +static void draw_view(struct tinyds_view *view, pixman_image_t *dst_image) { struct ds_buffer *buffer; @@ -372,13 +408,14 @@ draw_view(struct tinyds_view *view, pixman_image_t *dst_image) if (!buffer) return; - src_image = image_from_buffer(buffer, + src_image = pixman_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, + 0, 0, 0, 0, + view->x, view->y, pixman_image_get_width(src_image), pixman_image_get_height(src_image)); pixman_image_unref(src_image); @@ -386,90 +423,12 @@ draw_view(struct tinyds_view *view, pixman_image_t *dst_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) +static int +server_dispatch_stdin(int fd, uint32_t mask, void *data) { - tmp->alpha = 65535; - tmp->red = (r << 8) + r; - tmp->green = (g << 8) + g; - tmp->blue = (b << 8) +b; + struct tinyds_server *server = data; - return tmp; -} + wl_display_terminate(server->display); -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); + return 1; } diff --git a/meson.build b/meson.build index 2bf1398..445fd6a 100644 --- a/meson.build +++ b/meson.build @@ -33,6 +33,7 @@ features = { subdir('include') subdir('src') subdir('examples') +subdir('clients') configure_file(output: 'config.h', install: false, configuration: cdata) diff --git a/packaging/libds.spec b/packaging/libds.spec index 69d58f6..3b479e2 100644 --- a/packaging/libds.spec +++ b/packaging/libds.spec @@ -16,6 +16,8 @@ BuildRequires: pkgconfig(libdrm) BuildRequires: pkgconfig(libtdm) BuildRequires: pkgconfig(libtbm) +BuildRequires: pkgconfig(wayland-tbm-server) +BuildRequires: pkgconfig(wayland-tbm-client) %description Wayland Compositor Library -- 2.7.4