[digesting_libdecor] extract redraw logic from each event path and just run once per frame (this introduces a bug with the title bar button highlights, will have to fix after simplifications are finished, not clear how to avoid incrementally)

main
Allen Webster 2026-03-02 14:27:41 -08:00
parent 107aa41329
commit 59031ef536
2 changed files with 127 additions and 169 deletions

View File

@ -345,6 +345,8 @@ const struct zxdg_toplevel_decoration_v1_listener
xdg_toplevel_decoration_listener = { xdg_toplevel_decoration_configure, }; xdg_toplevel_decoration_listener = { xdg_toplevel_decoration_configure, };
static GtkWidget* find_widget_by_type(GtkWidget *root, enum header_element type); static GtkWidget* find_widget_by_type(GtkWidget *root, enum header_element type);
static void ensure_title_bar_surfaces(void);
static void ensure_component(struct border_component *cmpnt);
int main(){ int main(){
/* get desktop settings */ /* get desktop settings */
@ -650,7 +652,17 @@ int main(){
} }
/* re-render cursor */ /* re-render cursor */
ctx.seat->current_cursor = wl_cursor_from_pos(ctx.seat->pointer_x, ctx.seat->pointer_y); {
struct wl_cursor *cursor = ctx.cursor_left_ptr;
if (ctx.active == COMPONENT_SLOT_SHADOW &&
(ctx.frame_capabilities & LIBDECOR_ACTION_RESIZE)){
enum libdecor_resize_edge edge = edge_from_pos(ctx.seat->pointer_x, ctx.seat->pointer_y);
if (edge != LIBDECOR_RESIZE_EDGE_NONE){
cursor = ctx.cursors[edge - 1];
}
}
ctx.seat->current_cursor = cursor;
}
if (ctx.seat->current_cursor != 0){ if (ctx.seat->current_cursor != 0){
struct wl_cursor_image *image = ctx.seat->current_cursor->images[0]; struct wl_cursor_image *image = ctx.seat->current_cursor->images[0];
struct wl_buffer *buffer = wl_cursor_image_get_buffer(image); struct wl_buffer *buffer = wl_cursor_image_get_buffer(image);
@ -667,16 +679,10 @@ int main(){
ctx.titlebar_gesture.first_pressed_button = 0; ctx.titlebar_gesture.first_pressed_button = 0;
ctx.hdr_focus.widget = 0; ctx.hdr_focus.widget = 0;
ctx.hdr_focus.type = HEADER_NONE; ctx.hdr_focus.type = HEADER_NONE;
draw_decoration();
wl_surface_commit(ctx.wl_surface);
} }
if (ctx.pointer_enter){ if (ctx.pointer_enter){
ctx.pointer_enter = 0; ctx.pointer_enter = 0;
if (ctx.active != 0){
draw_decoration();
wl_surface_commit(ctx.wl_surface);
}
} }
if (ctx.pointer_motion){ if (ctx.pointer_motion){
@ -720,9 +726,6 @@ int main(){
} }
} }
} }
draw_title_bar();
wl_surface_commit(ctx.wl_surface);
} }
if (ctx.pointer_button){ if (ctx.pointer_button){
@ -757,23 +760,21 @@ int main(){
xdg_toplevel_show_window_menu(ctx.xdg_toplevel, ctx.seat->wl_seat, ctx.seat->serial, ctx.seat->pointer_x, -title_height); xdg_toplevel_show_window_menu(ctx.xdg_toplevel, ctx.seat->wl_seat, ctx.seat->serial, ctx.seat->pointer_x, -title_height);
ctx.titlebar_gesture.state = TITLEBAR_GESTURE_STATE_CONSUMED; ctx.titlebar_gesture.state = TITLEBAR_GESTURE_STATE_CONSUMED;
} }
else if (ctx.pointer_button_button == BTN_LEFT &&
ctx.titlebar_gesture.first_pressed_button == BTN_LEFT &&
ctx.pointer_button_time - ctx.titlebar_gesture.first_pressed_time < (uint32_t)ctx.double_click_time_ms){
if ((ctx.frame_capabilities & LIBDECOR_ACTION_RESIZE)){
toggle_maximized();
}
ctx.titlebar_gesture.state = TITLEBAR_GESTURE_STATE_CONSUMED;
}
else{ else{
if (ctx.pointer_button_button == BTN_LEFT && ctx.titlebar_gesture.first_pressed_button = ctx.pointer_button_button;
ctx.titlebar_gesture.first_pressed_button == BTN_LEFT && ctx.titlebar_gesture.first_pressed_time = ctx.pointer_button_time;
ctx.pointer_button_time - ctx.titlebar_gesture.first_pressed_time < (uint32_t)ctx.double_click_time_ms){ ctx.titlebar_gesture.pressed_x = ctx.seat->pointer_x;
if ((ctx.frame_capabilities & LIBDECOR_ACTION_RESIZE)){ ctx.titlebar_gesture.pressed_y = ctx.seat->pointer_y;
toggle_maximized(); ctx.titlebar_gesture.serial = ctx.seat->serial;
} ctx.titlebar_gesture.state = TITLEBAR_GESTURE_STATE_BUTTON_PRESSED;
ctx.titlebar_gesture.state = TITLEBAR_GESTURE_STATE_CONSUMED;
}
else{
ctx.titlebar_gesture.first_pressed_button = ctx.pointer_button_button;
ctx.titlebar_gesture.first_pressed_time = ctx.pointer_button_time;
ctx.titlebar_gesture.pressed_x = ctx.seat->pointer_x;
ctx.titlebar_gesture.pressed_y = ctx.seat->pointer_y;
ctx.titlebar_gesture.serial = ctx.seat->serial;
ctx.titlebar_gesture.state = TITLEBAR_GESTURE_STATE_BUTTON_PRESSED;
}
} }
ctx.titlebar_gesture.button_pressed_count = 1; ctx.titlebar_gesture.button_pressed_count = 1;
@ -781,12 +782,7 @@ int main(){
switch (ctx.hdr_focus.type){ switch (ctx.hdr_focus.type){
case HEADER_MIN: case HEADER_MIN:
case HEADER_MAX: case HEADER_MAX:
case HEADER_CLOSE: { case HEADER_CLOSE: ctx.hdr_focus_is_active = 1; break;
ctx.hdr_focus_is_active = 1;
draw_title_bar();
wl_surface_commit(ctx.wl_surface);
}break;
default: break; default: break;
} }
} }
@ -833,11 +829,6 @@ int main(){
else{ else{
ctx.hdr_focus_is_active = 0; ctx.hdr_focus_is_active = 0;
} }
if (GTK_IS_WIDGET(ctx.header)) {
draw_title_bar();
wl_surface_commit(ctx.wl_surface);
}
} }
}break; }break;
@ -859,13 +850,10 @@ int main(){
default: break; default: break;
} }
} }
/* apply new surface config */ /* apply new surface config */
if (ctx.has_cached_config){ if (ctx.has_cached_config){
ctx.has_cached_config = 0;
if (ctx.cached_config.initialized){ if (ctx.cached_config.initialized){
ctx.frame_window_state = ctx.cached_config.window_state; ctx.frame_window_state = ctx.cached_config.window_state;
} }
@ -900,8 +888,13 @@ int main(){
ctx.w = w; ctx.w = w;
ctx.h = h; ctx.h = h;
} }
}
frame_commit();
frame_commit();
wl_surface_commit(ctx.wl_surface);
if (ctx.has_cached_config){
ctx.has_cached_config = 0;
xdg_surface_ack_configure(ctx.xdg_surface, ctx.cached_config.serial); xdg_surface_ack_configure(ctx.xdg_surface, ctx.cached_config.serial);
} }
@ -978,6 +971,14 @@ decoration_type_from_window_state(enum libdecor_window_state window_state){
return(result); return(result);
} }
static void
hide_border_component(struct border_component *border_component){
if (border_component->wl_surface){
wl_surface_attach(border_component->wl_surface, 0, 0, 0);
wl_surface_commit(border_component->wl_surface);
}
}
void void
frame_commit(void){ frame_commit(void){
bool csd = false; bool csd = false;
@ -997,12 +998,8 @@ frame_commit(void){
} }
else{ else{
int csd_added_w = 0; int csd_added_w = border_size.x[0] + border_size.x[1];
int csd_added_h = 0; int csd_added_h = border_size.y[0] + border_size.y[1];
if (csd){
csd_added_w = border_size.x[0] + border_size.x[1];
csd_added_h = border_size.y[0] + border_size.y[1];
}
for (int i = 0; i < 2; i += 1){ for (int i = 0; i < 2; i += 1){
int mw = 0; int mw = 0;
int mh = 0; int mh = 0;
@ -1021,9 +1018,76 @@ frame_commit(void){
if (csd){ if (csd){
ctx.decoration_type = decoration_type_from_window_state(ctx.frame_window_state); ctx.decoration_type = decoration_type_from_window_state(ctx.frame_window_state);
draw_decoration();
bool shadow = 0;
bool title_bar = 0;
switch (ctx.decoration_type){
case DECORATION_TYPE_NONE: break;
case DECORATION_TYPE_ALL: { shadow = 1; title_bar = 1; }break;
case DECORATION_TYPE_TITLE_ONLY: { title_bar = 1; }break;
}
if (shadow){
ctx.component_slot[COMPONENT_SLOT_SHADOW].opaque = false;
ensure_component(&ctx.component_slot[COMPONENT_SLOT_SHADOW]);
draw_border_component(COMPONENT_SLOT_SHADOW);
ctx.shadow_showing = true;
}
else{
hide_border_component(&ctx.component_slot[COMPONENT_SLOT_SHADOW]);
ctx.shadow_showing = false;
}
if (title_bar){
ensure_title_bar_surfaces();
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);
}
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;
{
gtk_header_bar_set_title(GTK_HEADER_BAR(ctx.header), "");
gtk_widget_get_preferred_width(ctx.header, NULL, &pref_width);
gtk_header_bar_set_title(GTK_HEADER_BAR(ctx.header), ctx.title);
}
ctx.size_bounds.x[0] = CLAMP_BOT(ctx.size_bounds.x[0], pref_width);
if (ctx.size_bounds.x[1] != 0){
ctx.size_bounds.x[1] = CLAMP_BOT(ctx.size_bounds.x[1], ctx.size_bounds.x[0]);
}
if (ctx.w < ctx.size_bounds.x[0]){
ctx.w = ctx.size_bounds.x[0];
frame_commit();
}
else{
GtkAllocation allocation = {0, 0, ctx.w, 0};
gtk_widget_get_preferred_height(ctx.header, 0, &allocation.height);
gtk_widget_size_allocate(ctx.header, &allocation);
draw_border_component(COMPONENT_SLOT_HEADER);
}
}
else{
hide_border_component(&ctx.component_slot[COMPONENT_SLOT_HEADER]);
}
} }
else{ else{
ctx.decoration_type = DECORATION_TYPE_NONE;
g_clear_pointer(&ctx.header, gtk_widget_destroy); g_clear_pointer(&ctx.header, gtk_widget_destroy);
g_clear_pointer(&ctx.window, gtk_widget_destroy); g_clear_pointer(&ctx.window, gtk_widget_destroy);
@ -1031,29 +1095,19 @@ frame_commit(void){
struct border_component * border_component = &ctx.component_slot[i]; struct border_component * border_component = &ctx.component_slot[i];
if (border_component->wl_subsurface != 0){ if (border_component->wl_subsurface != 0){
wl_subsurface_destroy(border_component->wl_subsurface); wl_subsurface_destroy(border_component->wl_subsurface);
border_component->wl_subsurface = 0;
} }
if (border_component->wl_surface != 0){ if (border_component->wl_surface != 0){
wl_surface_destroy(border_component->wl_surface); wl_surface_destroy(border_component->wl_surface);
border_component->wl_surface = 0;
} }
if (border_component->wl_buffer != 0){ if (border_component->wl_buffer != 0){
wl_buffer_destroy(border_component->wl_buffer); wl_buffer_destroy(border_component->wl_buffer);
border_component->wl_buffer = 0;
} }
if (border_component->data != 0){ if (border_component->data != 0){
munmap(border_component->data, border_component->data_size); 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;
} }
memset(ctx.component_slot, 0, sizeof ctx.component_slot);
ctx.shadow_showing = false; ctx.shadow_showing = false;
g_clear_pointer(&ctx.title, free);
ctx.decoration_type = DECORATION_TYPE_NONE;
} }
{ {
@ -1066,7 +1120,6 @@ frame_commit(void){
extent.w += border_size.x[0] + border_size.x[1]; extent.w += border_size.x[0] + border_size.x[1];
extent.h += border_size.y[0] + border_size.y[1]; extent.h += border_size.y[0] + border_size.y[1];
} }
xdg_surface_set_window_geometry(ctx.xdg_surface, extent.x, extent.y, extent.w, extent.h); xdg_surface_set_window_geometry(ctx.xdg_surface, extent.x, extent.y, extent.w, extent.h);
} }
} }
@ -1459,14 +1512,6 @@ toggle_maximized(void){
} }
} }
static void
hide_border_component(struct border_component *border_component){
if (border_component->wl_surface){
wl_surface_attach(border_component->wl_surface, 0, 0, 0);
wl_surface_commit(border_component->wl_surface);
}
}
static enum component_slot static enum component_slot
component_slot_from_wl_surface(const struct wl_surface *surface){ component_slot_from_wl_surface(const struct wl_surface *surface){
enum component_slot result = 0; enum component_slot result = 0;
@ -1490,8 +1535,6 @@ ensure_component(struct border_component *cmpnt){
static void static void
ensure_title_bar_surfaces(void){ ensure_title_bar_surfaces(void){
GtkStyleContext *context_hdr;
ctx.component_slot[COMPONENT_SLOT_HEADER].opaque = false; ctx.component_slot[COMPONENT_SLOT_HEADER].opaque = false;
ensure_component(&ctx.component_slot[COMPONENT_SLOT_HEADER]); ensure_component(&ctx.component_slot[COMPONENT_SLOT_HEADER]);
@ -1521,7 +1564,7 @@ ensure_title_bar_surfaces(void){
"show-close-button", TRUE, "show-close-button", TRUE,
NULL); NULL);
context_hdr = gtk_widget_get_style_context(ctx.header); GtkStyleContext *context_hdr = gtk_widget_get_style_context(ctx.header);
gtk_style_context_add_class(context_hdr, GTK_STYLE_CLASS_TITLEBAR); gtk_style_context_add_class(context_hdr, GTK_STYLE_CLASS_TITLEBAR);
gtk_style_context_add_class(context_hdr, "default-decoration"); gtk_style_context_add_class(context_hdr, "default-decoration");
@ -1564,8 +1607,6 @@ extent2d_from_component_slot(enum component_slot slot){
static void static void
draw_header_button(cairo_t *cr, cairo_surface_t *surface, draw_header_button(cairo_t *cr, cairo_surface_t *surface,
enum header_element button_type){ enum header_element button_type){
GtkWidget *button;
GtkAllocation allocation; GtkAllocation allocation;
gchar *icon_name; gchar *icon_name;
@ -1574,7 +1615,8 @@ draw_header_button(cairo_t *cr, cairo_surface_t *surface,
GtkAllocation allocation_icon; GtkAllocation allocation_icon;
GtkIconInfo* icon_info; GtkIconInfo* icon_info;
double sx, sy; double sx;
double sy;
gint icon_width, icon_height; gint icon_width, icon_height;
@ -1586,12 +1628,12 @@ draw_header_button(cairo_t *cr, cairo_surface_t *surface,
GtkBorder border; GtkBorder border;
GtkBorder padding; GtkBorder padding;
button = find_widget_by_type(ctx.header, button_type); GtkWidget *button = find_widget_by_type(ctx.header, button_type);
if (button){ if (button != 0){
GtkStyleContext *button_style = gtk_widget_get_style_context(button); GtkStyleContext *button_style = gtk_widget_get_style_context(button);
GtkStateFlags style_state = 0;
/* change style based on window state and focus */ /* change style based on window state and focus */
GtkStateFlags style_state = 0;
if (!(ctx.frame_window_state & LIBDECOR_WINDOW_STATE_ACTIVE)){ if (!(ctx.frame_window_state & LIBDECOR_WINDOW_STATE_ACTIVE)){
style_state |= GTK_STATE_FLAG_BACKDROP; style_state |= GTK_STATE_FLAG_BACKDROP;
} }
@ -1622,9 +1664,9 @@ draw_header_button(cairo_t *cr, cairo_surface_t *surface,
}break; }break;
case HEADER_MAX:{ case HEADER_MAX:{
icon_name = (ctx.frame_window_state & LIBDECOR_WINDOW_STATE_MAXIMIZED) ? icon_name = ((ctx.frame_window_state & LIBDECOR_WINDOW_STATE_MAXIMIZED) ?
"window-restore-symbolic" : "window-restore-symbolic" :
"window-maximize-symbolic"; "window-maximize-symbolic");
}break; }break;
case HEADER_CLOSE: { case HEADER_CLOSE: {
@ -1638,7 +1680,7 @@ draw_header_button(cairo_t *cr, cairo_surface_t *surface,
/* get scale */ /* get scale */
cairo_surface_get_device_scale(surface, &sx, &sy); cairo_surface_get_device_scale(surface, &sx, &sy);
scale = (sx+sy) / 2.0; scale = (sx+sy)/2.0;
/* get original icon dimensions */ /* get original icon dimensions */
icon_widget = gtk_bin_get_child(GTK_BIN(button)); icon_widget = gtk_bin_get_child(GTK_BIN(button));
@ -1655,14 +1697,14 @@ draw_header_button(cairo_t *cr, cairo_surface_t *surface,
/* icon pixel buffer*/ /* icon pixel buffer*/
gtk_style_context_save(button_style); gtk_style_context_save(button_style);
gtk_style_context_set_state(button_style, style_state); gtk_style_context_set_state(button_style, style_state);
icon_pixbuf = gtk_icon_info_load_symbolic_for_context( icon_pixbuf = gtk_icon_info_load_symbolic_for_context(icon_info, button_style, 0, 0);
icon_info, button_style, NULL, NULL);
icon_surface = gdk_cairo_surface_create_from_pixbuf(icon_pixbuf, scale, NULL); icon_surface = gdk_cairo_surface_create_from_pixbuf(icon_pixbuf, scale, NULL);
gtk_style_context_restore(button_style); gtk_style_context_restore(button_style);
/* dimensions and position */ /* dimensions and position */
gtk_style_context_get(button_style, gtk_style_context_get_state(button_style), gtk_style_context_get(button_style, gtk_style_context_get_state(button_style),
"min-width", &width, "min-height", &height, NULL); "min-width", &width,
"min-height", &height, NULL);
if (width < icon_width){ if (width < icon_width){
width = icon_width; width = icon_width;
@ -1842,76 +1884,6 @@ draw_border_component(enum component_slot slot){
} }
} }
static void
draw_title_bar(void){
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);
}
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;
{
gtk_header_bar_set_title(GTK_HEADER_BAR(ctx.header), "");
gtk_widget_get_preferred_width(ctx.header, NULL, &pref_width);
gtk_header_bar_set_title(GTK_HEADER_BAR(ctx.header), ctx.title);
}
ctx.size_bounds.x[0] = CLAMP_BOT(ctx.size_bounds.x[0], pref_width);
if (ctx.size_bounds.x[1] != 0){
ctx.size_bounds.x[1] = CLAMP_BOT(ctx.size_bounds.x[1], ctx.size_bounds.x[0]);
}
if (ctx.w < ctx.size_bounds.x[0]){
ctx.w = ctx.size_bounds.x[0];
frame_commit();
}
else{
GtkAllocation allocation = {0, 0, ctx.w, 0};
gtk_widget_get_preferred_height(ctx.header, 0, &allocation.height);
gtk_widget_size_allocate(ctx.header, &allocation);
draw_border_component(COMPONENT_SLOT_HEADER);
}
}
static void
draw_decoration(void){
switch (ctx.decoration_type){
case DECORATION_TYPE_NONE: {
hide_border_component(&ctx.component_slot[COMPONENT_SLOT_SHADOW]);
ctx.shadow_showing = false;
hide_border_component(&ctx.component_slot[COMPONENT_SLOT_HEADER]);
}break;
case DECORATION_TYPE_ALL: {
ctx.component_slot[COMPONENT_SLOT_SHADOW].opaque = false;
ensure_component(&ctx.component_slot[COMPONENT_SLOT_SHADOW]);
draw_border_component(COMPONENT_SLOT_SHADOW);
ctx.shadow_showing = true;
ensure_title_bar_surfaces();
draw_title_bar();
}break;
case DECORATION_TYPE_TITLE_ONLY: {
hide_border_component(&ctx.component_slot[COMPONENT_SLOT_SHADOW]);
ctx.shadow_showing = false;
ensure_title_bar_surfaces();
draw_title_bar();
}break;
}
}
static Sides2D static Sides2D
border_size_from_window_state(enum libdecor_window_state window_state){ border_size_from_window_state(enum libdecor_window_state window_state){
Sides2D border_size = {0}; Sides2D border_size = {0};
@ -1960,19 +1932,6 @@ edge_from_pos(int x, int y){
return(result); return(result);
} }
static struct wl_cursor *
wl_cursor_from_pos(int x, int y){
struct wl_cursor *result = ctx.cursor_left_ptr;
if (ctx.active == COMPONENT_SLOT_SHADOW &&
(ctx.frame_capabilities & LIBDECOR_ACTION_RESIZE)){
enum libdecor_resize_edge edge = edge_from_pos(x, y);
if (edge != LIBDECOR_RESIZE_EDGE_NONE){
result = ctx.cursors[edge - 1];
}
}
return(result);
}
//#include "desktop-settings.c" //#include "desktop-settings.c"
static bool static bool

View File

@ -270,7 +270,6 @@ static const char *libdecor_gtk_proxy_tag = "libdecor-gtk";
static Sides2D border_size_from_window_state(enum libdecor_window_state window_state); 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); static struct wl_cursor* wl_cursor_from_pos(int x, int y);
static void draw_decoration(void);
static void draw_header_button(cairo_t *cr, cairo_surface_t *surface, enum header_element button_type); static void draw_header_button(cairo_t *cr, cairo_surface_t *surface, enum header_element button_type);
static void draw_border_component(enum component_slot slot); static void draw_border_component(enum component_slot slot);