diff --git a/digesting_libdecor.c b/digesting_libdecor.c index 06c90d4..5dbb577 100755 --- a/digesting_libdecor.c +++ b/digesting_libdecor.c @@ -160,18 +160,18 @@ pointer_motion(void *data, struct wl_pointer *wl_pointer, uint32_t time, wl_pointer_set_cursor(seat->wl_pointer, seat->serial, seat->cursor_surface, image->hotspot_x, image->hotspot_y); } - /* avoid warnings after decoration has been turned off */ if (!GTK_IS_WIDGET(ctx.header) || ctx.active != COMPONENT_SLOT_HEADER){ ctx.hdr_focus.type = HEADER_NONE; } - new_focus = get_header_focus(GTK_HEADER_BAR(ctx.header), seat->pointer_x, seat->pointer_y); /* only update if widget change so that we keep the state */ if (ctx.hdr_focus.widget != new_focus.widget){ ctx.hdr_focus = new_focus; + ctx.hdr_state = 0; } - ctx.hdr_focus.state |= GTK_STATE_FLAG_PRELIGHT; + ctx.hdr_state |= GTK_STATE_FLAG_PRELIGHT; + /* redraw with updated button visuals */ draw_title_bar(); wl_surface_commit(ctx.wl_surface); @@ -236,7 +236,7 @@ pointer_button(void *data, struct wl_pointer *wl_pointer, uint32_t serial, case HEADER_MIN: case HEADER_MAX: case HEADER_CLOSE: { - ctx.hdr_focus.state |= GTK_STATE_FLAG_ACTIVE; + ctx.hdr_state |= GTK_STATE_FLAG_ACTIVE; draw_title_bar(); wl_surface_commit(ctx.wl_surface); }break; @@ -281,7 +281,7 @@ pointer_button(void *data, struct wl_pointer *wl_pointer, uint32_t serial, default: break; } - ctx.hdr_focus.state &= ~GTK_STATE_FLAG_ACTIVE; + ctx.hdr_state &= ~GTK_STATE_FLAG_ACTIVE; if (GTK_IS_WIDGET(ctx.header)){ draw_title_bar(); wl_surface_commit(ctx.wl_surface); @@ -289,7 +289,7 @@ pointer_button(void *data, struct wl_pointer *wl_pointer, uint32_t serial, } } else{ - ctx.hdr_focus.state &= ~GTK_STATE_FLAG_ACTIVE; + ctx.hdr_state &= ~GTK_STATE_FLAG_ACTIVE; if (GTK_IS_WIDGET(ctx.header)) { draw_title_bar(); wl_surface_commit(ctx.wl_surface); @@ -364,7 +364,7 @@ touch_down(void *data, struct wl_touch *wl_touch, uint32_t serial, case HEADER_MIN: case HEADER_MAX: case HEADER_CLOSE: { - ctx.hdr_focus.state |= GTK_STATE_FLAG_ACTIVE; + ctx.hdr_state |= GTK_STATE_FLAG_ACTIVE; draw_title_bar(); wl_surface_commit(ctx.wl_surface); }break; @@ -398,7 +398,7 @@ touch_up(void *data, struct wl_touch *wl_touch, uint32_t serial, uint32_t time, if (seat->touch_focus && own_proxy(seat->touch_focus)){ if (ctx.touch_active != 0){ if (ctx.touch_active == COMPONENT_SLOT_HEADER){ - switch (ctx.hdr_focus.type) { + switch (ctx.hdr_focus.type){ case HEADER_MIN: { if (ctx.frame_capabilities & LIBDECOR_ACTION_MINIMIZE){ xdg_toplevel_set_minimized(ctx.xdg_toplevel); @@ -422,19 +422,20 @@ touch_up(void *data, struct wl_touch *wl_touch, uint32_t serial, uint32_t time, } /* unset active/clicked state once released */ - ctx.hdr_focus.state &= ~GTK_STATE_FLAG_ACTIVE; + ctx.hdr_state &= ~GTK_STATE_FLAG_ACTIVE; if (GTK_IS_WIDGET(ctx.header)) { draw_title_bar(); wl_surface_commit(ctx.wl_surface); } } - - seat->touch_focus = 0; - ctx.touch_active = 0; - ctx.hdr_focus.widget = 0; - ctx.hdr_focus.type = HEADER_NONE; - draw_decoration(); - wl_surface_commit(ctx.wl_surface); + else{ + seat->touch_focus = 0; + ctx.touch_active = 0; + ctx.hdr_focus.widget = 0; + ctx.hdr_focus.type = HEADER_NONE; + draw_decoration(); + wl_surface_commit(ctx.wl_surface); + } } } } @@ -716,7 +717,8 @@ const struct xdg_toplevel_listener xdg_toplevel_listener = { }; static void -xdg_toplevel_decoration_configure(void *data, struct zxdg_toplevel_decoration_v1 *zxdg_toplevel_decoration_v1, uint32_t mode){ +xdg_toplevel_decoration_configure(void *data, struct zxdg_toplevel_decoration_v1 *toplevel, + uint32_t mode){ if (!ctx.has_decoration_mode){ ctx.has_decoration_mode = true; ctx.decoration_mode = mode; @@ -954,7 +956,6 @@ int main(){ xdg_toplevel_set_title(ctx.xdg_toplevel, "Example Window"); ctx.title = strdup("Example Window"); - draw_decoration(); wl_surface_commit(ctx.wl_surface); /* (nodocs-wl_egl) */ @@ -1011,16 +1012,11 @@ int main(){ } } - /* (egl) eglMakeCurrent */ EGLBoolean make_current_success2 = 0; if (ctx.egl_surface != EGL_NO_SURFACE){ make_current_success2 = eglMakeCurrent(ctx.egl_display, ctx.egl_surface, ctx.egl_surface, ctx.egl_context); } - /* (egl) eglSwapInterval - ** " specifies the minimum number of video frame periods per buffer swap - ** for the window associated with the current context " - */ EGLBoolean swap_interval_success = 0; if (make_current_success2){ swap_interval_success = eglSwapInterval(ctx.egl_display, 1); @@ -1073,10 +1069,7 @@ int main(){ } } - if (ctx.close_signal){ - exit_loop = 1; - } - + /* apply new surface config */ if (ctx.has_cached_config){ ctx.has_cached_config = 0; if (ctx.cached_config.initialized){ @@ -1099,6 +1092,10 @@ int main(){ glClear(GL_COLOR_BUFFER_BIT); } + if (ctx.close_signal){ + exit_loop = 1; + } + /* (egl) eglSwapBuffers ** " back-buffered window surface, then the color buffer is copied ** (posted) to the native window associated with that surface " @@ -1169,28 +1166,6 @@ decoration_type_from_window_state(enum libdecor_window_state window_state){ return(result); } -static void -free_border_component(struct border_component *border_component){ - if (border_component->wl_surface) { - wl_subsurface_destroy(border_component->wl_subsurface); - border_component->wl_subsurface = NULL; - wl_surface_destroy(border_component->wl_surface); - border_component->wl_surface = NULL; - } - - if (border_component->wl_buffer != 0){ - wl_buffer_destroy(border_component->wl_buffer); - } - if (border_component->data != 0){ - munmap(border_component->data, border_component->data_size); - } - border_component->wl_buffer = 0; - border_component->data = 0; - border_component->data_size = 0; - border_component->width = 0; - border_component->height = 0; -} - static enum xdg_toplevel_resize_edge xdg_edge_from_edge(enum libdecor_resize_edge edge){ enum xdg_toplevel_resize_edge result = XDG_TOPLEVEL_RESIZE_EDGE_NONE; @@ -1208,26 +1183,19 @@ xdg_edge_from_edge(enum libdecor_resize_edge edge){ return(result); } -void -libdecor_frame_set_fullscreen(struct wl_output *output){ - xdg_toplevel_set_fullscreen(ctx.xdg_toplevel, output); -} - -void -libdecor_frame_unset_fullscreen(void){ - xdg_toplevel_unset_fullscreen(ctx.xdg_toplevel); -} - void libdecor_frame_commit(void){ - Sides2D border_size = border_size_from_window_state(ctx.frame_window_state); - bool csd = false; if (ctx.visible && ctx.decoration_mode == ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE){ csd = true; } + Sides2D border_size = {0}; + if (csd){ + border_size = border_size_from_window_state(ctx.frame_window_state); + } + if (!(ctx.frame_capabilities & LIBDECOR_ACTION_RESIZE)){ int mw = ctx.frame_content_width; int mh = ctx.frame_content_height; @@ -1270,8 +1238,28 @@ libdecor_frame_commit(void){ g_clear_pointer(&ctx.header, gtk_widget_destroy); g_clear_pointer(&ctx.window, gtk_widget_destroy); - free_border_component(&ctx.component_slot[COMPONENT_SLOT_HEADER]); - free_border_component(&ctx.component_slot[COMPONENT_SLOT_SHADOW]); + for (int i = 1; i < COMPONENT_SLOT_COUNT; i += 1){ + struct border_component * border_component = &ctx.component_slot[i]; + if (border_component->wl_subsurface != 0){ + wl_subsurface_destroy(border_component->wl_subsurface); + border_component->wl_subsurface = 0; + } + if (border_component->wl_surface != 0){ + wl_surface_destroy(border_component->wl_surface); + border_component->wl_surface = 0; + } + if (border_component->wl_buffer != 0){ + wl_buffer_destroy(border_component->wl_buffer); + border_component->wl_buffer = 0; + } + if (border_component->data != 0){ + munmap(border_component->data, border_component->data_size); + border_component->data = 0; + } + border_component->data_size = 0; + border_component->width = 0; + border_component->height = 0; + } ctx.shadow_showing = false; g_clear_pointer(&ctx.title, free); @@ -1283,7 +1271,6 @@ libdecor_frame_commit(void){ Extent2D extent = {0}; extent.w = ctx.frame_content_width; extent.h = ctx.frame_content_height; - if (csd){ extent.x = -border_size.x[0]; extent.y = -border_size.y[0]; @@ -1546,200 +1533,161 @@ render_shadow(cairo_t *cr, cairo_surface_t *surface, //#include "os-compatibility.c" #ifndef HAVE_MKOSTEMP static int -set_cloexec_or_close(int fd) -{ +set_cloexec_or_close(int fd){ + bool error = 0; long flags; - if (fd == -1) - return -1; - - flags = fcntl(fd, F_GETFD); - if (flags == -1) - goto err; - - if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) - goto err; + if (fd >= 0){ + if (fcntl(fd, F_GETFD) == -1){ + error = 1; + } + else if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1){ + error = 1; + } + + if (error){ + close(fd); + fd = -1; + } + } return fd; - - err: - close(fd); - return -1; } #endif static int -create_tmpfile_cloexec(char *tmpname) -{ +create_tmpfile_cloexec(char *tmpname){ int fd; #ifdef HAVE_MKOSTEMP fd = mkostemp(tmpname, O_CLOEXEC); - if (fd >= 0) + if (fd >= 0){ unlink(tmpname); + } #else fd = mkstemp(tmpname); + fd = set_cloexec_or_close(fd); if (fd >= 0) { - fd = set_cloexec_or_close(fd); unlink(tmpname); } #endif - return fd; + return(fd); } static int -os_resize_anonymous_file(int fd, off_t size) -{ +os_resize_anonymous_file(int fd, off_t size){ #ifdef HAVE_POSIX_FALLOCATE + sigset_t mask; sigset_t old_mask; - - /* posix_fallocate() might be interrupted, so we need to check - * for EINTR and retry in that case. - * However, in the presence of an alarm, the interrupt may trigger - * repeatedly and prevent a large posix_fallocate() to ever complete - * successfully, so we need to first block SIGALRM to prevent - * this. - */ sigemptyset(&mask); sigaddset(&mask, SIGALRM); sigprocmask(SIG_BLOCK, &mask, &old_mask); - /* - * Filesystems that do not support fallocate will return EINVAL or - * EOPNOTSUPP. In this case we need to fall back to ftruncate - */ + do { errno = posix_fallocate(fd, 0, size); } while (errno == EINTR); sigprocmask(SIG_SETMASK, &old_mask, NULL); - if (errno == 0) - return 0; - else if (errno != EINVAL && errno != EOPNOTSUPP) - return -1; -#endif - if (ftruncate(fd, size) < 0) - return -1; - return 0; + int result = 0; + if (errno != 0){ + result = -1; + if (errno == EINVAL || errno == EOPNOTSUPP){ + if (ftruncate(fd, size) >= 0){ + result = 0; + } + } + } + return(result); + +#else + + int result = -1; + if (ftruncate(fd, size) >= 0){ + result = 0; + } + return(result); + +#endif } - -/* - * Create a new, unique, anonymous file of the given size, and - * return the file descriptor for it. The file descriptor is set - * CLOEXEC. The file is immediately suitable for mmap()'ing - * the given size at offset zero. - * - * The file should not have a permanent backing store like a disk, - * but may have if XDG_RUNTIME_DIR is not properly implemented in OS. - * - * The file name is deleted from the file system. - * - * The file is suitable for buffer sharing between processes by - * transmitting the file descriptor over Unix sockets using the - * SCM_RIGHTS methods. - * - * If the C library implements posix_fallocate(), it is used to - * guarantee that disk space is available for the file at the - * given size. If disk space is insufficient, errno is set to ENOSPC. - * If posix_fallocate() is not supported, program may receive - * SIGBUS on accessing mmap()'ed file contents instead. - * - * If the C library implements memfd_create(), it is used to create the - * file purely in memory, without any backing file name on the file - * system, and then sealing off the possibility of shrinking it. This - * can then be checked before accessing mmap()'ed file contents, to - * make sure SIGBUS can't happen. It also avoids requiring - * XDG_RUNTIME_DIR. - */ int libdecor_os_create_anonymous_file(off_t size){ - static const char template[] = "/libdecor-shared-XXXXXX"; - const char *path; - char *name; - int fd; + static const char key[] = "/libdecor-shared-XXXXXX"; + int fd = -1; #ifdef HAVE_MEMFD_CREATE fd = memfd_create("libdecor", MFD_CLOEXEC | MFD_ALLOW_SEALING); - if (fd >= 0) { - /* We can add this seal before calling posix_fallocate(), as - * the file is currently zero-sized anyway. - * - * There is also no need to check for the return value, we - * couldn't do anything with it anyway. - */ + if (fd >= 0){ fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_SEAL); - } else + } #endif - { - path = getenv("XDG_RUNTIME_DIR"); - if (!path) { + + if (fd < 0){ + const char *path = getenv("XDG_RUNTIME_DIR"); + if (path == 0){ errno = ENOENT; - return -1; } - - name = malloc(strlen(path) + sizeof(template)); - if (!name) - return -1; - - strcpy(name, path); - strcat(name, template); - - fd = create_tmpfile_cloexec(name); - - free(name); - - if (fd < 0) - return -1; + else{ + char *name = malloc(strlen(path) + sizeof(key)); + if (name != 0){ + strcpy(name, path); + strcat(name, key); + fd = create_tmpfile_cloexec(name); + free(name); + } + } } - if (os_resize_anonymous_file(fd, size) < 0) { + if (fd >= 0 && os_resize_anonymous_file(fd, size) < 0){ close(fd); - return -1; + fd = -1; } - return fd; + return(fd); } //#include "plugins/gtk/libdecor-gtk.c" +struct find_widget_variables{ + char *name; + GtkWidget *widget; +} find_widget_variables; + static void -gtk_fill_widget_from_name(GtkWidget *widget, void *data){ - bool match = false; - if (GTK_IS_WIDGET(widget)){ - struct header_element_data *elem = data; - GtkStyleContext *style_context = gtk_widget_get_style_context(widget); - char *style_ctx = gtk_style_context_to_string(style_context, GTK_STYLE_CONTEXT_PRINT_SHOW_STYLE); - if (strstr(style_ctx, elem->name) != 0){ - elem->widget = widget; - match = true; +fill_widget_from_name(GtkWidget *widget, void *data){ + struct find_widget_variables *vars = data; + if (vars->widget == 0){ + bool match = false; + if (GTK_IS_WIDGET(widget)){ + GtkStyleContext *style_context = gtk_widget_get_style_context(widget); + char *style_str = gtk_style_context_to_string(style_context, GTK_STYLE_CONTEXT_PRINT_SHOW_STYLE); + if (strstr(style_str, vars->name) != 0){ + vars->widget = widget; + match = true; + } + free(style_str); + } + + if (!match && GTK_IS_CONTAINER(widget)){ + gtk_container_forall(GTK_CONTAINER(widget), &fill_widget_from_name, data); } - free(style_ctx); - } - - /* recursively traverse container */ - if (!match && GTK_IS_CONTAINER(widget)){ - gtk_container_forall(GTK_CONTAINER(widget), >k_fill_widget_from_name, data); } } -static struct header_element_data -find_widget_by_type(GtkWidget *widget, enum header_element type){ - char* name = 0; +static GtkWidget* +find_widget_by_type(GtkWidget *root, enum header_element type){ + struct find_widget_variables vars = {0}; switch (type){ - case HEADER_FULL: name = "headerbar.titlebar:"; break; - case HEADER_TITLE: name = "label.title:"; break; - case HEADER_MIN: name = ".minimize"; break; - case HEADER_MAX: name = ".maximize"; break; - case HEADER_CLOSE: name = ".close"; break; + case HEADER_FULL: vars.name = "headerbar.titlebar:"; break; + case HEADER_TITLE: vars.name = "label.title:"; break; + case HEADER_MIN: vars.name = ".minimize"; break; + case HEADER_MAX: vars.name = ".maximize"; break; + case HEADER_CLOSE: vars.name = ".close"; break; default:break; } - - struct header_element_data data = {0}; - data.name = name; - data.type = type; - gtk_fill_widget_from_name(widget, &data); - return(data); + fill_widget_from_name(root, &vars); + return(vars.widget); } static struct header_element_data @@ -1748,13 +1696,14 @@ get_header_focus(const GtkHeaderBar *header_bar, int x, int y){ struct header_element_data result = {0}; for (size_t i = 0; i < ARRAY_LENGTH(elems); i += 1){ - struct header_element_data elem = find_widget_by_type(GTK_WIDGET(header_bar), elems[i]); - if (elem.widget){ + GtkWidget *widget = find_widget_by_type(GTK_WIDGET(header_bar), elems[i]); + if (widget != 0){ GtkAllocation allocation; - gtk_widget_get_allocation(GTK_WIDGET(elem.widget), &allocation); + gtk_widget_get_allocation(GTK_WIDGET(widget), &allocation); if (allocation.x <= x && x < allocation.x + allocation.width && allocation.y <= y && y < allocation.y + allocation.height){ - result = elem; + result.type = elems[i]; + result.widget = widget; break; } } @@ -1897,10 +1846,7 @@ array_append(enum header_element **array, size_t *n, enum header_element item){ static void draw_header_button(cairo_t *cr, cairo_surface_t *surface, enum header_element button_type){ - struct header_element_data elem; GtkWidget *button; - GtkStyleContext* button_style; - GtkStateFlags style_state; GtkAllocation allocation; @@ -1922,19 +1868,18 @@ draw_header_button(cairo_t *cr, cairo_surface_t *surface, GtkBorder border; GtkBorder padding; - elem = find_widget_by_type(ctx.header, button_type); - button = elem.widget; + button = find_widget_by_type(ctx.header, button_type); if (button){ - button_style = gtk_widget_get_style_context(button); - style_state = elem.state; + GtkStyleContext *button_style = gtk_widget_get_style_context(button); + GtkStateFlags style_state = 0; /* change style based on window state and focus */ - if (!(ctx.frame_window_state & LIBDECOR_WINDOW_STATE_ACTIVE)) { + if (!(ctx.frame_window_state & LIBDECOR_WINDOW_STATE_ACTIVE)){ style_state |= GTK_STATE_FLAG_BACKDROP; } - if (ctx.hdr_focus.widget == button) { + if (ctx.hdr_focus.widget == button){ style_state |= GTK_STATE_FLAG_PRELIGHT; - if (ctx.hdr_focus.state & GTK_STATE_FLAG_ACTIVE) { + if (ctx.hdr_state & GTK_STATE_FLAG_ACTIVE){ style_state |= GTK_STATE_FLAG_ACTIVE; } } @@ -2037,7 +1982,7 @@ static void draw_border_component(enum component_slot slot){ if (slot < COMPONENT_SLOT_COUNT && ctx.component_slot[slot].wl_surface != 0){ - Extent2D extent = extent2d_from_component_slot(slot); + struct border_component *component = &ctx.component_slot[slot]; if (slot == COMPONENT_SLOT_SHADOW && ctx.shadow_showing){ struct wl_region *input_region; @@ -2045,26 +1990,25 @@ draw_border_component(enum component_slot slot){ input_region = wl_compositor_create_region(ctx.wl_compositor); wl_region_add(input_region, 0, 0, extent.w, extent.h); wl_region_subtract(input_region, -extent.x, -extent.y, ctx.frame_content_width, ctx.frame_content_height); - wl_surface_set_input_region(ctx.component_slot[slot].wl_surface, input_region); + wl_surface_set_input_region(component->wl_surface, input_region); wl_region_destroy(input_region); } - struct border_component *component = &ctx.component_slot[slot]; - { if (component->wl_buffer != 0){ wl_buffer_destroy(component->wl_buffer); + component->wl_buffer = 0; } if (component->data != 0){ munmap(component->data, component->data_size); + component->data = 0; } - component->wl_buffer = 0; - component->data = 0; component->data_size = 0; component->width = 0; component->height = 0; } + Extent2D extent = extent2d_from_component_slot(slot); { int width = extent.w; int height = extent.h; @@ -2125,7 +2069,7 @@ draw_border_component(enum component_slot slot){ /* title */ { - GtkWidget *label = find_widget_by_type(ctx.header, HEADER_TITLE).widget; + GtkWidget *label = find_widget_by_type(ctx.header, HEADER_TITLE); GtkAllocation allocation; gtk_widget_get_allocation(label, &allocation); @@ -2182,11 +2126,7 @@ draw_border_component(enum component_slot slot){ static void draw_title_bar(void){ - enum libdecor_window_state state; - GtkStyleContext *style; - - state = ctx.frame_window_state; - style = gtk_widget_get_style_context(ctx.window); + enum libdecor_window_state state = ctx.frame_window_state; if (!(state & LIBDECOR_WINDOW_STATE_ACTIVE)){ gtk_widget_set_state_flags(ctx.window, GTK_STATE_FLAG_BACKDROP, true); @@ -2194,14 +2134,13 @@ draw_title_bar(void){ else{ gtk_widget_unset_state_flags(ctx.window, GTK_STATE_FLAG_BACKDROP); } - + GtkStyleContext *style = gtk_widget_get_style_context(ctx.window); if (!(ctx.frame_window_state & LIBDECOR_WINDOW_STATE_NON_FLOATING)){ gtk_style_context_remove_class(style, "maximized"); } else{ gtk_style_context_add_class(style, "maximized"); } - gtk_widget_show_all(ctx.window); int pref_width; @@ -2327,8 +2266,9 @@ update_touch_focus(struct seat *seat, wl_fixed_t x, wl_fixed_t y){ struct header_element_data new_focus = get_header_focus(GTK_HEADER_BAR(ctx.header), wl_fixed_to_int(x), wl_fixed_to_int(y)); if (ctx.hdr_focus.widget != new_focus.widget){ ctx.hdr_focus = new_focus; + ctx.hdr_state = 0; } - ctx.hdr_focus.state |= GTK_STATE_FLAG_PRELIGHT; + ctx.hdr_state |= GTK_STATE_FLAG_PRELIGHT; draw_title_bar(); wl_surface_commit(ctx.wl_surface); } diff --git a/digesting_libdecor.h b/digesting_libdecor.h index 5f75f03..843615d 100644 --- a/digesting_libdecor.h +++ b/digesting_libdecor.h @@ -146,11 +146,9 @@ enum titlebar_gesture_state { TITLEBAR_GESTURE_STATE_DISCARDED, }; -struct header_element_data { - const char *name; +struct header_element_data{ enum header_element type; GtkWidget *widget; - GtkStateFlags state; }; enum decoration_type { @@ -253,8 +251,6 @@ enum titlebar_gesture { void libdecor_frame_show_window_menu(struct wl_seat *wl_seat, uint32_t serial, int x, int y); void libdecor_frame_commit(void); -void libdecor_frame_set_fullscreen(struct wl_output *output); -void libdecor_frame_unset_fullscreen(void); // #include "libdecor-cairo-blur.h" @@ -281,8 +277,6 @@ static void do_map(void); static const char *libdecor_gtk_proxy_tag = "libdecor-gtk"; -static void libdecor_plugin_gtk_frame_free(void); -static void libdecor_plugin_gtk_frame_commit(void); static Sides2D border_size_from_window_state(enum libdecor_window_state window_state); static struct wl_cursor* wl_cursor_from_pos(int x, int y); @@ -386,6 +380,7 @@ typedef struct Ctx{ GtkWidget *window; GtkWidget *header; struct header_element_data hdr_focus; + GtkStateFlags hdr_state; cairo_surface_t *shadow_blur;