summaryrefslogtreecommitdiffstats
path: root/uwac/libuwac/uwac-window.c
diff options
context:
space:
mode:
Diffstat (limited to 'uwac/libuwac/uwac-window.c')
-rw-r--r--uwac/libuwac/uwac-window.c883
1 files changed, 883 insertions, 0 deletions
diff --git a/uwac/libuwac/uwac-window.c b/uwac/libuwac/uwac-window.c
new file mode 100644
index 0000000..49728bd
--- /dev/null
+++ b/uwac/libuwac/uwac-window.c
@@ -0,0 +1,883 @@
+/*
+ * Copyright © 2014 David FORT <contact@hardening-consulting.com>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. The copyright holders make no representations
+ * about the suitability of this software for any purpose. It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <assert.h>
+#include <sys/mman.h>
+#include <errno.h>
+
+#include "uwac-priv.h"
+#include "uwac-utils.h"
+#include "uwac-os.h"
+
+#include <uwac/config.h>
+
+#define UWAC_INITIAL_BUFFERS 3
+
+static int bppFromShmFormat(enum wl_shm_format format)
+{
+ switch (format)
+ {
+ case WL_SHM_FORMAT_ARGB8888:
+ case WL_SHM_FORMAT_XRGB8888:
+ default:
+ return 4;
+ }
+}
+
+static void buffer_release(void* data, struct wl_buffer* buffer)
+{
+ UwacBufferReleaseData* releaseData = data;
+ UwacBuffer* uwacBuffer = &releaseData->window->buffers[releaseData->bufferIdx];
+ uwacBuffer->used = false;
+}
+
+static const struct wl_buffer_listener buffer_listener = { buffer_release };
+
+static void UwacWindowDestroyBuffers(UwacWindow* w)
+{
+ for (int i = 0; i < w->nbuffers; i++)
+ {
+ UwacBuffer* buffer = &w->buffers[i];
+#ifdef UWAC_HAVE_PIXMAN_REGION
+ pixman_region32_fini(&buffer->damage);
+#else
+ region16_uninit(&buffer->damage);
+#endif
+ UwacBufferReleaseData* releaseData =
+ (UwacBufferReleaseData*)wl_buffer_get_user_data(buffer->wayland_buffer);
+ wl_buffer_destroy(buffer->wayland_buffer);
+ free(releaseData);
+ munmap(buffer->data, buffer->size);
+ }
+
+ w->nbuffers = 0;
+ free(w->buffers);
+ w->buffers = NULL;
+}
+
+static int UwacWindowShmAllocBuffers(UwacWindow* w, int nbuffers, int allocSize, uint32_t width,
+ uint32_t height, enum wl_shm_format format);
+
+static void xdg_handle_toplevel_configure(void* data, struct xdg_toplevel* xdg_toplevel,
+ int32_t width, int32_t height, struct wl_array* states)
+{
+ UwacWindow* window = (UwacWindow*)data;
+ int scale = window->display->actual_scale;
+ width *= scale;
+ height *= scale;
+ UwacConfigureEvent* event = NULL;
+ int ret = 0;
+ int surfaceState = 0;
+ enum xdg_toplevel_state* state = NULL;
+ surfaceState = 0;
+ wl_array_for_each(state, states)
+ {
+ switch (*state)
+ {
+ case XDG_TOPLEVEL_STATE_MAXIMIZED:
+ surfaceState |= UWAC_WINDOW_MAXIMIZED;
+ break;
+
+ case XDG_TOPLEVEL_STATE_FULLSCREEN:
+ surfaceState |= UWAC_WINDOW_FULLSCREEN;
+ break;
+
+ case XDG_TOPLEVEL_STATE_ACTIVATED:
+ surfaceState |= UWAC_WINDOW_ACTIVATED;
+ break;
+
+ case XDG_TOPLEVEL_STATE_RESIZING:
+ surfaceState |= UWAC_WINDOW_RESIZING;
+ break;
+
+ default:
+ break;
+ }
+ }
+ window->surfaceStates = surfaceState;
+ event = (UwacConfigureEvent*)UwacDisplayNewEvent(window->display, UWAC_EVENT_CONFIGURE);
+
+ if (!event)
+ {
+ assert(uwacErrorHandler(window->display, UWAC_ERROR_NOMEMORY,
+ "failed to allocate a configure event\n"));
+ return;
+ }
+
+ event->window = window;
+ event->states = surfaceState;
+
+ if ((width > 0 && height > 0) && (width != window->width || height != window->height))
+ {
+ event->width = width;
+ event->height = height;
+ UwacWindowDestroyBuffers(window);
+ window->width = width;
+ window->stride = width * bppFromShmFormat(window->format);
+ window->height = height;
+ ret = UwacWindowShmAllocBuffers(window, UWAC_INITIAL_BUFFERS, window->stride * height,
+ width, height, window->format);
+
+ if (ret != UWAC_SUCCESS)
+ {
+ assert(
+ uwacErrorHandler(window->display, ret, "failed to reallocate a wayland buffers\n"));
+ window->drawingBufferIdx = window->pendingBufferIdx = -1;
+ return;
+ }
+
+ window->drawingBufferIdx = 0;
+ if (window->pendingBufferIdx != -1)
+ window->pendingBufferIdx = window->drawingBufferIdx;
+
+ if (window->viewport)
+ {
+ wp_viewport_set_source(window->viewport, wl_fixed_from_int(0), wl_fixed_from_int(0),
+ wl_fixed_from_int(window->width * scale),
+ wl_fixed_from_int(window->height * scale));
+ wp_viewport_set_destination(window->viewport, window->width * scale,
+ window->height * scale);
+ }
+ }
+ else
+ {
+ event->width = window->width;
+ event->height = window->height;
+ }
+}
+
+static void xdg_handle_toplevel_close(void* data, struct xdg_toplevel* xdg_toplevel)
+{
+ UwacCloseEvent* event = NULL;
+ UwacWindow* window = (UwacWindow*)data;
+ event = (UwacCloseEvent*)UwacDisplayNewEvent(window->display, UWAC_EVENT_CLOSE);
+
+ if (!event)
+ {
+ assert(uwacErrorHandler(window->display, UWAC_ERROR_INTERNAL,
+ "failed to allocate a close event\n"));
+ return;
+ }
+
+ event->window = window;
+}
+
+static const struct xdg_toplevel_listener xdg_toplevel_listener = {
+ xdg_handle_toplevel_configure,
+ xdg_handle_toplevel_close,
+};
+
+static void xdg_handle_surface_configure(void* data, struct xdg_surface* xdg_surface,
+ uint32_t serial)
+{
+ xdg_surface_ack_configure(xdg_surface, serial);
+}
+
+static const struct xdg_surface_listener xdg_surface_listener = {
+ .configure = xdg_handle_surface_configure,
+};
+
+#if BUILD_IVI
+
+static void ivi_handle_configure(void* data, struct ivi_surface* surface, int32_t width,
+ int32_t height)
+{
+ UwacWindow* window = (UwacWindow*)data;
+ UwacConfigureEvent* event = NULL;
+ int ret = 0;
+ event = (UwacConfigureEvent*)UwacDisplayNewEvent(window->display, UWAC_EVENT_CONFIGURE);
+
+ if (!event)
+ {
+ assert(uwacErrorHandler(window->display, UWAC_ERROR_NOMEMORY,
+ "failed to allocate a configure event\n"));
+ return;
+ }
+
+ event->window = window;
+ event->states = 0;
+
+ if (width && height)
+ {
+ event->width = width;
+ event->height = height;
+ UwacWindowDestroyBuffers(window);
+ window->width = width;
+ window->stride = width * bppFromShmFormat(window->format);
+ window->height = height;
+ ret = UwacWindowShmAllocBuffers(window, UWAC_INITIAL_BUFFERS, window->stride * height,
+ width, height, window->format);
+
+ if (ret != UWAC_SUCCESS)
+ {
+ assert(
+ uwacErrorHandler(window->display, ret, "failed to reallocate a wayland buffers\n"));
+ window->drawingBufferIdx = window->pendingBufferIdx = -1;
+ return;
+ }
+
+ window->drawingBufferIdx = 0;
+ if (window->pendingBufferIdx != -1)
+ window->pendingBufferIdx = window->drawingBufferIdx;
+ }
+ else
+ {
+ event->width = window->width;
+ event->height = window->height;
+ }
+}
+
+static const struct ivi_surface_listener ivi_surface_listener = {
+ ivi_handle_configure,
+};
+#endif
+
+static void shell_ping(void* data, struct wl_shell_surface* surface, uint32_t serial)
+{
+ wl_shell_surface_pong(surface, serial);
+}
+
+static void shell_configure(void* data, struct wl_shell_surface* surface, uint32_t edges,
+ int32_t width, int32_t height)
+{
+ UwacWindow* window = (UwacWindow*)data;
+ UwacConfigureEvent* event = NULL;
+ int ret = 0;
+ event = (UwacConfigureEvent*)UwacDisplayNewEvent(window->display, UWAC_EVENT_CONFIGURE);
+
+ if (!event)
+ {
+ assert(uwacErrorHandler(window->display, UWAC_ERROR_NOMEMORY,
+ "failed to allocate a configure event\n"));
+ return;
+ }
+
+ event->window = window;
+ event->states = 0;
+
+ if (width && height)
+ {
+ event->width = width;
+ event->height = height;
+ UwacWindowDestroyBuffers(window);
+ window->width = width;
+ window->stride = width * bppFromShmFormat(window->format);
+ window->height = height;
+ ret = UwacWindowShmAllocBuffers(window, UWAC_INITIAL_BUFFERS, window->stride * height,
+ width, height, window->format);
+
+ if (ret != UWAC_SUCCESS)
+ {
+ assert(
+ uwacErrorHandler(window->display, ret, "failed to reallocate a wayland buffers\n"));
+ window->drawingBufferIdx = window->pendingBufferIdx = -1;
+ return;
+ }
+
+ window->drawingBufferIdx = 0;
+ if (window->pendingBufferIdx != -1)
+ window->pendingBufferIdx = window->drawingBufferIdx;
+ }
+ else
+ {
+ event->width = window->width;
+ event->height = window->height;
+ }
+}
+
+static void shell_popup_done(void* data, struct wl_shell_surface* surface)
+{
+}
+
+static const struct wl_shell_surface_listener shell_listener = { shell_ping, shell_configure,
+ shell_popup_done };
+
+int UwacWindowShmAllocBuffers(UwacWindow* w, int nbuffers, int allocSize, uint32_t width,
+ uint32_t height, enum wl_shm_format format)
+{
+ int ret = UWAC_SUCCESS;
+ UwacBuffer* newBuffers = NULL;
+ int fd = 0;
+ void* data = NULL;
+ struct wl_shm_pool* pool = NULL;
+ size_t pagesize = sysconf(_SC_PAGESIZE);
+ newBuffers = xrealloc(w->buffers, (w->nbuffers + nbuffers) * sizeof(UwacBuffer));
+
+ if (!newBuffers)
+ return UWAC_ERROR_NOMEMORY;
+
+ /* round up to a multiple of PAGESIZE to page align data for each buffer */
+ allocSize = (allocSize + pagesize - 1) & ~(pagesize - 1);
+
+ w->buffers = newBuffers;
+ memset(w->buffers + w->nbuffers, 0, sizeof(UwacBuffer) * nbuffers);
+ fd = uwac_create_anonymous_file(1ull * allocSize * nbuffers);
+
+ if (fd < 0)
+ {
+ return UWAC_ERROR_INTERNAL;
+ }
+
+ data = mmap(NULL, 1ull * allocSize * nbuffers, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+
+ if (data == MAP_FAILED)
+ {
+ ret = UWAC_ERROR_NOMEMORY;
+ goto error_mmap;
+ }
+
+ pool = wl_shm_create_pool(w->display->shm, fd, allocSize * nbuffers);
+
+ if (!pool)
+ {
+ munmap(data, 1ull * allocSize * nbuffers);
+ ret = UWAC_ERROR_NOMEMORY;
+ goto error_mmap;
+ }
+
+ for (int i = 0; i < nbuffers; i++)
+ {
+ int bufferIdx = w->nbuffers + i;
+ UwacBuffer* buffer = &w->buffers[bufferIdx];
+#ifdef UWAC_HAVE_PIXMAN_REGION
+ pixman_region32_init(&buffer->damage);
+#else
+ region16_init(&buffer->damage);
+#endif
+ buffer->data = &((char*)data)[allocSize * i];
+ buffer->size = allocSize;
+ buffer->wayland_buffer =
+ wl_shm_pool_create_buffer(pool, allocSize * i, width, height, w->stride, format);
+ UwacBufferReleaseData* listener_data = xmalloc(sizeof(UwacBufferReleaseData));
+ listener_data->window = w;
+ listener_data->bufferIdx = bufferIdx;
+ wl_buffer_add_listener(buffer->wayland_buffer, &buffer_listener, listener_data);
+ }
+
+ wl_shm_pool_destroy(pool);
+ w->nbuffers += nbuffers;
+error_mmap:
+ close(fd);
+ return ret;
+}
+
+static UwacBuffer* UwacWindowFindFreeBuffer(UwacWindow* w, ssize_t* index)
+{
+ int ret = 0;
+
+ if (index)
+ *index = -1;
+
+ size_t i = 0;
+ for (; i < w->nbuffers; i++)
+ {
+ if (!w->buffers[i].used)
+ {
+ w->buffers[i].used = true;
+ if (index)
+ *index = i;
+ return &w->buffers[i];
+ }
+ }
+
+ ret = UwacWindowShmAllocBuffers(w, 2, w->stride * w->height, w->width, w->height, w->format);
+
+ if (ret != UWAC_SUCCESS)
+ {
+ w->display->last_error = ret;
+ return NULL;
+ }
+
+ w->buffers[i].used = true;
+ if (index)
+ *index = i;
+ return &w->buffers[i];
+}
+
+static UwacReturnCode UwacWindowSetDecorations(UwacWindow* w)
+{
+ if (!w || !w->display)
+ return UWAC_ERROR_INTERNAL;
+
+ if (w->display->deco_manager)
+ {
+ w->deco = zxdg_decoration_manager_v1_get_toplevel_decoration(w->display->deco_manager,
+ w->xdg_toplevel);
+ if (!w->deco)
+ {
+ uwacErrorHandler(w->display, UWAC_NOT_FOUND,
+ "Current window manager does not allow decorating with SSD");
+ }
+ else
+ zxdg_toplevel_decoration_v1_set_mode(w->deco,
+ ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
+ }
+ else if (w->display->kde_deco_manager)
+ {
+ w->kde_deco =
+ org_kde_kwin_server_decoration_manager_create(w->display->kde_deco_manager, w->surface);
+ if (!w->kde_deco)
+ {
+ uwacErrorHandler(w->display, UWAC_NOT_FOUND,
+ "Current window manager does not allow decorating with SSD");
+ }
+ else
+ org_kde_kwin_server_decoration_request_mode(w->kde_deco,
+ ORG_KDE_KWIN_SERVER_DECORATION_MODE_SERVER);
+ }
+ return UWAC_SUCCESS;
+}
+
+UwacWindow* UwacCreateWindowShm(UwacDisplay* display, uint32_t width, uint32_t height,
+ enum wl_shm_format format)
+{
+ UwacWindow* w = NULL;
+ int allocSize = 0;
+ int ret = 0;
+
+ if (!display)
+ {
+ return NULL;
+ }
+
+ w = xzalloc(sizeof(*w));
+
+ if (!w)
+ {
+ display->last_error = UWAC_ERROR_NOMEMORY;
+ return NULL;
+ }
+
+ w->display = display;
+ w->format = format;
+ w->width = width;
+ w->height = height;
+ w->stride = width * bppFromShmFormat(format);
+ allocSize = w->stride * height;
+ ret = UwacWindowShmAllocBuffers(w, UWAC_INITIAL_BUFFERS, allocSize, width, height, format);
+
+ if (ret != UWAC_SUCCESS)
+ {
+ display->last_error = ret;
+ goto out_error_free;
+ }
+
+ w->buffers[0].used = true;
+ w->drawingBufferIdx = 0;
+ w->pendingBufferIdx = -1;
+ w->surface = wl_compositor_create_surface(display->compositor);
+
+ if (!w->surface)
+ {
+ display->last_error = UWAC_ERROR_NOMEMORY;
+ goto out_error_surface;
+ }
+
+ wl_surface_set_user_data(w->surface, w);
+
+#if BUILD_IVI
+ uint32_t ivi_surface_id = 1;
+ char* env = getenv("IVI_SURFACE_ID");
+ if (env)
+ {
+ unsigned long val = 0;
+ char* endp = NULL;
+
+ errno = 0;
+ val = strtoul(env, &endp, 10);
+
+ if (!errno && val != 0 && val != UINT32_MAX)
+ ivi_surface_id = val;
+ }
+
+ if (display->ivi_application)
+ {
+ w->ivi_surface =
+ ivi_application_surface_create(display->ivi_application, ivi_surface_id, w->surface);
+ assert(w->ivi_surface);
+ ivi_surface_add_listener(w->ivi_surface, &ivi_surface_listener, w);
+ }
+ else
+#endif
+#if BUILD_FULLSCREEN_SHELL
+ if (display->fullscreen_shell)
+ {
+ zwp_fullscreen_shell_v1_present_surface(display->fullscreen_shell, w->surface,
+ ZWP_FULLSCREEN_SHELL_V1_PRESENT_METHOD_CENTER,
+ NULL);
+ }
+ else
+#endif
+ if (display->xdg_base)
+ {
+ w->xdg_surface = xdg_wm_base_get_xdg_surface(display->xdg_base, w->surface);
+
+ if (!w->xdg_surface)
+ {
+ display->last_error = UWAC_ERROR_NOMEMORY;
+ goto out_error_shell;
+ }
+
+ xdg_surface_add_listener(w->xdg_surface, &xdg_surface_listener, w);
+
+ w->xdg_toplevel = xdg_surface_get_toplevel(w->xdg_surface);
+ if (!w->xdg_toplevel)
+ {
+ display->last_error = UWAC_ERROR_NOMEMORY;
+ goto out_error_shell;
+ }
+
+ assert(w->xdg_surface);
+ xdg_toplevel_add_listener(w->xdg_toplevel, &xdg_toplevel_listener, w);
+ wl_surface_commit(w->surface);
+ wl_display_roundtrip(w->display->display);
+ }
+ else
+ {
+ w->shell_surface = wl_shell_get_shell_surface(display->shell, w->surface);
+ assert(w->shell_surface);
+ wl_shell_surface_add_listener(w->shell_surface, &shell_listener, w);
+ wl_shell_surface_set_toplevel(w->shell_surface);
+ }
+
+ if (display->viewporter)
+ {
+ w->viewport = wp_viewporter_get_viewport(display->viewporter, w->surface);
+ if (display->actual_scale != 1)
+ wl_surface_set_buffer_scale(w->surface, display->actual_scale);
+ }
+
+ wl_list_insert(display->windows.prev, &w->link);
+ display->last_error = UWAC_SUCCESS;
+ UwacWindowSetDecorations(w);
+ return w;
+out_error_shell:
+ wl_surface_destroy(w->surface);
+out_error_surface:
+ UwacWindowDestroyBuffers(w);
+out_error_free:
+ free(w);
+ return NULL;
+}
+
+UwacReturnCode UwacDestroyWindow(UwacWindow** pwindow)
+{
+ UwacWindow* w = NULL;
+ assert(pwindow);
+ w = *pwindow;
+ UwacWindowDestroyBuffers(w);
+
+ if (w->deco)
+ zxdg_toplevel_decoration_v1_destroy(w->deco);
+
+ if (w->kde_deco)
+ org_kde_kwin_server_decoration_destroy(w->kde_deco);
+
+ if (w->xdg_surface)
+ xdg_surface_destroy(w->xdg_surface);
+
+#if BUILD_IVI
+
+ if (w->ivi_surface)
+ ivi_surface_destroy(w->ivi_surface);
+
+#endif
+
+ if (w->opaque_region)
+ wl_region_destroy(w->opaque_region);
+
+ if (w->input_region)
+ wl_region_destroy(w->input_region);
+
+ if (w->viewport)
+ wp_viewport_destroy(w->viewport);
+
+ wl_surface_destroy(w->surface);
+ wl_list_remove(&w->link);
+ free(w);
+ *pwindow = NULL;
+ return UWAC_SUCCESS;
+}
+
+UwacReturnCode UwacWindowSetOpaqueRegion(UwacWindow* window, uint32_t x, uint32_t y, uint32_t width,
+ uint32_t height)
+{
+ assert(window);
+
+ if (window->opaque_region)
+ wl_region_destroy(window->opaque_region);
+
+ window->opaque_region = wl_compositor_create_region(window->display->compositor);
+
+ if (!window->opaque_region)
+ return UWAC_ERROR_NOMEMORY;
+
+ wl_region_add(window->opaque_region, x, y, width, height);
+ wl_surface_set_opaque_region(window->surface, window->opaque_region);
+ return UWAC_SUCCESS;
+}
+
+UwacReturnCode UwacWindowSetInputRegion(UwacWindow* window, uint32_t x, uint32_t y, uint32_t width,
+ uint32_t height)
+{
+ assert(window);
+
+ if (window->input_region)
+ wl_region_destroy(window->input_region);
+
+ window->input_region = wl_compositor_create_region(window->display->compositor);
+
+ if (!window->input_region)
+ return UWAC_ERROR_NOMEMORY;
+
+ wl_region_add(window->input_region, x, y, width, height);
+ wl_surface_set_input_region(window->surface, window->input_region);
+ return UWAC_SUCCESS;
+}
+
+void* UwacWindowGetDrawingBuffer(UwacWindow* window)
+{
+ UwacBuffer* buffer = NULL;
+
+ if (window->drawingBufferIdx < 0)
+ return NULL;
+
+ buffer = &window->buffers[window->drawingBufferIdx];
+ if (!buffer)
+ return NULL;
+
+ return buffer->data;
+}
+
+static void frame_done_cb(void* data, struct wl_callback* callback, uint32_t time);
+
+static const struct wl_callback_listener frame_listener = { frame_done_cb };
+
+#ifdef UWAC_HAVE_PIXMAN_REGION
+static void damage_surface(UwacWindow* window, UwacBuffer* buffer, int scale)
+{
+ int nrects = 0;
+ const pixman_box32_t* box = pixman_region32_rectangles(&buffer->damage, &nrects);
+
+ for (int i = 0; i < nrects; i++, box++)
+ {
+ const int x = ((int)floor(box->x1 / scale)) - 1;
+ const int y = ((int)floor(box->y1 / scale)) - 1;
+ const int w = ((int)ceil((box->x2 - box->x1) / scale)) + 2;
+ const int h = ((int)ceil((box->y2 - box->y1) / scale)) + 2;
+ wl_surface_damage(window->surface, x, y, w, h);
+ }
+
+ pixman_region32_clear(&buffer->damage);
+}
+#else
+static void damage_surface(UwacWindow* window, UwacBuffer* buffer, int scale)
+{
+ uint32_t nrects = 0;
+ const RECTANGLE_16* box = region16_rects(&buffer->damage, &nrects);
+
+ for (UINT32 i = 0; i < nrects; i++, box++)
+ {
+ const int x = ((int)floor(box->left / scale)) - 1;
+ const int y = ((int)floor(box->top / scale)) - 1;
+ const int w = ((int)ceil((box->right - box->left) / scale)) + 2;
+ const int h = ((int)ceil((box->bottom - box->top) / scale)) + 2;
+ wl_surface_damage(window->surface, x, y, w, h);
+ }
+
+ region16_clear(&buffer->damage);
+}
+#endif
+
+static void UwacSubmitBufferPtr(UwacWindow* window, UwacBuffer* buffer)
+{
+ wl_surface_attach(window->surface, buffer->wayland_buffer, 0, 0);
+
+ int scale = window->display->actual_scale;
+ damage_surface(window, buffer, scale);
+
+ struct wl_callback* frame_callback = wl_surface_frame(window->surface);
+ wl_callback_add_listener(frame_callback, &frame_listener, window);
+ wl_surface_commit(window->surface);
+ buffer->dirty = false;
+}
+
+static void frame_done_cb(void* data, struct wl_callback* callback, uint32_t time)
+{
+ UwacWindow* window = (UwacWindow*)data;
+ UwacFrameDoneEvent* event = NULL;
+
+ wl_callback_destroy(callback);
+ window->pendingBufferIdx = -1;
+ event = (UwacFrameDoneEvent*)UwacDisplayNewEvent(window->display, UWAC_EVENT_FRAME_DONE);
+
+ if (event)
+ event->window = window;
+}
+
+#ifdef UWAC_HAVE_PIXMAN_REGION
+UwacReturnCode UwacWindowAddDamage(UwacWindow* window, uint32_t x, uint32_t y, uint32_t width,
+ uint32_t height)
+{
+ UwacBuffer* buf = NULL;
+
+ if (window->drawingBufferIdx < 0)
+ return UWAC_ERROR_INTERNAL;
+
+ buf = &window->buffers[window->drawingBufferIdx];
+ if (!pixman_region32_union_rect(&buf->damage, &buf->damage, x, y, width, height))
+ return UWAC_ERROR_INTERNAL;
+
+ buf->dirty = true;
+ return UWAC_SUCCESS;
+}
+#else
+UwacReturnCode UwacWindowAddDamage(UwacWindow* window, uint32_t x, uint32_t y, uint32_t width,
+ uint32_t height)
+{
+ RECTANGLE_16 box;
+ UwacBuffer* buf = NULL;
+
+ box.left = x;
+ box.top = y;
+ box.right = x + width;
+ box.bottom = y + height;
+
+ if (window->drawingBufferIdx < 0)
+ return UWAC_ERROR_INTERNAL;
+
+ buf = &window->buffers[window->drawingBufferIdx];
+ if (!buf)
+ return UWAC_ERROR_INTERNAL;
+
+ if (!region16_union_rect(&buf->damage, &buf->damage, &box))
+ return UWAC_ERROR_INTERNAL;
+
+ buf->dirty = true;
+ return UWAC_SUCCESS;
+}
+#endif
+
+UwacReturnCode UwacWindowGetDrawingBufferGeometry(UwacWindow* window, UwacSize* geometry,
+ size_t* stride)
+{
+ if (!window || (window->drawingBufferIdx < 0))
+ return UWAC_ERROR_INTERNAL;
+
+ if (geometry)
+ {
+ geometry->width = window->width;
+ geometry->height = window->height;
+ }
+
+ if (stride)
+ *stride = window->stride;
+
+ return UWAC_SUCCESS;
+}
+
+UwacReturnCode UwacWindowSubmitBuffer(UwacWindow* window, bool copyContentForNextFrame)
+{
+ UwacBuffer* currentDrawingBuffer = NULL;
+ UwacBuffer* nextDrawingBuffer = NULL;
+ UwacBuffer* pendingBuffer = NULL;
+
+ if (window->drawingBufferIdx < 0)
+ return UWAC_ERROR_INTERNAL;
+
+ currentDrawingBuffer = &window->buffers[window->drawingBufferIdx];
+
+ if ((window->pendingBufferIdx >= 0) || !currentDrawingBuffer->dirty)
+ return UWAC_SUCCESS;
+
+ window->pendingBufferIdx = window->drawingBufferIdx;
+ nextDrawingBuffer = UwacWindowFindFreeBuffer(window, &window->drawingBufferIdx);
+ pendingBuffer = &window->buffers[window->pendingBufferIdx];
+
+ if ((!nextDrawingBuffer) || (window->drawingBufferIdx < 0))
+ return UWAC_ERROR_NOMEMORY;
+
+ if (copyContentForNextFrame)
+ memcpy(nextDrawingBuffer->data, pendingBuffer->data,
+ 1ull * window->stride * window->height);
+
+ UwacSubmitBufferPtr(window, pendingBuffer);
+ return UWAC_SUCCESS;
+}
+
+UwacReturnCode UwacWindowGetGeometry(UwacWindow* window, UwacSize* geometry)
+{
+ assert(window);
+ assert(geometry);
+ geometry->width = window->width;
+ geometry->height = window->height;
+ return UWAC_SUCCESS;
+}
+
+UwacReturnCode UwacWindowSetFullscreenState(UwacWindow* window, UwacOutput* output,
+ bool isFullscreen)
+{
+ if (window->xdg_toplevel)
+ {
+ if (isFullscreen)
+ {
+ xdg_toplevel_set_fullscreen(window->xdg_toplevel, output ? output->output : NULL);
+ }
+ else
+ {
+ xdg_toplevel_unset_fullscreen(window->xdg_toplevel);
+ }
+ }
+ else if (window->shell_surface)
+ {
+ if (isFullscreen)
+ {
+ wl_shell_surface_set_fullscreen(window->shell_surface,
+ WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT, 0,
+ output ? output->output : NULL);
+ }
+ else
+ {
+ wl_shell_surface_set_toplevel(window->shell_surface);
+ }
+ }
+
+ return UWAC_SUCCESS;
+}
+
+void UwacWindowSetTitle(UwacWindow* window, const char* name)
+{
+ if (window->xdg_toplevel)
+ xdg_toplevel_set_title(window->xdg_toplevel, name);
+ else if (window->shell_surface)
+ wl_shell_surface_set_title(window->shell_surface, name);
+}
+
+void UwacWindowSetAppId(UwacWindow* window, const char* app_id)
+{
+ if (window->xdg_toplevel)
+ xdg_toplevel_set_app_id(window->xdg_toplevel, app_id);
+}