Use individual surfaces for corner and bar shadows

Simplifies a bunch of code and also gives us the option at a later date
to turn off some shadows selectively when the window is in a tiled
state.
This commit is contained in:
Kovid Goyal 2024-04-07 22:28:41 +05:30
parent 38daac868a
commit bf60321466
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
4 changed files with 232 additions and 276 deletions

View File

@ -22,6 +22,7 @@
#define B(x) ((x) & 0xff)
#define SWAP(x, y) do { __typeof__(x) SWAP = x; x = y; y = SWAP; } while (0)
// shadow tile {{{
typedef float kernel_type;
static void
@ -83,6 +84,27 @@ create_shadow_mask(size_t width, size_t height, size_t margin, size_t kernel_siz
return mask;
}
#define st decs.shadow_tile
static size_t
create_shadow_tile(_GLFWwindow *window) {
const size_t margin = (size_t)roundf(decs.metrics.width * decs.for_window_state.fscale);
if (st.data && st.for_decoration_size == margin) return margin;
st.for_decoration_size = margin;
free(st.data);
st.segments = 7;
st.stride = st.segments * margin;
st.corner_size = margin * (st.segments - 1) / 2;
kernel_type* mask = create_shadow_mask(st.stride, st.stride, margin, 2 * margin + 1, (kernel_type)0.7, 32 * margin);
st.data = malloc(sizeof(uint32_t) * st.stride * st.stride);
if (st.data) for (size_t i = 0; i < st.stride * st.stride; i++) st.data[i] = ((uint8_t)(mask[i] * 255)) << 24;
free(mask);
return margin;
}
// }}}
static void
swap_buffers(_GLFWWaylandBufferPair *pair) {
SWAP(pair->front, pair->back);
@ -100,10 +122,14 @@ init_buffer_pair(_GLFWWaylandBufferPair *pair, size_t width, size_t height, floa
return 2 * pair->size_in_bytes;
}
#define all_shadow_surfaces(Q) Q(shadow_left); Q(shadow_top); Q(shadow_right); Q(shadow_bottom); \
Q(shadow_upper_left); Q(shadow_upper_right); Q(shadow_lower_left); Q(shadow_lower_right);
#define all_surfaces(Q) Q(titlebar); all_shadow_surfaces(Q);
static bool
window_has_buffer(_GLFWwindow *window, struct wl_buffer *q) {
#define Q(which) if (decs.which.buffer.a == q) { decs.which.buffer.a_needs_to_be_destroyed = false; return true; } if (decs.which.buffer.b == q) { decs.which.buffer.b_needs_to_be_destroyed = false; return true; }
Q(left); Q(top); Q(right); Q(bottom);
all_surfaces(Q);
#undef Q
return false;
}
@ -133,8 +159,6 @@ alloc_buffer_pair(uintptr_t window_id, _GLFWWaylandBufferPair *pair, struct wl_s
pair->data.front = pair->data.a; pair->data.back = pair->data.b;
}
#define st decs.shadow_tile
void
csd_initialize_metrics(_GLFWwindow *window) {
decs.metrics.width = 12;
@ -144,22 +168,6 @@ csd_initialize_metrics(_GLFWwindow *window) {
decs.metrics.vertical = decs.metrics.width + decs.metrics.top;
}
static size_t
create_shadow_tile(_GLFWwindow *window) {
const size_t margin = decs.bottom.buffer.height;
if (st.data && st.for_decoration_size == margin) return margin;
st.for_decoration_size = margin;
free(st.data);
st.segments = 7;
st.stride = st.segments * margin;
st.corner_size = margin * (st.segments - 1) / 2;
kernel_type* mask = create_shadow_mask(st.stride, st.stride, margin, 2 * margin + 1, (kernel_type)0.7, 32 * margin);
st.data = malloc(sizeof(uint32_t) * st.stride * st.stride);
if (st.data) for (size_t i = 0; i < st.stride * st.stride; i++) st.data[i] = ((uint8_t)(mask[i] * 255)) << 24;
free(mask);
return margin;
}
static void
render_title_bar(_GLFWwindow *window, bool to_front_buffer) {
@ -179,45 +187,26 @@ render_title_bar(_GLFWwindow *window, bool to_front_buffer) {
if (luma < 0.5) { fg_color = dark_fg; hover_bg = hover_dark_bg; is_dark = true; }
if (!decs.use_custom_titlebar_color) bg_color = luma < 0.5 ? dark_bg : light_bg;
} else if (appearance == GLFW_COLOR_SCHEME_DARK) { bg_color = dark_bg; fg_color = dark_fg; hover_bg = hover_dark_bg; is_dark = true; }
uint8_t *output = to_front_buffer ? decs.top.buffer.data.front : decs.top.buffer.data.back;
// render shadow part
const size_t margin = create_shadow_tile(window);
const size_t edge_segment_size = st.corner_size - margin;
const uint8_t divisor = is_focused ? 1 : 2;
for (size_t y = 0; y < margin; y++) {
// left segment
uint32_t *s = st.data + y * st.stride + margin;
uint32_t *d = (uint32_t*)(output + y * decs.top.buffer.stride);
for (size_t x = 0; x < edge_segment_size; x++) d[x] = (A(s[x]) / divisor) << 24;
// middle segment
s += edge_segment_size;
size_t limit = decs.top.buffer.width > edge_segment_size ? decs.top.buffer.width - edge_segment_size : 0;
for (size_t x = edge_segment_size, sx = 0; x < limit; x++, sx = (sx + 1) % margin) d[x] = (A(s[sx]) / divisor) << 24;
// right segment
s += margin;
for (size_t x = limit; x < decs.top.buffer.width; x++, s++) d[x] = (A(*s) / divisor) << 24;
}
uint8_t *output = to_front_buffer ? decs.titlebar.buffer.data.front : decs.titlebar.buffer.data.back;
// render text part
int button_size = (int)roundf(decs.metrics.visible_titlebar_height * decs.for_window_state.fscale);
int num_buttons = 1;
if (window->wl.wm_capabilities.maximize) num_buttons++;
if (window->wl.wm_capabilities.minimize) num_buttons++;
output += decs.top.buffer.stride * margin;
if (window->wl.title && window->wl.title[0] && _glfw.callbacks.draw_text) {
if (_glfw.callbacks.draw_text((GLFWwindow*)window, window->wl.title, fg_color, bg_color, output, decs.top.buffer.width, decs.top.buffer.height - margin, 0, 0, num_buttons * button_size, false)) goto render_buttons;
if (_glfw.callbacks.draw_text((GLFWwindow*)window, window->wl.title, fg_color, bg_color, output, decs.titlebar.buffer.width, decs.titlebar.buffer.height, 0, 0, num_buttons * button_size, false)) goto render_buttons;
}
// rendering of text failed, blank the buffer
for (uint32_t *px = (uint32_t*)output, *end = (uint32_t*)(output + decs.top.buffer.size_in_bytes); px < end; px++) *px = bg_color;
for (uint32_t *px = (uint32_t*)output, *end = (uint32_t*)(output + decs.titlebar.buffer.size_in_bytes); px < end; px++) *px = bg_color;
render_buttons:
decs.maximize.width = 0; decs.minimize.width = 0; decs.close.width = 0;
if (!button_size) return;
int left = decs.top.buffer.width - num_buttons * button_size;
int left = decs.titlebar.buffer.width - num_buttons * button_size;
#define draw(which, text, hover_bg) { \
_glfw.callbacks.draw_text((GLFWwindow*)window, text, fg_color, decs.which.hovered ? hover_bg : bg_color, output, decs.top.buffer.width, \
decs.top.buffer.height - margin, /*x=*/left, /*y=*/0, /*right_margin=*/decs.top.buffer.width - left - button_size, true); \
_glfw.callbacks.draw_text((GLFWwindow*)window, text, fg_color, decs.which.hovered ? hover_bg : bg_color, output, decs.titlebar.buffer.width, \
decs.titlebar.buffer.height, /*x=*/left, /*y=*/0, /*right_margin=*/decs.titlebar.buffer.width - left - button_size, true); \
decs.which.left = left; decs.which.width = button_size; left += button_size; \
}
if (window->wl.wm_capabilities.minimize) draw(minimize, "🗕", hover_bg);
@ -229,70 +218,88 @@ render_buttons:
static void
update_title_bar(_GLFWwindow *window) {
render_title_bar(window, false);
swap_buffers(&decs.top.buffer);
swap_buffers(&decs.titlebar.buffer);
}
static void
render_edges(_GLFWwindow *window) {
const size_t margin = create_shadow_tile(window);
if (!st.data) return; // out of memory
render_horizontal_shadow(_GLFWwindow *window, ssize_t scaled_shadow_size, ssize_t src_y_offset, ssize_t y, _GLFWWaylandBufferPair *buf) {
// left region
ssize_t src_y = src_y_offset + y;
const ssize_t src_leftover_corner = st.corner_size - scaled_shadow_size;
uint32_t *src = st.data + st.stride * src_y + scaled_shadow_size;
uint32_t *d_start = (uint32_t*)(buf->data.front + y * buf->stride);
uint32_t *d_end = (uint32_t*)(buf->data.front + (y+1) * buf->stride);
uint32_t *left_region_end = d_start + MIN(d_end - d_start, src_leftover_corner);
memcpy(d_start, src, sizeof(uint32_t) * (left_region_end - d_start));
// right region
uint32_t *right_region_start = MAX(d_start, d_end - src_leftover_corner);
src = st.data + st.stride * (src_y+1) - st.corner_size;
memcpy(right_region_start, src, sizeof(uint32_t) * MIN(src_leftover_corner, d_end - right_region_start));
src = st.data + st.stride * src_y + st.corner_size;
// middle region
for (uint32_t *d = left_region_end; d < right_region_start; d += scaled_shadow_size)
memcpy(d, src, sizeof(uint32_t) * MIN(right_region_start - d, scaled_shadow_size));
}
// bottom edge
uint32_t *src = st.data + (st.segments - 1) * margin * st.stride;
for (size_t y = 0; y < margin; y++) {
uint32_t *d = (uint32_t*)(decs.bottom.buffer.data.front + y * decs.bottom.buffer.stride);
uint32_t *s = src + st.stride * y;
// left corner
for (size_t x = 0; x < st.corner_size && x < decs.bottom.buffer.width; x++) d[x] = s[x];
// middle
size_t pos = st.corner_size, limit = decs.bottom.buffer.width > st.corner_size ? decs.bottom.buffer.width - st.corner_size : 0;
s += st.corner_size;
while (pos < limit) {
uint32_t *p = d + pos;
for (size_t x = 0; x < margin && pos + x < limit; x++) p[x] = s[x];
pos += margin;
}
// right corner
s += margin;
for (size_t x = 0; x < st.corner_size && limit + x < decs.bottom.buffer.width; x++) d[limit + x] = s[x];
}
static void
copy_vertical_region(
_GLFWwindow *window, ssize_t src_y_start, ssize_t src_y_limit,
ssize_t y_start, ssize_t y_limit, ssize_t src_x_offset, _GLFWWaylandBufferPair *buf
) {
for (ssize_t dy = y_start, sy = src_y_start; dy < y_limit && sy < src_y_limit; dy++, sy++)
memcpy(buf->data.front + dy * buf->stride, st.data + sy * st.stride + src_x_offset, sizeof(uint32_t) * buf->width);
}
// upper corners
for (size_t y = 0; y < st.corner_size && y < decs.left.buffer.height; y++) {
uint32_t *left_src = st.data + st.stride * y;
uint32_t *left_dest = (uint32_t*)(decs.left.buffer.data.front + y * decs.left.buffer.stride);
memcpy(left_dest, left_src, margin * sizeof(uint32_t));
uint32_t *right_src = left_src + 2 * st.corner_size;
uint32_t *right_dest = (uint32_t*)(decs.right.buffer.data.front + y * decs.right.buffer.stride);
memcpy(right_dest, right_src, margin * sizeof(uint32_t));
}
static void
render_shadows(_GLFWwindow *window) {
const ssize_t scaled_shadow_size = create_shadow_tile(window);
if (!st.data || !scaled_shadow_size) return; // out of memory
// upper and lower shadows
for (ssize_t y = 0; y < scaled_shadow_size; y++) {
_GLFWWaylandBufferPair *buf = &decs.shadow_upper_left.buffer;
uint32_t *src = st.data + st.stride * y;
uint32_t *d = (uint32_t*)(buf->data.front + y * buf->stride);
memcpy(d, src, sizeof(uint32_t) * scaled_shadow_size);
// lower corners
size_t src_height = st.corner_size - margin;
size_t dest_top = decs.left.buffer.height > src_height ? decs.left.buffer.height - src_height : 0;
size_t src_top = st.corner_size + margin;
for (size_t src_y = src_top, dest_y = dest_top; src_y < src_top + src_height && dest_y < decs.left.buffer.height; src_y++, dest_y++) {
uint32_t *s = st.data + st.stride * src_y;
uint32_t *d = (uint32_t*)(decs.left.buffer.data.front + dest_y * decs.left.buffer.stride);
memcpy(d, s, margin * sizeof(uint32_t));
s += 2 * st.corner_size;
d = (uint32_t*)(decs.right.buffer.data.front + dest_y * decs.left.buffer.stride);
memcpy(d, s, margin * sizeof(uint32_t));
}
buf = &decs.shadow_upper_right.buffer;
src += st.stride - scaled_shadow_size;
d = (uint32_t*)(buf->data.front + y * buf->stride);
memcpy(d, src, sizeof(uint32_t) * scaled_shadow_size);
// sides
size_t limit = decs.left.buffer.height > src_height ? decs.left.buffer.height - src_height : 0;
for (size_t dest_y = st.corner_size, src_y = 0; dest_y < limit; dest_y++, src_y = (src_y + 1) % margin) {
uint32_t *src = st.data + (st.corner_size + src_y) * st.stride;
uint32_t *left_dest = (uint32_t*)(decs.left.buffer.data.front + dest_y * decs.left.buffer.stride);
memcpy(left_dest, src, margin * sizeof(uint32_t));
src += 2 * st.corner_size;
uint32_t *right_dest = (uint32_t*)(decs.right.buffer.data.front + dest_y * decs.right.buffer.stride);
memcpy(right_dest, src, margin * sizeof(uint32_t));
const size_t tile_bottom_start = st.stride - scaled_shadow_size;
buf = &decs.shadow_lower_left.buffer;
src = st.data + (tile_bottom_start + y) * st.stride;
d = (uint32_t*)(buf->data.front + y * buf->stride);
memcpy(d, src, sizeof(uint32_t) * scaled_shadow_size);
buf = &decs.shadow_lower_right.buffer;
src += st.stride - scaled_shadow_size;
d = (uint32_t*)(buf->data.front + y * buf->stride);
memcpy(d, src, sizeof(uint32_t) * scaled_shadow_size);
render_horizontal_shadow(window, scaled_shadow_size, 0, y, &decs.shadow_top.buffer);
render_horizontal_shadow(window, scaled_shadow_size, st.stride - scaled_shadow_size, y, &decs.shadow_bottom.buffer);
}
// side shadows
// top region
const ssize_t src_leftover_corner = st.corner_size - scaled_shadow_size;
ssize_t y_start = 0, y_end = decs.shadow_left.buffer.height, top_end = MIN(y_end, src_leftover_corner);
ssize_t right_src_start = st.stride - scaled_shadow_size;
#define c(src_y_start, src_y_limit, dest_y_start, dest_y_limit) { \
copy_vertical_region(window, src_y_start, src_y_limit, dest_y_start, dest_y_limit, 0, &decs.shadow_left.buffer); \
copy_vertical_region(window, src_y_start, src_y_limit, dest_y_start, dest_y_limit, right_src_start, &decs.shadow_right.buffer); \
}
c(scaled_shadow_size, st.corner_size, y_start, top_end);
// bottom region
ssize_t bottom_start = MAX(0, y_end - src_leftover_corner);
c(st.stride - st.corner_size, st.stride - scaled_shadow_size, bottom_start, y_end);
// middle region
for (ssize_t dest_y = top_end; dest_y < bottom_start; dest_y += scaled_shadow_size)
c(st.corner_size, st.corner_size + scaled_shadow_size, dest_y, MIN(dest_y + scaled_shadow_size, bottom_start));
#undef c
#define copy(which) for (uint32_t *src = (uint32_t*)decs.which.buffer.data.front, *dest = (uint32_t*)decs.which.buffer.data.back; src < (uint32_t*)(decs.which.buffer.data.front + decs.which.buffer.size_in_bytes); src++, dest++) *dest = (A(*src) / 2 ) << 24;
copy(left); copy(bottom); copy(right);
all_shadow_surfaces(copy);
#undef copy
}
@ -302,14 +309,18 @@ static bool
create_shm_buffers(_GLFWwindow* window) {
const float scale = _glfwWaylandWindowScale(window);
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;
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);
#define bp(which, width, height) decs.mapping.size += init_buffer_pair(&decs.which.buffer, width, height, scale);
bp(titlebar, window->wl.width, decs.metrics.visible_titlebar_height);
bp(shadow_top, window->wl.width, decs.metrics.width);
bp(shadow_bottom, window->wl.width, decs.metrics.width);
bp(shadow_left, decs.metrics.width, window->wl.height + decs.metrics.visible_titlebar_height);
bp(shadow_right, decs.metrics.width, window->wl.height + decs.metrics.visible_titlebar_height);
bp(shadow_upper_left, decs.metrics.width, decs.metrics.width);
bp(shadow_upper_right, decs.metrics.width, decs.metrics.width);
bp(shadow_lower_left, decs.metrics.width, decs.metrics.width);
bp(shadow_lower_right, decs.metrics.width, decs.metrics.width);
#undef bp
int fd = createAnonymousFile(decs.mapping.size);
if (fd < 0) {
@ -326,20 +337,20 @@ create_shm_buffers(_GLFWwindow* window) {
struct wl_shm_pool* pool = wl_shm_create_pool(_glfw.wl.shm, fd, decs.mapping.size);
close(fd);
size_t offset = 0;
#define a(which) alloc_buffer_pair(window->id, &decs.which.buffer, pool, decs.mapping.data, &offset)
a(top); a(left); a(bottom); a(right);
#undef a
#define Q(which) alloc_buffer_pair(window->id, &decs.which.buffer, pool, decs.mapping.data, &offset)
all_surfaces(Q);
#undef Q
wl_shm_pool_destroy(pool);
create_shadow_tile(window);
render_title_bar(window, true);
render_edges(window);
debug("Created decoration buffers at scale: %f vertical_height: %zu horizontal_width: %zu\n", scale, vertical_height, horizontal_width);
render_shadows(window);
debug("Created decoration buffers at scale: %f\n", scale);
return true;
}
static void
free_csd_surfaces(_GLFWwindow *window) {
#define d(which) {\
#define Q(which) {\
if (decs.which.subsurface) wl_subsurface_destroy(decs.which.subsurface); \
decs.which.subsurface = NULL; \
if (decs.which.surface) wl_surface_destroy(decs.which.surface); \
@ -347,33 +358,34 @@ free_csd_surfaces(_GLFWwindow *window) {
if (decs.which.wp_viewport) wp_viewport_destroy(decs.which.wp_viewport); \
decs.which.wp_viewport = NULL; \
}
d(left); d(top); d(right); d(bottom);
#undef d
all_surfaces(Q);
#undef Q
}
static void
free_csd_buffers(_GLFWwindow *window) {
#define d(which) { \
#define Q(which) { \
if (decs.which.buffer.a_needs_to_be_destroyed && decs.which.buffer.a) wl_buffer_destroy(decs.which.buffer.a); \
if (decs.which.buffer.b_needs_to_be_destroyed && 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
all_surfaces(Q);
#undef Q
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) {
position_csd_surface(_GLFWWaylandCSDSurface *s, int x, int y) {
wl_surface_set_buffer_scale(s->surface, 1);
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) {
create_csd_surfaces(_GLFWwindow *window, _GLFWWaylandCSDSurface *s) {
s->surface = wl_compositor_create_surface(_glfw.wl.compositor);
wl_surface_set_user_data(s->surface, window);
s->subsurface = wl_subcompositor_get_subsurface(_glfw.wl.subcompositor, s->surface, window->wl.surface);
if (_glfw.wl.wp_viewporter) s->wp_viewport = wp_viewporter_get_viewport(_glfw.wl.wp_viewporter, s->surface);
}
@ -401,7 +413,7 @@ ensure_csd_resources(_GLFWwindow *window) {
decs.for_window_state.fscale != _glfwWaylandWindowScale(window) ||
!decs.mapping.data
);
const bool needs_update = focus_changed || size_changed || !decs.left.surface || decs.buffer_destroyed;
const bool needs_update = focus_changed || size_changed || !decs.titlebar.surface || decs.buffer_destroyed;
debug("CSD: old.size: %dx%d new.size: %dx%d needs_update: %d size_changed: %d buffer_destroyed: %d\n",
decs.for_window_state.width, decs.for_window_state.height, window->wl.width, window->wl.height, needs_update, size_changed, decs.buffer_destroyed);
if (!needs_update) return false;
@ -411,28 +423,26 @@ ensure_csd_resources(_GLFWwindow *window) {
decs.buffer_destroyed = false;
}
int32_t x, y;
x = 0; y = -decs.metrics.top;
if (!decs.top.surface) create_csd_surfaces(window, &decs.top);
position_csd_surface(&decs.top, x, y);
#define setup_surface(which, x, y) \
if (!decs.which.surface) create_csd_surfaces(window, &decs.which); \
position_csd_surface(&decs.which, x, y);
x = -decs.metrics.width; y = -decs.metrics.top;
if (!decs.left.surface) create_csd_surfaces(window, &decs.left);
position_csd_surface(&decs.left, x, y);
x = -decs.metrics.width; y = window->wl.height;
if (!decs.bottom.surface) create_csd_surfaces(window, &decs.bottom);
position_csd_surface(&decs.bottom, x, y);
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);
setup_surface(titlebar, 0, -decs.metrics.visible_titlebar_height);
setup_surface(shadow_top, decs.titlebar.x, decs.titlebar.y - decs.metrics.width);
setup_surface(shadow_bottom, decs.titlebar.x, window->wl.height);
setup_surface(shadow_left, -decs.metrics.width, decs.titlebar.y);
setup_surface(shadow_right, window->wl.width, decs.shadow_left.y);
setup_surface(shadow_upper_left, decs.shadow_left.x, decs.shadow_top.y);
setup_surface(shadow_upper_right, decs.shadow_right.x, decs.shadow_top.y);
setup_surface(shadow_lower_left, decs.shadow_left.x, decs.shadow_bottom.y);
setup_surface(shadow_lower_right, decs.shadow_right.x, decs.shadow_bottom.y);
if (focus_changed) update_title_bar(window);
damage_csd(top, decs.top.buffer.front);
damage_csd(left, is_focused ? decs.left.buffer.front : decs.left.buffer.back);
damage_csd(bottom, is_focused ? decs.bottom.buffer.front : decs.bottom.buffer.back);
damage_csd(right, is_focused ? decs.right.buffer.front : decs.right.buffer.back);
damage_csd(titlebar, decs.titlebar.buffer.front);
#define d(which) damage_csd(which, is_focused ? decs.which.buffer.front : decs.which.buffer.back);
d(shadow_left); d(shadow_right); d(shadow_top); d(shadow_bottom);
d(shadow_upper_left); d(shadow_upper_right); d(shadow_lower_left); d(shadow_lower_right);
#undef d
decs.for_window_state.width = window->wl.width;
decs.for_window_state.height = window->wl.height;
@ -461,9 +471,9 @@ bool
csd_change_title(_GLFWwindow *window) {
if (!window_is_csd_capable(window)) return false;
if (ensure_csd_resources(window)) return true; // CSD were re-rendered for other reasons
if (decs.top.surface) {
if (decs.titlebar.surface) {
update_title_bar(window);
damage_csd(top, decs.top.buffer.front);
damage_csd(titlebar, decs.titlebar.buffer.front);
return true;
}
return false;
@ -471,7 +481,7 @@ csd_change_title(_GLFWwindow *window) {
void
csd_set_window_geometry(_GLFWwindow *window, int32_t *width, int32_t *height) {
bool has_csd = window_is_csd_capable(window) && decs.top.surface && !(window->wl.current.toplevel_states & TOPLEVEL_STATE_FULLSCREEN);
bool has_csd = window_is_csd_capable(window) && decs.titlebar.surface && !(window->wl.current.toplevel_states & TOPLEVEL_STATE_FULLSCREEN);
bool size_specified_by_compositor = *width > 0 && *height > 0;
if (!size_specified_by_compositor) {
*width = window->wl.user_requested_content_size.width;
@ -551,17 +561,15 @@ set_cursor(GLFWCursorShape shape, _GLFWwindow* window)
static bool
update_hovered_button(_GLFWwindow *window) {
bool has_hovered_button = false;
if (y >= decs.metrics.width) {
int scaled_x = (int)round(decs.for_window_state.fscale * x);
int scaled_x = (int)round(decs.for_window_state.fscale * x);
#define c(which) \
if (decs.which.left <= scaled_x && scaled_x < decs.which.left + decs.which.width) { \
has_hovered_button = true; \
if (!decs.which.hovered) { decs.titlebar_needs_update = true; decs.which.hovered = true; } \
} else if (decs.which.hovered) { decs.titlebar_needs_update = true; decs.which.hovered = false; }
if (decs.which.left <= scaled_x && scaled_x < decs.which.left + decs.which.width) { \
has_hovered_button = true; \
if (!decs.which.hovered) { decs.titlebar_needs_update = true; decs.which.hovered = true; } \
} else if (decs.which.hovered) { decs.titlebar_needs_update = true; decs.which.hovered = false; }
c(minimize); c(maximize); c(close);
c(minimize); c(maximize); c(close);
#undef c
}
update_title_bar(window);
return has_hovered_button;
}
@ -572,9 +580,9 @@ has_hovered_button(_GLFWwindow *window) {
}
static void
handle_pointer_leave(_GLFWwindow *window) {
handle_pointer_leave(_GLFWwindow *window, struct wl_surface *surface) {
#define c(which) if (decs.which.hovered) { decs.titlebar_needs_update = true; decs.which.hovered = false; }
if (decs.focus == TOP_DECORATION) {
if (surface == decs.titlebar.surface) {
c(minimize); c(maximize); c(close);
}
#undef c
@ -588,38 +596,27 @@ handle_pointer_move(_GLFWwindow *window) {
switch (decs.focus)
{
case CENTRAL_WINDOW: break;
case TOP_DECORATION:
if (update_hovered_button(window)) {
cursorShape = GLFW_POINTER_CURSOR;
} else if (y < decs.metrics.width) cursorShape = GLFW_N_RESIZE_CURSOR;
break;
case LEFT_DECORATION:
if (y < decs.metrics.width)
cursorShape = GLFW_NW_RESIZE_CURSOR;
else
cursorShape = GLFW_W_RESIZE_CURSOR;
break;
case RIGHT_DECORATION:
if (y < decs.metrics.width)
cursorShape = GLFW_NE_RESIZE_CURSOR;
else
cursorShape = GLFW_E_RESIZE_CURSOR;
break;
case BOTTOM_DECORATION:
if (x < decs.metrics.width)
cursorShape = GLFW_SW_RESIZE_CURSOR;
else if (x > window->wl.width + decs.metrics.width)
cursorShape = GLFW_SE_RESIZE_CURSOR;
else
cursorShape = GLFW_S_RESIZE_CURSOR;
break;
case CSD_titlebar: if (update_hovered_button(window)) cursorShape = GLFW_POINTER_CURSOR; break;
case CSD_shadow_top: cursorShape = GLFW_N_RESIZE_CURSOR; break;
case CSD_shadow_bottom: cursorShape = GLFW_S_RESIZE_CURSOR; break;
case CSD_shadow_left: cursorShape = GLFW_W_RESIZE_CURSOR; break;
case CSD_shadow_right: cursorShape = GLFW_E_RESIZE_CURSOR; break;
case CSD_shadow_upper_left: cursorShape = GLFW_NW_RESIZE_CURSOR; break;
case CSD_shadow_upper_right: cursorShape = GLFW_NE_RESIZE_CURSOR; break;
case CSD_shadow_lower_left: cursorShape = GLFW_SW_RESIZE_CURSOR; break;
case CSD_shadow_lower_right: cursorShape = GLFW_SE_RESIZE_CURSOR; break;
}
if (_glfw.wl.cursorPreviousShape != cursorShape) set_cursor(cursorShape, window);
}
static void
handle_pointer_enter(_GLFWwindow *window) {
handle_pointer_move(window); // enter is also a move
handle_pointer_enter(_GLFWwindow *window, struct wl_surface *surface) {
#define Q(which) if (decs.which.surface == surface) { \
decs.focus = CSD_##which; handle_pointer_move(window); return; } // enter is also a move
all_surfaces(Q)
#undef Q
decs.focus = CENTRAL_WINDOW;
}
static void
@ -628,7 +625,7 @@ handle_pointer_button(_GLFWwindow *window, uint32_t button, uint32_t state) {
if (button == BTN_LEFT) {
switch (decs.focus) {
case CENTRAL_WINDOW: break;
case TOP_DECORATION:
case CSD_titlebar:
if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
monotonic_t last_click_at = decs.last_click_on_top_decoration_at;
decs.last_click_on_top_decoration_at = monotonic();
@ -648,39 +645,22 @@ handle_pointer_button(_GLFWwindow *window, uint32_t button, uint32_t state) {
} else if (decs.close.hovered) _glfwInputWindowCloseRequest(window);
}
if (!has_hovered_button(window)) {
if (y < decs.metrics.width)
edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP;
else {
if (window->wl.xdg.toplevel)
xdg_toplevel_move(window->wl.xdg.toplevel, _glfw.wl.seat, _glfw.wl.pointer_serial);
}
if (window->wl.xdg.toplevel) xdg_toplevel_move(window->wl.xdg.toplevel, _glfw.wl.seat, _glfw.wl.pointer_serial);
}
break;
case LEFT_DECORATION:
if (y < decs.metrics.width)
edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT;
else
edges = XDG_TOPLEVEL_RESIZE_EDGE_LEFT;
break;
case RIGHT_DECORATION:
if (y < decs.metrics.width)
edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT;
else
edges = XDG_TOPLEVEL_RESIZE_EDGE_RIGHT;
break;
case BOTTOM_DECORATION:
if (x < decs.metrics.width)
edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT;
else if (x > window->wl.width + decs.metrics.width)
edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT;
else
edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM;
break;
case CSD_shadow_left: edges = XDG_TOPLEVEL_RESIZE_EDGE_LEFT; break;
case CSD_shadow_upper_left: edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT; break;
case CSD_shadow_right: edges = XDG_TOPLEVEL_RESIZE_EDGE_RIGHT; break;
case CSD_shadow_upper_right: edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT; break;
case CSD_shadow_top: edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP; break;
case CSD_shadow_lower_left: edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT; break;
case CSD_shadow_bottom: edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM; break;
case CSD_shadow_lower_right: edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT; break;
}
if (edges != XDG_TOPLEVEL_RESIZE_EDGE_NONE) xdg_toplevel_resize(window->wl.xdg.toplevel, _glfw.wl.seat, _glfw.wl.pointer_serial, edges);
}
else if (button == BTN_RIGHT) {
if (decs.focus != CENTRAL_WINDOW && window->wl.xdg.toplevel)
if (decs.focus == CSD_titlebar && window->wl.xdg.toplevel)
{
if (window->wl.wm_capabilities.window_menu) xdg_toplevel_show_window_menu(
window->wl.xdg.toplevel, _glfw.wl.seat, _glfw.wl.pointer_serial, (int32_t)x, (int32_t)y - decs.metrics.top);
@ -693,13 +673,13 @@ handle_pointer_button(_GLFWwindow *window, uint32_t button, uint32_t state) {
void
csd_handle_pointer_event(_GLFWwindow *window, int button, int state) {
csd_handle_pointer_event(_GLFWwindow *window, int button, int state, struct wl_surface *surface) {
if (!window_is_csd_capable(window)) return;
decs.titlebar_needs_update = false;
switch (button) {
case -1: handle_pointer_move(window); break;
case -2: handle_pointer_enter(window); break;
case -3: handle_pointer_leave(window); break;
case -2: handle_pointer_enter(window, surface); break;
case -3: handle_pointer_leave(window, surface); break;
default: handle_pointer_button(window, button, state); break;
}
if (decs.titlebar_needs_update) {

View File

@ -14,4 +14,4 @@ bool csd_change_title(_GLFWwindow *window);
void csd_set_window_geometry(_GLFWwindow *window, int32_t *width, int32_t *height);
bool csd_set_titlebar_color(_GLFWwindow *window, uint32_t color, bool use_system_color);
void csd_set_visible(_GLFWwindow *window, bool visible);
void csd_handle_pointer_event(_GLFWwindow *window, int button, int state);
void csd_handle_pointer_event(_GLFWwindow *window, int button, int state, struct wl_surface* surface);

88
glfw/wl_init.c vendored
View File

@ -62,48 +62,38 @@ static int min(int n1, int n2)
return n1 < n2 ? n1 : n2;
}
static _GLFWwindow*
findWindowFromDecorationSurface(struct wl_surface* surface, _GLFWdecorationSideWayland* which)
{
_GLFWdecorationSideWayland focus;
if (!which) which = &focus;
_GLFWwindow* window = _glfw.windowListHead;
#define q(edge, result) if (surface == window->wl.decorations.edge.surface) { *which = result; break; }
while (window) {
q(top, TOP_DECORATION);
q(left, LEFT_DECORATION);
q(right, RIGHT_DECORATION);
q(bottom, BOTTOM_DECORATION);
window = window->next;
}
#undef q
return window;
}
#define x window->wl.allCursorPosX
#define y window->wl.allCursorPosY
static _GLFWwindow*
get_window_from_surface(struct wl_surface* surface) {
if (!surface) return NULL;
_GLFWwindow *ans = wl_surface_get_user_data(surface);
if (ans) {
const _GLFWwindow *w = _glfw.windowListHead;
while (w) {
if (w == ans) return ans;
w = w->next;
}
}
return NULL;
}
static void
pointerHandleEnter(
void* data UNUSED, struct wl_pointer* pointer UNUSED, uint32_t serial, struct wl_surface* surface,
wl_fixed_t sx, wl_fixed_t sy
) {
// Happens in case we just destroyed the surface.
if (!surface) return;
_GLFWdecorationSideWayland focus = CENTRAL_WINDOW;
_GLFWwindow* window = wl_surface_get_user_data(surface);
if (!window) {
window = findWindowFromDecorationSurface(surface, &focus);
if (!window) return;
}
window->wl.decorations.focus = focus;
_GLFWwindow* window = get_window_from_surface(surface);
if (!window) return;
_glfw.wl.serial = serial; _glfw.wl.input_serial = serial; _glfw.wl.pointer_serial = serial; _glfw.wl.pointer_enter_serial = serial;
_glfw.wl.pointerFocus = window;
window->wl.allCursorPosX = wl_fixed_to_double(sx);
window->wl.allCursorPosY = wl_fixed_to_double(sy);
if (focus != CENTRAL_WINDOW) csd_handle_pointer_event(window, -2, -2);
else {
if (surface != window->wl.surface) {
csd_handle_pointer_event(window, -2, -2, surface);
} else {
window->wl.decorations.focus = CENTRAL_WINDOW;
window->wl.hovered = true;
window->wl.cursorPosX = x;
window->wl.cursorPosY = y;
@ -114,35 +104,32 @@ pointerHandleEnter(
}
static void
pointerHandleLeave(void* data UNUSED, struct wl_pointer* pointer UNUSED, uint32_t serial, struct wl_surface* surface UNUSED) {
pointerHandleLeave(void* data UNUSED, struct wl_pointer* pointer UNUSED, uint32_t serial, struct wl_surface* surface) {
_GLFWwindow* window = _glfw.wl.pointerFocus;
if (!window) return;
_glfw.wl.serial = serial;
_glfw.wl.pointerFocus = NULL;
if (window->wl.decorations.focus == CENTRAL_WINDOW) {
if (window->wl.surface == surface) {
window->wl.hovered = false;
_glfwInputCursorEnter(window, false);
_glfw.wl.cursorPreviousShape = GLFW_INVALID_CURSOR;
} else csd_handle_pointer_event(window, -3, -3);
} else csd_handle_pointer_event(window, -3, -3, surface);
}
static void
pointerHandleMotion(void* data UNUSED, struct wl_pointer* pointer UNUSED, uint32_t time UNUSED, wl_fixed_t sx, wl_fixed_t sy) {
_GLFWwindow* window = _glfw.wl.pointerFocus;
if (!window) return;
if (window->cursorMode == GLFW_CURSOR_DISABLED) return;
if (!window || window->cursorMode == GLFW_CURSOR_DISABLED) return;
window->wl.allCursorPosX = wl_fixed_to_double(sx);
window->wl.allCursorPosY = wl_fixed_to_double(sy);
if (window->wl.decorations.focus != CENTRAL_WINDOW) {
csd_handle_pointer_event(window, -1, -1);
return;
csd_handle_pointer_event(window, -1, -1, NULL);
} else {
window->wl.cursorPosX = x;
window->wl.cursorPosY = y;
_glfwInputCursorPos(window, x, y);
_glfw.wl.cursorPreviousShape = GLFW_INVALID_CURSOR;
}
window->wl.cursorPosX = x;
window->wl.cursorPosY = y;
_glfwInputCursorPos(window, x, y);
_glfw.wl.cursorPreviousShape = GLFW_INVALID_CURSOR;
}
static void pointerHandleButton(void* data UNUSED,
@ -157,7 +144,7 @@ static void pointerHandleButton(void* data UNUSED,
_GLFWwindow* window = _glfw.wl.pointerFocus;
if (!window) return;
if (window->wl.decorations.focus != CENTRAL_WINDOW) {
csd_handle_pointer_event(window, button, state);
csd_handle_pointer_event(window, button, state, NULL);
return;
}
/* Makes left, right and middle 0, 1 and 2. Overall order follows evdev
@ -292,17 +279,8 @@ static void keyboardHandleEnter(void* data UNUSED,
struct wl_surface* surface,
struct wl_array* keys)
{
// Happens in the case we just destroyed the surface.
if (!surface)
return;
_GLFWwindow* window = wl_surface_get_user_data(surface);
if (!window)
{
window = findWindowFromDecorationSurface(surface, NULL);
if (!window)
return;
}
_GLFWwindow* window = get_window_from_surface(surface);
if (!window) return;
_glfw.wl.serial = serial; _glfw.wl.input_serial = serial; _glfw.wl.keyboard_enter_serial = serial;
_glfw.wl.keyboardFocusId = window->id;

20
glfw/wl_platform.h vendored
View File

@ -94,14 +94,11 @@ typedef void (* PFN_wl_egl_window_resize)(struct wl_egl_window*, int, int, int,
#define wl_egl_window_destroy _glfw.wl.egl.window_destroy
#define wl_egl_window_resize _glfw.wl.egl.window_resize
typedef enum _GLFWdecorationSideWayland
typedef enum _GLFWCSDSurface
{
CENTRAL_WINDOW,
TOP_DECORATION,
LEFT_DECORATION,
RIGHT_DECORATION,
BOTTOM_DECORATION,
} _GLFWdecorationSideWayland;
CENTRAL_WINDOW, CSD_titlebar, CSD_shadow_top, CSD_shadow_left, CSD_shadow_bottom, CSD_shadow_right,
CSD_shadow_upper_left, CSD_shadow_upper_right, CSD_shadow_lower_left, CSD_shadow_lower_right,
} _GLFWCSDSurface;
typedef struct _GLFWWaylandBufferPair {
struct wl_buffer *a, *b, *front, *back;
@ -111,13 +108,13 @@ typedef struct _GLFWWaylandBufferPair {
bool a_needs_to_be_destroyed, b_needs_to_be_destroyed;
} _GLFWWaylandBufferPair;
typedef struct _GLFWWaylandCSDEdge {
typedef struct _GLFWWaylandCSDSurface {
struct wl_surface *surface;
struct wl_subsurface *subsurface;
struct wp_viewport *wp_viewport;
_GLFWWaylandBufferPair buffer;
int x, y;
} _GLFWWaylandCSDEdge;
} _GLFWWaylandCSDSurface;
typedef enum WaylandWindowState {
@ -203,8 +200,9 @@ typedef struct _GLFWwindowWayland
struct {
bool serverSide, buffer_destroyed, titlebar_needs_update;
_GLFWdecorationSideWayland focus;
_GLFWWaylandCSDEdge top, left, right, bottom;
_GLFWCSDSurface focus;
_GLFWWaylandCSDSurface titlebar, shadow_left, shadow_right, shadow_top, shadow_bottom, shadow_upper_left, shadow_upper_right, shadow_lower_left, shadow_lower_right;
struct {
uint8_t *data;