diff --git a/glfw/wl_client_side_decorations.c b/glfw/wl_client_side_decorations.c index 02301a8fc..14d6549f8 100644 --- a/glfw/wl_client_side_decorations.c +++ b/glfw/wl_client_side_decorations.c @@ -13,166 +13,168 @@ #include #define decs window->wl.decorations -#define tb decs.title_bar -#define eb decs.edges #define ARGB(a, r, g, b) (((a) << 24) | ((r) << 16) | ((g) << 8) | (b)) static const uint32_t bg_color = 0xfffefefe; -static void -free_title_bar_resources(_GLFWwindow *window) { - if (tb.front_buffer) { - wl_buffer_destroy(tb.front_buffer); - tb.front_buffer = NULL; - } - if (tb.back_buffer) { - wl_buffer_destroy(tb.back_buffer); - tb.back_buffer = NULL; - } - if (tb.data) { - munmap(tb.data, tb.buffer_sz * 2); - tb.data = NULL; - } +static size_t +init_buffer_pair(_GLFWWaylandBufferPair *pair, size_t width, size_t height, unsigned scale) { + memset(pair, 0, sizeof(_GLFWWaylandBufferPair)); + pair->width = width * scale; + pair->height = height * scale; + pair->stride = 4 * pair->width; + pair->size_in_bytes = pair->stride * pair->height; + return 2 * pair->size_in_bytes; } static void -free_edge_resources(_GLFWwindow *window) { - if (eb.left) { wl_buffer_destroy(eb.left); eb.left = NULL; } - if (eb.right) { wl_buffer_destroy(eb.right); eb.right = NULL; } - if (eb.bottom) { wl_buffer_destroy(eb.bottom); eb.bottom = NULL; } +alloc_buffer_pair(_GLFWWaylandBufferPair *pair, struct wl_shm_pool *pool, uint8_t *data, size_t *offset) { + pair->data.a = data + *offset; + pair->a = wl_shm_pool_create_buffer(pool, *offset, pair->width, pair->height, pair->stride, WL_SHM_FORMAT_ARGB8888); + *offset += pair->size_in_bytes; + pair->data.b = data + *offset; + pair->b = wl_shm_pool_create_buffer(pool, *offset, pair->width, pair->height, pair->stride, WL_SHM_FORMAT_ARGB8888); + *offset += pair->size_in_bytes; + pair->front = pair->a; pair->back = pair->b; + pair->data.front = pair->data.a; pair->data.back = pair->data.b; + pair->back_buffer_is_safe = true; +} + +static void +render_title_bar(_GLFWwindow *window, bool to_front_buffer) { + uint8_t *output = to_front_buffer ? decs.top.buffer.data.front : decs.top.buffer.data.back; + for (uint32_t *px = (uint32_t*)output, *end = (uint32_t*)(output + decs.top.buffer.size_in_bytes); px < end; px++) { + *px = bg_color; + } +} + + +static void +render_edge(_GLFWWaylandBufferPair *pair) { + for (uint32_t *px = (uint32_t*)pair->data.front, *end = (uint32_t*)(pair->data.front + pair->size_in_bytes); px < end; px++) { + *px = bg_color; + } } static bool -create_shm_buffers_for_title_bar(_GLFWwindow* window) { - free_title_bar_resources(window); - int scale = window->wl.scale; - if (scale < 1) scale = 1; - size_t width = window->wl.width * scale, height = decs.metrics.top * scale; - const size_t stride = 4 * width; - tb.buffer_sz = stride * height; - const size_t mapping_sz = tb.buffer_sz * 2; - - int fd = createAnonymousFile(mapping_sz); - if (fd < 0) { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Creating a buffer file for %zu B failed: %s", - mapping_sz, strerror(errno)); - return false; - } - tb.data = mmap(NULL, mapping_sz, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - if (tb.data == MAP_FAILED) { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: mmap failed: %s", strerror(errno)); - close(fd); - return false; - } - for (uint32_t *px = (uint32_t*)tb.data, *end = (uint32_t*)(tb.data + tb.buffer_sz); px < end; px++) *px = bg_color; - struct wl_shm_pool* pool = wl_shm_create_pool(_glfw.wl.shm, fd, mapping_sz); - close(fd); -#define c(offset) wl_shm_pool_create_buffer( \ - pool, offset, width, height, stride, WL_SHM_FORMAT_ARGB8888); - tb.front_buffer = c(0); tb.back_buffer = c(tb.buffer_sz); -#undef c - wl_shm_pool_destroy(pool); - return true; -} - -static void -render_left_edge(uint8_t *data, size_t width, size_t height, bool is_focused) { - if (is_focused) for (uint32_t *px = (uint32_t*)data, *end = (uint32_t*)(data + 4 * width * height); px < end; px++) *px = bg_color; - else memset(data, 0, 4 * width * height); -} -#define render_right_edge render_left_edge -#define render_bottom_edge render_left_edge - -static bool -create_shm_buffers_for_edges(_GLFWwindow* window, bool is_focused) { - free_edge_resources(window); +create_shm_buffers(_GLFWwindow* window) { int scale = window->wl.scale; if (scale < 1) scale = 1; - size_t vertical_width = decs.metrics.width, vertical_height = window->wl.height + decs.metrics.top; - size_t horizontal_height = decs.metrics.width, horizontal_width = window->wl.width + 2 * decs.metrics.width; - vertical_width *= scale; vertical_height *= scale; - horizontal_width *= scale; horizontal_height *= scale; - const size_t mapping_sz = 4 * (2 * vertical_width * vertical_height + horizontal_height * horizontal_width); + const size_t vertical_width = decs.metrics.width, vertical_height = window->wl.height + decs.metrics.top; + const size_t horizontal_height = decs.metrics.width, horizontal_width = window->wl.width + 2 * decs.metrics.width; - int fd = createAnonymousFile(mapping_sz); + decs.mapping.size = 0; + decs.mapping.size += init_buffer_pair(&decs.top.buffer, window->wl.width, decs.metrics.top, scale); + decs.mapping.size += init_buffer_pair(&decs.left.buffer, vertical_width, vertical_height, scale); + decs.mapping.size += init_buffer_pair(&decs.bottom.buffer, horizontal_width, horizontal_height, scale); + decs.mapping.size += init_buffer_pair(&decs.right.buffer, vertical_width, vertical_height, scale); + + int fd = createAnonymousFile(decs.mapping.size); if (fd < 0) { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Creating a buffer file for %zu B failed: %s", - mapping_sz, strerror(errno)); + _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Creating a buffer file for %zu B failed: %s", + decs.mapping.size, strerror(errno)); return false; } - uint8_t *data = mmap(NULL, mapping_sz, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - if (data == MAP_FAILED) { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: mmap failed: %s", strerror(errno)); + decs.mapping.data = mmap(NULL, decs.mapping.size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (decs.mapping.data == MAP_FAILED) { + _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: mmap failed: %s", strerror(errno)); close(fd); return false; } - render_left_edge(data, vertical_width, vertical_height, is_focused); - render_right_edge(data + 4 * vertical_width * vertical_height, vertical_width, vertical_height, is_focused); - render_bottom_edge(data + 8 * vertical_width * vertical_height, horizontal_width, horizontal_height, is_focused); - struct wl_shm_pool* pool = wl_shm_create_pool(_glfw.wl.shm, fd, mapping_sz); + struct wl_shm_pool* pool = wl_shm_create_pool(_glfw.wl.shm, fd, decs.mapping.size); close(fd); - eb.left = wl_shm_pool_create_buffer( - pool, 0, vertical_width, vertical_height, vertical_width * 4, WL_SHM_FORMAT_ARGB8888); - eb.right = wl_shm_pool_create_buffer( - pool, 4 * vertical_width * vertical_height, vertical_width, vertical_height, vertical_width * 4, WL_SHM_FORMAT_ARGB8888); - eb.bottom = wl_shm_pool_create_buffer( - pool, 8 * vertical_width * vertical_height, horizontal_width, horizontal_height, horizontal_width * 4, WL_SHM_FORMAT_ARGB8888); + size_t offset = 0; +#define a(which) alloc_buffer_pair(&decs.which.buffer, pool, decs.mapping.data, &offset) + a(top); a(left); a(bottom); a(right); +#undef a wl_shm_pool_destroy(pool); + render_title_bar(window, true); + render_edge(&decs.left.buffer); render_edge(&decs.bottom.buffer); render_edge(&decs.right.buffer); return true; } void free_csd_surfaces(_GLFWwindow *window) { #define d(which) {\ - if (decs.subsurfaces.which) wl_subsurface_destroy(decs.subsurfaces.which); decs.subsurfaces.which = NULL; \ - if (decs.surfaces.which) wl_surface_destroy(decs.surfaces.which); decs.surfaces.which = NULL; \ + if (decs.which.subsurface) wl_subsurface_destroy(decs.which.subsurface); decs.which.subsurface = NULL; \ + if (decs.which.surface) wl_surface_destroy(decs.which.surface); decs.which.surface = NULL; \ } d(left); d(top); d(right); d(bottom); #undef d } -#define create_decoration_surfaces(which, buffer, x, y) { \ - decs.surfaces.which = wl_compositor_create_surface(_glfw.wl.compositor); \ - wl_surface_set_buffer_scale(decs.surfaces.which, window->wl.scale < 1 ? 1 : window->wl.scale); \ - decs.subsurfaces.which = wl_subcompositor_get_subsurface(_glfw.wl.subcompositor, decs.surfaces.which, window->wl.surface); \ - wl_surface_attach(decs.surfaces.which, buffer, 0, 0); \ - wl_subsurface_set_position(decs.subsurfaces.which, x, y); \ - wl_surface_commit(decs.surfaces.which); \ +static void +free_csd_buffers(_GLFWwindow *window) { +#define d(which) { \ + if (decs.which.buffer.a) wl_buffer_destroy(decs.which.buffer.a); \ + if (decs.which.buffer.b) wl_buffer_destroy(decs.which.buffer.b); \ + memset(&decs.which.buffer, 0, sizeof(_GLFWWaylandBufferPair)); \ +} + d(left); d(top); d(right); d(bottom); +#undef d + if (decs.mapping.data) munmap(decs.mapping.data, decs.mapping.size); + decs.mapping.data = NULL; decs.mapping.size = 0; +} + +static void +position_csd_surface(_GLFWWaylandCSDEdge *s, int x, int y, int scale) { + wl_surface_set_buffer_scale(s->surface, scale); + s->x = x; s->y = y; + wl_subsurface_set_position(s->subsurface, s->x, s->y); +} + +static void +create_csd_surfaces(_GLFWwindow *window, _GLFWWaylandCSDEdge *s) { + s->surface = wl_compositor_create_surface(_glfw.wl.compositor); + s->subsurface = wl_subcompositor_get_subsurface(_glfw.wl.subcompositor, s->surface, window->wl.surface); } bool ensure_csd_resources(_GLFWwindow *window) { const bool is_focused = window->id == _glfw.focusedWindowId; - const bool state_changed = ( + const bool focus_changed = is_focused != decs.for_window_state.focused; + const bool size_changed = ( decs.for_window_state.width != window->wl.width || decs.for_window_state.height != window->wl.height || decs.for_window_state.scale != window->wl.scale || - decs.for_window_state.focused != is_focused + !decs.mapping.data ); - if (state_changed) free_all_csd_resources(window); - else if (decs.edges.left && decs.title_bar.front_buffer) return true; + const bool needs_update = focus_changed || size_changed || !decs.left.surface; + if (!needs_update) return true; + if (size_changed) { + free_csd_buffers(window); + if (!create_shm_buffers(window)) return false; + } - if (!create_shm_buffers_for_edges(window, is_focused)) return false; - if (!create_shm_buffers_for_title_bar(window)) return false; - - int x, y; + int x, y, scale = window->wl.scale < 1 ? 1 : window->wl.scale; x = 0; y = -decs.metrics.top; - create_decoration_surfaces(top, decs.title_bar.front_buffer, x, y); + if (!decs.top.surface) create_csd_surfaces(window, &decs.top); + position_csd_surface(&decs.top, x, y, scale); x = -decs.metrics.width; y = -decs.metrics.top; - create_decoration_surfaces(left, decs.edges.left, x, y); - - x = window->wl.width; y = -decs.metrics.top; - create_decoration_surfaces(right, decs.edges.right, x, y); + if (!decs.left.surface) create_csd_surfaces(window, &decs.left); + position_csd_surface(&decs.left, x, y, scale); x = -decs.metrics.width; y = window->wl.height; - create_decoration_surfaces(bottom, decs.edges.bottom, x, y); + if (!decs.bottom.surface) create_csd_surfaces(window, &decs.bottom); + position_csd_surface(&decs.bottom, x, y, scale); + + x = window->wl.width; y = -decs.metrics.top; + if (!decs.right.surface) create_csd_surfaces(window, &decs.right); + position_csd_surface(&decs.right, x, y, scale); + +#define c(which, xbuffer) \ + wl_surface_attach(decs.which.surface, xbuffer, 0, 0); \ + wl_surface_damage(decs.which.surface, 0, 0, decs.which.buffer.width, decs.which.buffer.height); \ + wl_surface_commit(decs.which.surface) + + c(top, decs.top.buffer.front); + c(left, is_focused ? decs.left.buffer.front : decs.left.buffer.back); + c(bottom, is_focused ? decs.bottom.buffer.front : decs.bottom.buffer.back); + c(right, is_focused ? decs.right.buffer.front : decs.right.buffer.back); +#undef c decs.for_window_state.width = window->wl.width; decs.for_window_state.height = window->wl.height; @@ -184,8 +186,7 @@ ensure_csd_resources(_GLFWwindow *window) { void free_all_csd_resources(_GLFWwindow *window) { free_csd_surfaces(window); - free_title_bar_resources(window); - free_edge_resources(window); + free_csd_buffers(window); } void diff --git a/glfw/wl_init.c b/glfw/wl_init.c index f9c0ad6f9..80f2505a7 100644 --- a/glfw/wl_init.c +++ b/glfw/wl_init.c @@ -66,7 +66,7 @@ findWindowFromDecorationSurface(struct wl_surface* surface, _GLFWdecorationSideW _GLFWdecorationSideWayland focus; if (!which) which = &focus; _GLFWwindow* window = _glfw.windowListHead; -#define q(edge, result) if (surface == window->wl.decorations.surfaces.edge) { *which = result; break; } +#define q(edge, result) if (surface == window->wl.decorations.edge.surface) { *which = result; break; } while (window) { q(top, TOP_DECORATION); q(left, LEFT_DECORATION); diff --git a/glfw/wl_platform.h b/glfw/wl_platform.h index 518e2011f..5a4dbef09 100644 --- a/glfw/wl_platform.h +++ b/glfw/wl_platform.h @@ -97,6 +97,20 @@ typedef enum _GLFWdecorationSideWayland BOTTOM_DECORATION, } _GLFWdecorationSideWayland; +typedef struct _GLFWWaylandBufferPair { + struct wl_buffer *a, *b, *front, *back; + struct { uint8_t *a, *b, *front, *back; } data; + bool back_buffer_is_safe, has_pending_update; + size_t size_in_bytes, width, height, stride; +} _GLFWWaylandBufferPair; + +typedef struct _GLFWWaylandCSDEdge { + struct wl_surface *surface; + struct wl_subsurface *subsurface; + _GLFWWaylandBufferPair buffer; + int x, y; +} _GLFWWaylandCSDEdge; + // Wayland-specific per-window data // @@ -143,24 +157,12 @@ typedef struct _GLFWwindowWayland struct { bool serverSide; _GLFWdecorationSideWayland focus; + _GLFWWaylandCSDEdge top, left, right, bottom; struct { - struct wl_surface *top, *left, *right, *bottom; - } surfaces; - - struct { - struct wl_subsurface *top, *left, *right, *bottom; - } subsurfaces; - - struct { - struct wl_buffer *front_buffer, *back_buffer; uint8_t *data; - size_t buffer_sz; - } title_bar; - - struct { - struct wl_buffer *left, *right, *bottom; - } edges; + size_t size; + } mapping; struct { int width, height, scale; diff --git a/glfw/wl_window.c b/glfw/wl_window.c index c84c33547..6d7db296b 100644 --- a/glfw/wl_window.c +++ b/glfw/wl_window.c @@ -222,7 +222,7 @@ static void resizeFramebuffer(_GLFWwindow* window) setOpaqueRegion(window); _glfwInputFramebufferSize(window, scaledWidth, scaledHeight); - if (window->wl.decorations.surfaces.top) resize_csd(window); + if (window->wl.decorations.mapping.data) resize_csd(window); } @@ -453,7 +453,7 @@ static void xdgToplevelHandleConfigure(void* data, } window->wl.fullscreened = fullscreen; if (!fullscreen) { - if (window->decorated && !window->wl.decorations.serverSide && window->wl.decorations.edges.left) { + if (window->decorated && !window->wl.decorations.serverSide && window->wl.decorations.left.surface) { width -= window->wl.decorations.metrics.horizontal; height -= window->wl.decorations.metrics.vertical; }