It looks like it's just going to be getting the subsurface offsets

main
Allen Webster 2026-03-05 18:52:57 -08:00
parent d7770effea
commit 070ab2f42f
2 changed files with 212 additions and 164 deletions

View File

@ -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 = &gtk_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 = &gtk_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 */

View File

@ -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 */