From 070ab2f42f0c155e806e0677fe5cdc7eccab626e Mon Sep 17 00:00:00 2001 From: Allen Webster Date: Thu, 5 Mar 2026 18:52:57 -0800 Subject: [PATCH] It looks like it's just going to be getting the subsurface offsets --- wayland_gtk_egl.c | 370 ++++++++++++++++++++++++++-------------------- wayland_gtk_egl.h | 6 +- 2 files changed, 212 insertions(+), 164 deletions(-) diff --git a/wayland_gtk_egl.c b/wayland_gtk_egl.c index 7b22bc9..f420168 100755 --- a/wayland_gtk_egl.c +++ b/wayland_gtk_egl.c @@ -1032,39 +1032,29 @@ csd_gtk_update_and_render(void){ CSD_Window *window = &ctx.window; CSD_GTK_Window *gtk_window = &ctx.gtk_window; - if (window->control_flags & CSD_WindowControlFlag_Resize){ - static const CSD_CursorShape cursor_box[] = { - CSD_CursorShape_Resize_TopLeft, CSD_CursorShape_Resize_Top, CSD_CursorShape_Resize_TopRight, - CSD_CursorShape_Resize_Left, CSD_CursorShape_Pointer, CSD_CursorShape_Resize_Right, - CSD_CursorShape_Resize_BottomLeft, CSD_CursorShape_Resize_Bottom, CSD_CursorShape_Resize_BottomRight, - }; - - static const enum xdg_toplevel_resize_edge xedge_box[] = { - XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT, XDG_TOPLEVEL_RESIZE_EDGE_TOP, XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT, - XDG_TOPLEVEL_RESIZE_EDGE_LEFT, XDG_TOPLEVEL_RESIZE_EDGE_NONE, XDG_TOPLEVEL_RESIZE_EDGE_RIGHT, - XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT, XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM, XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT, - }; - - CSD_S32 l = (seat->hover_p[0] < 0); - CSD_S32 r = (!l && seat->hover_p[0] >= window->dim[0]); - CSD_S32 t = (seat->hover_p[1] < -window->csd_frame.border[1][0]); - CSD_S32 b = (!t && seat->hover_p[1] >= window->dim[1]); - CSD_S32 loc = 3*(b - t) + (r - l); - seat->cursor_shape = cursor_box[4 + loc]; - if (ctx.has_event && - ctx.event_button == BTN_LEFT && - ctx.event_button_state == 1){ - ctx.has_event = 0; - if (loc != 0){ - xdg_toplevel_resize(window->main_xdg_toplevel, seat->wl_seat, - seat->serial, xedge_box[4 + loc]); - } - else if (seat->hover_p[1] < 0){ - xdg_toplevel_move(window->main_xdg_toplevel, seat->wl_seat, seat->serial); - } + /* determine cursor location information */ + enum{ + LOCATION_NULL, LOCATION_SHADOW, LOCATION_TITLEBAR, + }; + CSD_U32 cursor_loc = LOCATION_NULL; + CSD_S32 cursor_shadow_loc = 0; + { + { + CSD_S32 l = (seat->hover_p[0] < 0); + CSD_S32 r = (!l && seat->hover_p[0] >= window->dim[0]); + CSD_S32 t = (seat->hover_p[1] < -window->csd_frame.border[1][0]); + CSD_S32 b = (!t && seat->hover_p[1] >= window->dim[1]); + cursor_shadow_loc = 3*(b - t) + (r - l); + } + if (cursor_shadow_loc != 0){ + cursor_loc = LOCATION_SHADOW; + } + else if (seat->hover_p[1] < 0){ + cursor_loc = LOCATION_TITLEBAR; } } + /* draw shadow */ { CSD_SubSurface *subsurface = >k_window->shadow; @@ -1108,6 +1098,26 @@ csd_gtk_update_and_render(void){ csd_subsurface_commit(subsurface); } + + /* setup buttons */ + enum{ + HEADER_BUTTON_NULL, + HEADER_BUTTON_MIN, HEADER_BUTTON_MAX, HEADER_BUTTON_CLOSE, + HEADER_BUTTON_COUNT + }; + static char* header_button_name[] = { + 0, ".minimize", ".maximize", ".close" + }; + CSD_B32 header_button_active[] = { + /*NULL*/ 0, + /*MIN*/ (window->control_flags & CSD_WindowControlFlag_Min), + /*MAX*/ ((window->control_flags & CSD_WindowControlFlag_Max) && + (window->control_flags & CSD_WindowControlFlag_Resize)), + /*CLOSE*/ (window->control_flags & CSD_WindowControlFlag_Close) + }; + CSD_U32 header_button_hover = 0; + + /* draw frame */ { CSD_SubSurface *subsurface = >k_window->titlebar; @@ -1153,7 +1163,7 @@ csd_gtk_update_and_render(void){ cairo_t *cr = cairo_create(cr_surface); cairo_surface_set_device_scale(cr_surface, 1, 1); - /* background */ + /* draw background */ { GtkAllocation allocation; gtk_widget_get_allocation(GTK_WIDGET(gtk_window->header), &allocation); @@ -1163,7 +1173,7 @@ csd_gtk_update_and_render(void){ allocation.width, allocation.height); } - /* title */ + /* draw title */ { GtkWidget *label_title = csd_gtk__widget_from_name(gtk_window->header, "label.title:"); @@ -1181,138 +1191,122 @@ csd_gtk_update_and_render(void){ cairo_surface_destroy(cr_label_surface); } - /* buttons */ - CSD_U32 buttons[3] = {0}; - int button_count = 0; - if (window->control_flags & CSD_WindowControlFlag_Min){ - buttons[button_count] = 0; - button_count += 1; - } - if ((window->control_flags & CSD_WindowControlFlag_Max) && - (window->control_flags & CSD_WindowControlFlag_Resize)){ - buttons[button_count] = 1; - button_count += 1; - } - if (window->control_flags & CSD_WindowControlFlag_Close){ - buttons[button_count] = 2; - button_count += 1; - } - - for (int i = 0; i < button_count; i += 1){ - CSD_U32 button_code = buttons[i]; - - char *button_name = ""; - char *icon_name = ""; - switch (button_code){ - case 0: { - button_name = ".minimize"; - icon_name = "window-minimize-symbolic"; - }break; - case 1: { - button_name = ".maximize"; - icon_name = ((window->config.flags & CSD_WindowFlag_IsMax) ? + /* draw buttons */ + for (CSD_U32 i = 1; i < HEADER_BUTTON_COUNT; i += 1){ + if (header_button_active[i]){ + char *icon_name = ""; + switch (i){ + case HEADER_BUTTON_MIN: { + icon_name = "window-minimize-symbolic"; + }break; + case HEADER_BUTTON_MAX: { + icon_name = ((window->config.flags & CSD_WindowFlag_IsMax) ? "window-restore-symbolic" : "window-maximize-symbolic"); - }break; - case 2: { - button_name = ".close"; - icon_name = "window-close-symbolic"; - }break; - } - - GtkWidget *button_widget = - csd_gtk__widget_from_name(gtk_window->header, button_name); - - if (button_widget != 0){ - GtkStyleContext *style = gtk_widget_get_style_context(button_widget); - - /* change style based on window state and focus */ - GtkStateFlags style_state = 0; - if (!(window->config.flags & CSD_WindowFlag_IsActivated)){ - style_state |= GTK_STATE_FLAG_BACKDROP; + }break; + case HEADER_BUTTON_CLOSE: { + icon_name = "window-close-symbolic"; + }break; } - if (gtk_window->hover_button_code == button_code){ - style_state |= GTK_STATE_FLAG_PRELIGHT; - if (gtk_window->active_button_code == button_code){ - style_state |= GTK_STATE_FLAG_ACTIVE; + + GtkWidget *widget = + csd_gtk__widget_from_name(gtk_window->header, header_button_name[i]); + + if (widget != 0){ + /* layout */ + GtkAllocation rect; + gtk_widget_get_clip(widget, &rect); + + /* check pointer hover */ + CSD_B32 is_hovered = 0; + { + CSD_S32 x0 = rect.x; + CSD_S32 y0 = rect.y - title_dim[1]; + CSD_S32 x1 = rect.x + rect.width; + CSD_S32 y1 = rect.y + rect.height - title_dim[1]; + if (x0 <= seat->hover_p[0] && seat->hover_p[0] < x1 && + y0 <= seat->hover_p[1] && seat->hover_p[1] < y1){ + header_button_hover = i; + is_hovered = 1; + } } + + /* change style based on window state and focus */ + GtkStyleContext *style = gtk_widget_get_style_context(widget); + GtkStateFlags style_state = 0; + if (!(window->config.flags & CSD_WindowFlag_IsActivated)){ + style_state |= GTK_STATE_FLAG_BACKDROP; + } + if (is_hovered){ + style_state |= GTK_STATE_FLAG_PRELIGHT; + if (i == gtk_window->active_header_button){ + style_state |= GTK_STATE_FLAG_ACTIVE; + } + } + + /* background */ + gtk_style_context_save(style); + gtk_style_context_set_state(style, style_state); + gtk_render_background(style, cr, rect.x, rect.y, rect.width, rect.height); + gtk_render_frame(style, cr, rect.x, rect.y, rect.width, rect.height); + gtk_style_context_restore(style); + + /* get scale */ + double sx, sy; + cairo_surface_get_device_scale(cr_surface, &sx, &sy); + int scale = (sx + sy)/2.0; + + /* get original icon dimensions */ + GtkWidget *icon_widget = gtk_bin_get_child(GTK_BIN(widget)); + + /* icon info */ + gint icon_width, icon_height; + if (!gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &icon_width, &icon_height)){ + icon_width = 16; + icon_height = 16; + } + GtkIconInfo* icon_info = + gtk_icon_theme_lookup_icon_for_scale(gtk_icon_theme_get_default(), + icon_name, icon_width, scale, 0); + + /* icon pixel buffer*/ + gtk_style_context_save(style); + gtk_style_context_set_state(style, style_state); + GdkPixbuf *icon_pixbuf = gtk_icon_info_load_symbolic_for_context(icon_info, style, 0, 0); + cairo_surface_t *icon_surface = gdk_cairo_surface_create_from_pixbuf(icon_pixbuf, scale, 0); + gtk_style_context_restore(style); + + /* dimensions and position */ + gint width = 0; + gint height = 0; + gtk_style_context_get(style, + gtk_style_context_get_state(style), + "min-width", &width, + "min-height", &height, NULL); + width = CSD_ClampBot(width, icon_width); + height = CSD_ClampBot(height, icon_height); + + GtkBorder border; + gtk_style_context_get_border(style, gtk_style_context_get_state(style), &border); + + GtkBorder padding; + gtk_style_context_get_padding(style, gtk_style_context_get_state(style), &padding); + + gint left = border.left + padding.left; + gint right = border.right + padding.right; + gint top = border.top + padding.top; + gint bottom = border.bottom + padding.bottom; + + width += left + right; + height += top + bottom; + + gtk_render_icon_surface(gtk_widget_get_style_context(icon_widget), + cr, icon_surface, + rect.x + (width - icon_width)/2, + rect.y + (height - icon_height)/2); + cairo_paint(cr); + cairo_surface_destroy(icon_surface); + g_object_unref(icon_pixbuf); } - - /* background */ - GtkAllocation allocation; - gtk_widget_get_clip(button_widget, &allocation); - - gtk_style_context_save(style); - gtk_style_context_set_state(style, style_state); - gtk_render_background(style, cr, allocation.x, allocation.y, - allocation.width, allocation.height); - gtk_render_frame(style, cr, allocation.x, allocation.y, - allocation.width, allocation.height); - gtk_style_context_restore(style); - - /* get scale */ - double sx, sy; - cairo_surface_get_device_scale(cr_surface, &sx, &sy); - int scale = (sx + sy)/2.0; - - /* get original icon dimensions */ - GtkWidget *icon_widget = gtk_bin_get_child(GTK_BIN(button_widget)); - - /* icon info */ - gint icon_width, icon_height; - if (!gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &icon_width, &icon_height)){ - icon_width = 16; - icon_height = 16; - } - GtkIconInfo* icon_info = - gtk_icon_theme_lookup_icon_for_scale(gtk_icon_theme_get_default(), - icon_name, icon_width, scale, 0); - - /* icon pixel buffer*/ - gtk_style_context_save(style); - gtk_style_context_set_state(style, style_state); - GdkPixbuf *icon_pixbuf = gtk_icon_info_load_symbolic_for_context(icon_info, style, 0, 0); - cairo_surface_t *icon_surface = gdk_cairo_surface_create_from_pixbuf(icon_pixbuf, scale, 0); - gtk_style_context_restore(style); - - /* dimensions and position */ - gint width = 0; - gint height = 0; - gtk_style_context_get(style, - gtk_style_context_get_state(style), - "min-width", &width, - "min-height", &height, NULL); - width = CSD_ClampBot(width, icon_width); - height = CSD_ClampBot(height, icon_height); - - gint left = 0; - gint top = 0; - gint right = 0; - gint bottom = 0; - - GtkBorder border; - gtk_style_context_get_border(style, gtk_style_context_get_state(style), &border); - left += border.left; - right += border.right; - top += border.top; - bottom += border.bottom; - - GtkBorder padding; - gtk_style_context_get_padding(style, gtk_style_context_get_state(style), &padding); - left += padding.left; - right += padding.right; - top += padding.top; - bottom += padding.bottom; - - width += left + right; - height += top + bottom; - - gtk_render_icon_surface(gtk_widget_get_style_context(icon_widget), - cr, icon_surface, - allocation.x + (width - icon_width)/2, - allocation.y + (height - icon_height)/2); - cairo_paint(cr); - cairo_surface_destroy(icon_surface); - g_object_unref(icon_pixbuf); } } @@ -1323,6 +1317,62 @@ csd_gtk_update_and_render(void){ csd_subsurface_set_position(subsurface, 0, -window->csd_frame.border[1][0]); csd_subsurface_commit(subsurface); } + + /* process events */ + if (ctx.has_event){ + + /* handle left-button press */ + if (ctx.event_button == BTN_LEFT && + ctx.event_button_state == 1){ + switch (cursor_loc){ + default: case LOCATION_NULL: break; + + case LOCATION_SHADOW: { + static const enum xdg_toplevel_resize_edge xedge_box[] = { + XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT, XDG_TOPLEVEL_RESIZE_EDGE_TOP, XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT, + XDG_TOPLEVEL_RESIZE_EDGE_LEFT, XDG_TOPLEVEL_RESIZE_EDGE_NONE, XDG_TOPLEVEL_RESIZE_EDGE_RIGHT, + XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT, XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM, XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT, + }; + xdg_toplevel_resize(window->main_xdg_toplevel, seat->wl_seat, + seat->serial, xedge_box[4 + cursor_shadow_loc]); + ctx.has_event = 0; + }break; + + case LOCATION_TITLEBAR: { + if (header_button_hover == 0){ + xdg_toplevel_move(window->main_xdg_toplevel, seat->wl_seat, seat->serial); + } + else{ + gtk_window->active_header_button = header_button_hover; + } + ctx.has_event = 0; + }break; + } + } + + /* handle left-button release */ + if (ctx.event_button == BTN_LEFT && + ctx.event_button_state == 0){ + if (gtk_window->active_header_button != 0 && + gtk_window->active_header_button == header_button_hover){ + // activate button effect here + } + gtk_window->active_header_button = 0; + } + } + + /* set cursor shape */ + if (window->control_flags & CSD_WindowControlFlag_Resize){ + static const CSD_CursorShape cursor_box[] = { + CSD_CursorShape_Resize_TopLeft, CSD_CursorShape_Resize_Top, CSD_CursorShape_Resize_TopRight, + CSD_CursorShape_Resize_Left, CSD_CursorShape_Pointer, CSD_CursorShape_Resize_Right, + CSD_CursorShape_Resize_BottomLeft, CSD_CursorShape_Resize_Bottom, CSD_CursorShape_Resize_BottomRight, + }; + seat->cursor_shape = cursor_box[4 + cursor_shadow_loc]; + } + else{ + seat->cursor_shape = CSD_CursorShape_Pointer; + } } /* cairo shadow rendering */ diff --git a/wayland_gtk_egl.h b/wayland_gtk_egl.h index 30ed4d9..b51f774 100644 --- a/wayland_gtk_egl.h +++ b/wayland_gtk_egl.h @@ -167,16 +167,14 @@ typedef struct CSD_GTK_Ctx{ } CSD_GTK_Ctx; typedef struct CSD_GTK_Window{ - CSD_S32 double_click_time_ms; - CSD_SubSurface titlebar; CSD_SubSurface shadow; GtkWidget *window; GtkWidget *header; - CSD_U32 hover_button_code; - CSD_U32 active_button_code; + CSD_S32 double_click_time_ms; + CSD_U32 active_header_button; } CSD_GTK_Window; /* csd gtk implementation */