[digesting_libdecor] simplify CSD toggling

main
Allen Webster 2026-03-02 16:42:00 -08:00
parent 59031ef536
commit 7103c61c71
2 changed files with 457 additions and 452 deletions

View File

@ -288,15 +288,7 @@ xdg_toplevel_close(void *user_data, struct xdg_toplevel *xdg_toplevel){
#ifdef XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION #ifdef XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION
static void static void
xdg_toplevel_configure_bounds(void *user_data, struct xdg_toplevel *xdg_toplevel, int32_t w, int32_t h){ xdg_toplevel_configure_bounds(void *user_data, struct xdg_toplevel *xdg_toplevel, int32_t w, int32_t h){
int32_t inner_w = w; // TODO(allen):
int32_t inner_h = h;
if (ctx.visible &&
ctx.decoration_mode == ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE){
Sides2D border_size = border_size_from_window_state(0);
inner_w -= border_size.x[0] + border_size.x[1];
inner_h -= border_size.y[0] + border_size.y[1];
}
// event to user (inner_w,inner_h)
} }
#endif #endif
@ -342,12 +334,56 @@ xdg_toplevel_decoration_configure(void *data, struct zxdg_toplevel_decoration_v1
} }
const struct zxdg_toplevel_decoration_v1_listener 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); static void ensure_component(struct border_component *cmpnt);
static void
hide_border_component(struct border_component *border_component){
if (border_component->wl_surface != 0){
wl_surface_attach(border_component->wl_surface, 0, 0, 0);
wl_surface_commit(border_component->wl_surface);
}
}
static void
border_component_reallocate(struct border_component *component, int w, int h){
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->data_size = 0;
component->width = 0;
component->height = 0;
int stride = 4*w;
int size = stride*h;
int fd = os_create_anonymous_file(size);
if (fd >= 0){
void *data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (data != MAP_FAILED){
enum wl_shm_format fmt = WL_SHM_FORMAT_ARGB8888;
struct wl_shm_pool *pool = wl_shm_create_pool(ctx.wl_shm, fd, size);
struct wl_buffer *wl_buffer = wl_shm_pool_create_buffer(pool, 0, w, h, stride, fmt);
wl_shm_pool_destroy(pool);
component->wl_buffer = wl_buffer;
component->data = data;
component->data_size = size;
component->width = w;
component->height = h;
}
close(fd);
}
}
int main(){ int main(){
/* get desktop settings */ /* get desktop settings */
ctx.color_scheme = desktop_settings_get_color_scheme(); ctx.color_scheme = desktop_settings_get_color_scheme();
@ -367,7 +403,8 @@ int main(){
g_object_set(gtk_settings_get_default(), g_object_set(gtk_settings_get_default(),
"gtk-application-prefer-dark-theme", "gtk-application-prefer-dark-theme",
(ctx.color_scheme == LIBDECOR_COLOR_SCHEME_PREFER_DARK), NULL); (ctx.color_scheme == LIBDECOR_COLOR_SCHEME_PREFER_DARK),
NULL);
if (!gtk_init_success){ if (!gtk_init_success){
printf("failed to initialize gtk\n"); printf("failed to initialize gtk\n");
@ -663,6 +700,7 @@ int main(){
} }
ctx.seat->current_cursor = cursor; 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);
@ -673,6 +711,7 @@ int main(){
wl_pointer_set_cursor(ctx.seat->wl_pointer, ctx.seat->serial, ctx.seat->cursor_surface, image->hotspot_x, image->hotspot_y); wl_pointer_set_cursor(ctx.seat->wl_pointer, ctx.seat->serial, ctx.seat->cursor_surface, image->hotspot_x, image->hotspot_y);
} }
#if 1
if (ctx.pointer_leave){ if (ctx.pointer_leave){
ctx.pointer_leave = 0; ctx.pointer_leave = 0;
ctx.titlebar_gesture.state = TITLEBAR_GESTURE_STATE_INIT; ctx.titlebar_gesture.state = TITLEBAR_GESTURE_STATE_INIT;
@ -851,38 +890,195 @@ int main(){
default: break; default: break;
} }
} }
#endif
/* apply new surface config */ /* apply new surface config */
{
bool csd = 0;
if (ctx.visible &&
ctx.decoration_mode == ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE){
csd = 1;
}
if (csd && !ctx.csd_active){
ctx.csd_active = 1;
ensure_component(&ctx.component_slot[COMPONENT_SLOT_SHADOW]);
ensure_component(&ctx.component_slot[COMPONENT_SLOT_HEADER]);
{
if (ctx.component_slot[COMPONENT_SLOT_SHADOW].wl_surface != 0){
wl_subsurface_place_above(ctx.component_slot[COMPONENT_SLOT_HEADER].wl_subsurface,
ctx.component_slot[COMPONENT_SLOT_SHADOW].wl_surface);
}
if (GTK_IS_WIDGET(ctx.header)){
gtk_widget_destroy(ctx.header);
ctx.header = 0;
}
if (GTK_IS_WIDGET(ctx.window)){
gtk_widget_destroy(ctx.window);
ctx.window = 0;
}
ctx.window = gtk_offscreen_window_new();
ctx.header = gtk_header_bar_new();
g_object_get(gtk_widget_get_settings(ctx.window),
"gtk-double-click-time", &ctx.double_click_time_ms,
NULL);
g_object_set(ctx.header,
"title", ctx.title,
"has-subtitle", FALSE,
"show-close-button", TRUE,
NULL);
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, "default-decoration");
gtk_window_set_titlebar(GTK_WINDOW(ctx.window), ctx.header);
gtk_header_bar_set_show_close_button(GTK_HEADER_BAR(ctx.header), TRUE);
gtk_window_set_resizable(GTK_WINDOW(ctx.window), (ctx.frame_capabilities & LIBDECOR_ACTION_RESIZE) != 0);
}
}
if (!csd && ctx.csd_active){
if (ctx.header != 0){
gtk_widget_destroy(ctx.header);
ctx.header = 0;
}
if (ctx.window != 0){
gtk_widget_destroy(ctx.window);
ctx.window = 0;
}
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);
}
if (border_component->wl_surface != 0){
wl_surface_destroy(border_component->wl_surface);
}
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);
}
}
memset(ctx.component_slot, 0, sizeof ctx.component_slot);
}
if (ctx.has_cached_config){ if (ctx.has_cached_config){
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;
} }
}
enum decoration_type decoration_type = DECORATION_TYPE_ALL;
if (ctx.frame_window_state & LIBDECOR_WINDOW_STATE_FULLSCREEN){
decoration_type = DECORATION_TYPE_NONE;
}
else if (ctx.frame_window_state & (LIBDECOR_WINDOW_STATE_MAXIMIZED |
LIBDECOR_WINDOW_STATE_TILED_LEFT |
LIBDECOR_WINDOW_STATE_TILED_RIGHT |
LIBDECOR_WINDOW_STATE_TILED_TOP |
LIBDECOR_WINDOW_STATE_TILED_BOTTOM)){
decoration_type = DECORATION_TYPE_TITLE_ONLY;
}
bool shadow = 0;
bool title_bar = 0;
if (csd){
switch (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){
//ensure_component(&ctx.component_slot[COMPONENT_SLOT_SHADOW]);
}
else{
hide_border_component(&ctx.component_slot[COMPONENT_SLOT_SHADOW]);
}
if (title_bar){
//ensure_component(&ctx.component_slot[COMPONENT_SLOT_HEADER]);
}
else{
hide_border_component(&ctx.component_slot[COMPONENT_SLOT_HEADER]);
}
}
if (csd){
if (title_bar){
#if 0
if (ctx.header == 0){
if (ctx.component_slot[COMPONENT_SLOT_SHADOW].wl_surface != 0){
wl_subsurface_place_above(ctx.component_slot[COMPONENT_SLOT_HEADER].wl_subsurface,
ctx.component_slot[COMPONENT_SLOT_SHADOW].wl_surface);
}
if (GTK_IS_WIDGET(ctx.header)){
gtk_widget_destroy(ctx.header);
ctx.header = 0;
}
if (GTK_IS_WIDGET(ctx.window)){
gtk_widget_destroy(ctx.window);
ctx.window = 0;
}
ctx.window = gtk_offscreen_window_new();
ctx.header = gtk_header_bar_new();
g_object_get(gtk_widget_get_settings(ctx.window),
"gtk-double-click-time", &ctx.double_click_time_ms,
NULL);
g_object_set(ctx.header,
"title", ctx.title,
"has-subtitle", FALSE,
"show-close-button", TRUE,
NULL);
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, "default-decoration");
gtk_window_set_titlebar(GTK_WINDOW(ctx.window), ctx.header);
gtk_header_bar_set_show_close_button(GTK_HEADER_BAR(ctx.header), TRUE);
gtk_window_set_resizable(GTK_WINDOW(ctx.window), (ctx.frame_capabilities & LIBDECOR_ACTION_RESIZE) != 0);
}
#endif
}
}
Sides2D csd_size = {0};
if (csd){
if (title_bar){
gtk_widget_show_all(ctx.window);
gtk_widget_get_preferred_height(ctx.header, 0, &csd_size.y[0]);
}
}
if (ctx.has_cached_config){
if (ctx.cached_config.initialized && if (ctx.cached_config.initialized &&
ctx.cached_config.window_width != 0 && ctx.cached_config.window_width != 0 &&
ctx.cached_config.window_height != 0){ ctx.cached_config.window_height != 0){
int w = ctx.cached_config.window_width; int w = ctx.cached_config.window_width;
int h = ctx.cached_config.window_height; int h = ctx.cached_config.window_height;
if (ctx.visible && ctx.decoration_mode == ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE){ if (csd){
Sides2D border_size = border_size_from_window_state(ctx.cached_config.window_state); w -= csd_size.x[0] + csd_size.x[1];
w -= border_size.x[0] + border_size.x[1]; h -= csd_size.y[0] + csd_size.y[1];
h -= border_size.y[0] + border_size.y[1];
} }
if (!(ctx.cached_config.window_state & LIBDECOR_WINDOW_STATE_NON_FLOATING)){ if (!(ctx.cached_config.window_state & LIBDECOR_WINDOW_STATE_NON_FLOATING)){
if (ctx.size_bounds.x[0] > 0){ w = CLAMP_(ctx.size_bounds.x[0], w, ctx.size_bounds.x[1]);
w = MAX(ctx.size_bounds.x[0], w); h = CLAMP_(ctx.size_bounds.y[0], h, ctx.size_bounds.y[1]);
}
if (ctx.size_bounds.x[1] > 0){
w = MIN(w, ctx.size_bounds.x[1]);
}
if (ctx.size_bounds.y[0] > 0){
h = MAX(ctx.size_bounds.y[0], h);
}
if (ctx.size_bounds.y[1] > 0){
h = MIN(h, ctx.size_bounds.y[1]);
}
} }
ctx.w = w; ctx.w = w;
@ -890,7 +1086,208 @@ int main(){
} }
} }
frame_commit(); if (csd){
if (title_bar){
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 preferred_min_width;
{
gtk_header_bar_set_title(GTK_HEADER_BAR(ctx.header), "");
gtk_widget_get_preferred_width(ctx.header, NULL, &preferred_min_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], preferred_min_width);
ctx.size_bounds.x[1] = CLAMP_BOT(ctx.size_bounds.x[1], ctx.size_bounds.x[0]);
}
}
if (!(ctx.frame_capabilities & LIBDECOR_ACTION_RESIZE)){
xdg_toplevel_set_min_size(ctx.xdg_toplevel, ctx.w, ctx.h);
xdg_toplevel_set_max_size(ctx.xdg_toplevel, ctx.w, ctx.h);
}
else{
int csd_added_w = csd_size.x[0] + csd_size.x[1];
int csd_added_h = csd_size.y[0] + csd_size.y[1];
for (int i = 0; i < 2; i += 1){
int mw = ctx.size_bounds.x[i] + csd_added_w;
int mh = ctx.size_bounds.y[i] + csd_added_h;
if (i == 0){
xdg_toplevel_set_min_size(ctx.xdg_toplevel, mw, mh);
}
else{
xdg_toplevel_set_max_size(ctx.xdg_toplevel, mw, mh);
}
}
}
if (csd){
if (shadow){
Extent2D extent = {0};
{
extent.x = -(int)SHADOW_MARGIN;
extent.y = -(int)(SHADOW_MARGIN + csd_size.y[0]);
extent.w = ctx.w + 2*SHADOW_MARGIN;
extent.h = csd_size.y[0] + ctx.h + 2*SHADOW_MARGIN;
}
struct wl_region *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.w, ctx.h);
wl_surface_set_input_region(ctx.component_slot[COMPONENT_SLOT_SHADOW].wl_surface, input_region);
wl_region_destroy(input_region);
{
enum component_slot slot = COMPONENT_SLOT_SHADOW;
struct border_component *component = &ctx.component_slot[slot];
border_component_reallocate(component, extent.w, extent.h);
memset(component->data, 0, component->data_size);
{
int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, component->width);
cairo_surface_t *surface = cairo_image_surface_create_for_data(component->data, CAIRO_FORMAT_ARGB32, component->width, component->height, stride);
cairo_t *cr = cairo_create(surface);
cairo_surface_set_device_scale(surface, 1, 1);
render_shadow(cr, ctx.shadow_blur,
-(int)SHADOW_MARGIN/2, -(int)SHADOW_MARGIN/2,
component->width + SHADOW_MARGIN, component->height + SHADOW_MARGIN,
64, 64);
cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
cairo_rectangle(cr, -extent.x, -extent.y, ctx.w, ctx.h);
cairo_fill(cr);
cairo_destroy(cr);
cairo_surface_destroy(surface);
}
wl_surface_attach(component->wl_surface, component->wl_buffer, 0, 0);
wl_surface_set_buffer_scale(component->wl_surface, 1);
wl_surface_commit(component->wl_surface);
wl_surface_damage_buffer(component->wl_surface, 0, 0, extent.w, extent.h);
wl_subsurface_set_position(component->wl_subsurface, extent.x, extent.y);
}
}
if (title_bar){
Extent2D extent = {0};
{
extent.x = 0;
extent.y = -csd_size.y[0];
extent.w = ctx.w;
extent.h = csd_size.y[0];
}
{
GtkAllocation allocation = {0, 0, ctx.w, csd_size.y[0]};
gtk_widget_size_allocate(ctx.header, &allocation);
}
{
enum component_slot slot = COMPONENT_SLOT_HEADER;
struct border_component *component = &ctx.component_slot[slot];
border_component_reallocate(component, extent.w, extent.h);
memset(component->data, 0, component->data_size);
{
int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, component->width);
cairo_surface_t *surface = cairo_image_surface_create_for_data(component->data, CAIRO_FORMAT_ARGB32, component->width, component->height, stride);
cairo_t *cr = cairo_create(surface);
cairo_surface_set_device_scale(surface, 1, 1);
{
/* background */
{
GtkAllocation allocation;
gtk_widget_get_allocation(GTK_WIDGET(ctx.header), &allocation);
GtkStyleContext* style = gtk_widget_get_style_context(ctx.header);
gtk_render_background(style, cr, allocation.x, allocation.y, allocation.width, allocation.height);
}
/* title */
{
GtkWidget *label = find_widget_by_type(ctx.header, HEADER_TITLE);
GtkAllocation allocation;
gtk_widget_get_allocation(label, &allocation);
cairo_surface_t *label_surface = cairo_surface_create_for_rectangle(surface, allocation.x, allocation.y, allocation.width, allocation.height);
cairo_t *cr2 = cairo_create(label_surface);
gtk_widget_size_allocate(label, &allocation);
gtk_widget_draw(label, cr2);
cairo_destroy(cr2);
cairo_surface_destroy(label_surface);
}
/* buttons */
{
enum header_element buttons[3] = {0};
size_t nbuttons = 0;
if ((ctx.frame_capabilities & LIBDECOR_ACTION_MINIMIZE)){
buttons[nbuttons] = HEADER_MIN;
nbuttons += 1;
}
if ((ctx.frame_capabilities & LIBDECOR_ACTION_RESIZE)){
buttons[nbuttons] = HEADER_MAX;
nbuttons += 1;
}
if ((ctx.frame_capabilities & LIBDECOR_ACTION_CLOSE)){
buttons[nbuttons] = HEADER_CLOSE;
nbuttons += 1;
}
for (int i = 0; i < nbuttons; i += 1){
enum header_element button_type = buttons[i];
draw_header_button(cr, surface, button_type);
}
}
}
cairo_destroy(cr);
cairo_surface_destroy(surface);
}
wl_surface_attach(component->wl_surface, component->wl_buffer, 0, 0);
wl_surface_set_buffer_scale(component->wl_surface, 1);
wl_surface_commit(component->wl_surface);
wl_surface_damage_buffer(component->wl_surface, 0, 0, extent.w, extent.h);
wl_subsurface_set_position(component->wl_subsurface, extent.x, extent.y);
}
}
}
{
Extent2D extent = {0};
extent.w = ctx.w;
extent.h = ctx.h;
if (csd){
extent.x = -csd_size.x[0];
extent.y = -csd_size.y[0];
extent.w += csd_size.x[0] + csd_size.x[1];
extent.h += csd_size.y[0] + csd_size.y[1];
}
xdg_surface_set_window_geometry(ctx.xdg_surface, extent.x, extent.y, extent.w, extent.h);
}
}
wl_surface_commit(ctx.wl_surface); wl_surface_commit(ctx.wl_surface);
if (ctx.has_cached_config){ if (ctx.has_cached_config){
@ -955,175 +1352,31 @@ int main(){
//#include "libdecor.c" //#include "libdecor.c"
static enum decoration_type static Extent2D
decoration_type_from_window_state(enum libdecor_window_state window_state){ extent2d_from_component_slot(enum component_slot slot, int title_height){
enum decoration_type result = DECORATION_TYPE_ALL; Extent2D result = {0};
if (window_state & LIBDECOR_WINDOW_STATE_FULLSCREEN){
result = DECORATION_TYPE_NONE; switch (slot){
} default: case COMPONENT_SLOT_NONE: break;
else if (window_state & (LIBDECOR_WINDOW_STATE_MAXIMIZED |
LIBDECOR_WINDOW_STATE_TILED_LEFT | case COMPONENT_SLOT_SHADOW: {
LIBDECOR_WINDOW_STATE_TILED_RIGHT | result.x = -(int)SHADOW_MARGIN;
LIBDECOR_WINDOW_STATE_TILED_TOP | result.y = -(int)(SHADOW_MARGIN + title_height);
LIBDECOR_WINDOW_STATE_TILED_BOTTOM)){ result.w = ctx.w + 2*SHADOW_MARGIN;
result = DECORATION_TYPE_TITLE_ONLY; result.h = title_height + ctx.h + 2*SHADOW_MARGIN;
}break;
case COMPONENT_SLOT_HEADER: {
result.x = 0;
result.y = -title_height;
result.w = ctx.w;
result.h = title_height;
}break;
} }
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
frame_commit(void){
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)){
xdg_toplevel_set_min_size(ctx.xdg_toplevel, ctx.w, ctx.h);
xdg_toplevel_set_max_size(ctx.xdg_toplevel, ctx.w, ctx.h);
}
else{
int csd_added_w = border_size.x[0] + border_size.x[1];
int csd_added_h = border_size.y[0] + border_size.y[1];
for (int i = 0; i < 2; i += 1){
int mw = 0;
int mh = 0;
if (ctx.size_bounds.x[i] > 0 && ctx.size_bounds.y[i] > 0){
mw = ctx.size_bounds.x[i] + csd_added_w;
mh = ctx.size_bounds.y[i] + csd_added_h;
}
if (i == 0){
xdg_toplevel_set_min_size(ctx.xdg_toplevel, mw, mh);
}
else{
xdg_toplevel_set_max_size(ctx.xdg_toplevel, mw, mh);
}
}
}
if (csd){
ctx.decoration_type = decoration_type_from_window_state(ctx.frame_window_state);
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{
ctx.decoration_type = DECORATION_TYPE_NONE;
g_clear_pointer(&ctx.header, gtk_widget_destroy);
g_clear_pointer(&ctx.window, gtk_widget_destroy);
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);
}
if (border_component->wl_surface != 0){
wl_surface_destroy(border_component->wl_surface);
}
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);
}
}
memset(ctx.component_slot, 0, sizeof ctx.component_slot);
ctx.shadow_showing = false;
}
{
Extent2D extent = {0};
extent.w = ctx.w;
extent.h = ctx.h;
if (csd){
extent.x = -border_size.x[0];
extent.y = -border_size.y[0];
extent.w += border_size.x[0] + border_size.x[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);
}
}
//#include "libdecor-cairo-blur.c" //#include "libdecor-cairo-blur.c"
int int
blur_surface(cairo_surface_t *surface, int margin){ blur_surface(cairo_surface_t *surface, int margin){
@ -1533,77 +1786,6 @@ ensure_component(struct border_component *cmpnt){
} }
} }
static void
ensure_title_bar_surfaces(void){
ctx.component_slot[COMPONENT_SLOT_HEADER].opaque = false;
ensure_component(&ctx.component_slot[COMPONENT_SLOT_HEADER]);
if (ctx.component_slot[COMPONENT_SLOT_SHADOW].wl_surface){
wl_subsurface_place_above(ctx.component_slot[COMPONENT_SLOT_HEADER].wl_subsurface,
ctx.component_slot[COMPONENT_SLOT_SHADOW].wl_surface);
}
if (GTK_IS_WIDGET(ctx.header)){
gtk_widget_destroy(ctx.header);
ctx.header = 0;
}
if (GTK_IS_WIDGET(ctx.window)){
gtk_widget_destroy(ctx.window);
ctx.window = 0;
}
ctx.window = gtk_offscreen_window_new();
ctx.header = gtk_header_bar_new();
g_object_get(gtk_widget_get_settings(ctx.window),
"gtk-double-click-time", &ctx.double_click_time_ms,
NULL);
g_object_set(ctx.header,
"title", ctx.title,
"has-subtitle", FALSE,
"show-close-button", TRUE,
NULL);
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, "default-decoration");
gtk_window_set_titlebar(GTK_WINDOW(ctx.window), ctx.header);
gtk_header_bar_set_show_close_button(GTK_HEADER_BAR(ctx.header), TRUE);
gtk_window_set_resizable(GTK_WINDOW(ctx.window), (ctx.frame_capabilities & LIBDECOR_ACTION_RESIZE) != 0);
}
static Extent2D
extent2d_from_component_slot(enum component_slot slot){
Extent2D result = {0};
int title_height = 0;
if (GTK_IS_WIDGET(ctx.header)){
title_height = gtk_widget_get_allocated_height(ctx.header);
}
switch (slot){
default: case COMPONENT_SLOT_NONE: break;
case COMPONENT_SLOT_SHADOW: {
result.x = -(int)SHADOW_MARGIN;
result.y = -(int)(SHADOW_MARGIN + title_height);
result.w = ctx.w + 2*SHADOW_MARGIN;
result.h = title_height + ctx.h + 2*SHADOW_MARGIN;
}break;
case COMPONENT_SLOT_HEADER: {
result.x = 0;
result.y = -title_height;
result.w = gtk_widget_get_allocated_width(ctx.header);
result.h = title_height;
}break;
}
return(result);
}
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){
@ -1738,174 +1920,6 @@ draw_header_button(cairo_t *cr, cairo_surface_t *surface,
} }
} }
static void
draw_border_component(enum component_slot slot){
if (slot < COMPONENT_SLOT_COUNT &&
ctx.component_slot[slot].wl_surface != 0){
struct border_component *component = &ctx.component_slot[slot];
if (slot == COMPONENT_SLOT_SHADOW && ctx.shadow_showing){
struct wl_region *input_region;
Extent2D extent = extent2d_from_component_slot(COMPONENT_SLOT_SHADOW);
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.w, ctx.h);
wl_surface_set_input_region(component->wl_surface, input_region);
wl_region_destroy(input_region);
}
{
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->data_size = 0;
component->width = 0;
component->height = 0;
}
Extent2D extent = extent2d_from_component_slot(slot);
{
int width = extent.w;
int height = extent.h;
int stride = 4*width;
int size = stride*height;
int fd = os_create_anonymous_file(size);
if (fd >= 0){
void *data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (data != MAP_FAILED){
enum wl_shm_format fmt = (ctx.component_slot[slot].opaque ?
WL_SHM_FORMAT_XRGB8888 :
WL_SHM_FORMAT_ARGB8888);
struct wl_shm_pool *pool = wl_shm_create_pool(ctx.wl_shm, fd, size);
struct wl_buffer *wl_buffer = wl_shm_pool_create_buffer(pool, 0, width, height, stride, fmt);
wl_shm_pool_destroy(pool);
component->wl_buffer = wl_buffer;
component->data = data;
component->data_size = size;
component->width = width;
component->height = height;
}
close(fd);
}
}
memset(component->data, 0, component->data_size);
{
int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, component->width);
cairo_surface_t *surface = cairo_image_surface_create_for_data(component->data, CAIRO_FORMAT_ARGB32, component->width, component->height, stride);
cairo_t *cr = cairo_create(surface);
cairo_surface_set_device_scale(surface, 1, 1);
switch (slot){
default:break;
case COMPONENT_SLOT_SHADOW: {
render_shadow(cr, ctx.shadow_blur,
-(int)SHADOW_MARGIN/2, -(int)SHADOW_MARGIN/2,
component->width + SHADOW_MARGIN, component->height + SHADOW_MARGIN,
64, 64);
cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
cairo_rectangle(cr, -extent.x, -extent.y, ctx.w, ctx.h);
cairo_fill(cr);
}break;
case COMPONENT_SLOT_HEADER: {
/* background */
{
GtkAllocation allocation;
gtk_widget_get_allocation(GTK_WIDGET(ctx.header), &allocation);
GtkStyleContext* style = gtk_widget_get_style_context(ctx.header);
gtk_render_background(style, cr, allocation.x, allocation.y, allocation.width, allocation.height);
}
/* title */
{
GtkWidget *label = find_widget_by_type(ctx.header, HEADER_TITLE);
GtkAllocation allocation;
gtk_widget_get_allocation(label, &allocation);
cairo_surface_t *label_surface = cairo_surface_create_for_rectangle(surface, allocation.x, allocation.y, allocation.width, allocation.height);
cairo_t *cr2 = cairo_create(label_surface);
gtk_widget_size_allocate(label, &allocation);
gtk_widget_draw(label, cr2);
cairo_destroy(cr2);
cairo_surface_destroy(label_surface);
}
/* buttons */
{
enum header_element buttons[3] = {0};
size_t nbuttons = 0;
if ((ctx.frame_capabilities & LIBDECOR_ACTION_MINIMIZE)){
buttons[nbuttons] = HEADER_MIN;
nbuttons += 1;
}
if ((ctx.frame_capabilities & LIBDECOR_ACTION_RESIZE)){
buttons[nbuttons] = HEADER_MAX;
nbuttons += 1;
}
if ((ctx.frame_capabilities & LIBDECOR_ACTION_CLOSE)){
buttons[nbuttons] = HEADER_CLOSE;
nbuttons += 1;
}
for (int i = 0; i < nbuttons; i += 1){
enum header_element button_type = buttons[i];
draw_header_button(cr, surface, button_type);
}
}
}break;
}
cairo_destroy(cr);
cairo_surface_destroy(surface);
}
struct wl_surface *component_surface = ctx.component_slot[slot].wl_surface;
struct wl_subsurface *component_subsurface = ctx.component_slot[slot].wl_subsurface;
wl_surface_attach(component_surface, component->wl_buffer, 0, 0);
wl_surface_set_buffer_scale(component_surface, 1);
wl_surface_commit(component_surface);
wl_surface_damage_buffer(component_surface, 0, 0, extent.w, extent.h);
wl_subsurface_set_position(component_subsurface, extent.x, extent.y);
}
}
static Sides2D
border_size_from_window_state(enum libdecor_window_state window_state){
Sides2D border_size = {0};
switch (decoration_type_from_window_state(window_state)) {
case DECORATION_TYPE_NONE: break;
case DECORATION_TYPE_ALL: {
ctx.component_slot[COMPONENT_SLOT_SHADOW].opaque = false;
ensure_component(&ctx.component_slot[COMPONENT_SLOT_SHADOW]);
} G_GNUC_FALLTHROUGH;
case DECORATION_TYPE_TITLE_ONLY: {
if (ctx.header == 0){
ensure_title_bar_surfaces();
}
gtk_widget_show_all(ctx.window);
gtk_widget_get_preferred_height(ctx.header, 0, &border_size.y[0]);
}break;
}
return(border_size);
}
enum libdecor_resize_edge enum libdecor_resize_edge
edge_from_pos(int x, int y){ edge_from_pos(int x, int y){
static const enum libdecor_resize_edge box[9] = { static const enum libdecor_resize_edge box[9] = {

View File

@ -47,6 +47,7 @@
#define CLAMP_BOT(a, b) MAX(a, b) #define CLAMP_BOT(a, b) MAX(a, b)
#define CLAMP_TOP(a, b) MIN(a, b) #define CLAMP_TOP(a, b) MIN(a, b)
#define CLAMP_(a, x, b) CLAMP_BOT(a, CLAMP_TOP(x, b))
#define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0]) #define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0])
@ -167,7 +168,6 @@ enum component_slot {
struct border_component { struct border_component {
struct wl_surface *wl_surface; struct wl_surface *wl_surface;
struct wl_subsurface *wl_subsurface; struct wl_subsurface *wl_subsurface;
bool opaque;
struct wl_buffer *wl_buffer; struct wl_buffer *wl_buffer;
void *data; void *data;
@ -240,10 +240,6 @@ struct seat{
struct wl_list link; struct wl_list link;
}; };
// libdecor.h
void frame_commit(void);
// #include "libdecor-cairo-blur.h" // #include "libdecor-cairo-blur.h"
int blur_surface(cairo_surface_t *surface, int margin); int blur_surface(cairo_surface_t *surface, int margin);
void render_shadow(cairo_t *cr, cairo_surface_t *surface, void render_shadow(cairo_t *cr, cairo_surface_t *surface,
@ -267,11 +263,8 @@ static void do_map(void);
static const char *libdecor_gtk_proxy_tag = "libdecor-gtk"; static const char *libdecor_gtk_proxy_tag = "libdecor-gtk";
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_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 bool own_proxy(void *proxy); static bool own_proxy(void *proxy);
static enum component_slot component_slot_from_wl_surface(const struct wl_surface *surface); static enum component_slot component_slot_from_wl_surface(const struct wl_surface *surface);
@ -354,14 +347,12 @@ typedef struct Ctx{
bool visible; bool visible;
//struct libdecor_frame_gtk; //struct libdecor_frame_gtk;
enum decoration_type decoration_type;
enum component_slot active; enum component_slot active;
enum component_slot focus; enum component_slot focus;
enum component_slot grab; enum component_slot grab;
struct border_component component_slot[COMPONENT_SLOT_COUNT];
bool shadow_showing; bool csd_active;
struct border_component component_slot[COMPONENT_SLOT_COUNT];
GtkWidget *window; GtkWidget *window;
GtkWidget *header; GtkWidget *header;
struct header_element_data hdr_focus; struct header_element_data hdr_focus;