From 7aad2af60938a57d6cc189f070d25b4b024e245e Mon Sep 17 00:00:00 2001 From: IoIxD Date: Wed, 20 May 2026 13:45:35 -0700 Subject: [PATCH] wayland: merge libdecor --- include/Mw/LowLevel/Wayland.h | 70 +++++++++- src/backend/wayland/buffer.c | 2 +- src/backend/wayland/wayland.c | 254 ++++++++++++++++++---------------- 3 files changed, 202 insertions(+), 124 deletions(-) diff --git a/include/Mw/LowLevel/Wayland.h b/include/Mw/LowLevel/Wayland.h index 02ef26e..575c867 100644 --- a/include/Mw/LowLevel/Wayland.h +++ b/include/Mw/LowLevel/Wayland.h @@ -14,7 +14,13 @@ #include #include #include -#include +/* libdecor is included later on so that it's covered by the wayland dynloading. */ +struct libdecor_frame; +struct libdecor_interface; +struct libdecor_configuration; +struct libdecor_frame_interface; +enum libdecor_window_state; +struct wl_surface; #ifdef __cplusplus extern "C" { @@ -44,6 +50,8 @@ typedef struct wayland_call_table { void* lib; void* xkb_lib; void* cairo_lib; + + void* libdecor_lib; #ifdef USE_DBUS MwLLDBusFuncTable dbus; MwBool has_dbus; @@ -141,6 +149,20 @@ typedef struct wayland_call_table { void (*cairo_set_antialias)(cairo_t*, cairo_antialias_t); void (*cairo_set_operator)(cairo_t* cr, cairo_operator_t op); + MwBool has_libdecor; + struct libdecor* (*libdecor_new)(struct wl_display* display, struct libdecor_interface* iface); + struct libdecor_state* (*libdecor_state_new)(int width, int height); + void (*libdecor_frame_commit)(struct libdecor_frame* frame, struct libdecor_state* state, struct libdecor_configuration* configuration); + void (*libdecor_state_free)(struct libdecor_state* state); + struct libdecor_frame* (*libdecor_decorate)(struct libdecor* context, struct wl_surface* surface, struct libdecor_frame_interface* iface, void* user_data); + void (*libdecor_frame_set_app_id)(struct libdecor_frame* frame, const char* app_id); + void (*libdecor_frame_set_title)(struct libdecor_frame* frame, const char* title); + void (*libdecor_frame_map)(struct libdecor_frame* frame); + MwBool (*libdecor_configuration_get_content_size)(struct libdecor_configuration* configuration, struct libdecor_frame* frame, int* width, int* height); + MwBool (*libdecor_configuration_get_window_state)(struct libdecor_configuration* configuration, enum libdecor_window_state* window_state); + struct xdg_surface* (*libdecor_frame_get_xdg_surface)(struct libdecor_frame* frame); + int (*libdecor_dispatch)(struct libdecor* context, int timeout); + void (*libdecor_frame_translate_coordinate)(struct libdecor_frame* frame, int surface_x, int surface_y, int* frame_x, int* frame_y); } wayland_call_table_t; extern wayland_call_table_t wl_call_tbl; @@ -186,6 +208,17 @@ MwInline int wayland_load_funcs() { return 1; \ }; + /* i mean i pray if somebody is using thr wayland backend over apple/ios that actual decorations are supported. */ +#ifdef __APPLE__ + wl_call_tbl.libdecor_lib = MwDynamicOpen("libdecor-0.dylib"); +#else + wl_call_tbl.libdecor_lib = MwDynamicOpen("libdecor-0.so"); +#endif + wl_call_tbl.has_libdecor = (wl_call_tbl.libdecor_lib != NULL); + if(!wl_call_tbl.libdecor_lib) { + printf("(libdecor not found, window will not have any decorations.)\n"); + } + WAYLAND_FUNC(wl_display_dispatch) WAYLAND_FUNC(wl_display_dispatch_pending) WAYLAND_FUNC(wl_display_disconnect) @@ -267,6 +300,28 @@ MwInline int wayland_load_funcs() { wl_call_tbl.has_dbus = MwLLDBusFuncSetup(&wl_call_tbl.dbus); #endif + if(wl_call_tbl.has_libdecor) { +#define LIBDECOR_FUNC(x) \ + wl_call_tbl.x = MwDynamicSymbol(wl_call_tbl.libdecor_lib, #x); \ + if(!wl_call_tbl.x) { \ + printf("WARNING: Can't use libdecor because libdecor-1 doesn't export " #x ".\n"); \ + wl_call_tbl.has_libdecor = MwFALSE; \ + }; + + LIBDECOR_FUNC(libdecor_new); + LIBDECOR_FUNC(libdecor_state_new); + LIBDECOR_FUNC(libdecor_frame_commit); + LIBDECOR_FUNC(libdecor_state_free); + LIBDECOR_FUNC(libdecor_decorate); + LIBDECOR_FUNC(libdecor_frame_set_app_id); + LIBDECOR_FUNC(libdecor_frame_set_title); + LIBDECOR_FUNC(libdecor_frame_map); + LIBDECOR_FUNC(libdecor_configuration_get_content_size); + LIBDECOR_FUNC(libdecor_configuration_get_window_state); + LIBDECOR_FUNC(libdecor_frame_get_xdg_surface); + LIBDECOR_FUNC(libdecor_dispatch); + } + return 0; } @@ -342,6 +397,8 @@ MwInline int wayland_load_funcs() { #include "Wayland/xdg-toplevel-icon-client-protocol.h" #endif +#include + typedef struct wayland_protocol { void* listener; void* context; @@ -495,6 +552,11 @@ struct _MwLLWayland { MwLLDBusContext dbus; #endif + struct libdecor* decor; + struct libdecor_frame* decor_frame; + struct libdecor_interface* decor_interface; + enum libdecor_window_state decor_state; + MwBool dark_theme_detection; MwU32 dark_theme; @@ -568,8 +630,6 @@ struct _MwLLWayland { cairo_surface_t* back_cs; cairo_t* front_cairo; cairo_t* back_cairo; - /* The cairo to actually use for draw operations. Typically is front_cairo, but MwLLBeginDraw can change this to the back_cairo so it can be used to draw window decorations. */ - cairo_t* selected_cairo; }; struct _MwLLWaylandColor { @@ -610,10 +670,10 @@ void MwLLWaylandClipboardRead(wl_clipboard_device_context_t* ctx, int clipboard_ void MwLLWaylandFlush(MwLL handle); /* Standard procedure before event callbacks in Wayland */ -#define WAYLAND_EVENT_OP_START(self) pthread_mutex_lock(&self->wayland.eventsMutex); +#define WAYLAND_EVENT_OP_START(self) /* Footer for WAYLAND_EVENT_OP_START */ -#define WAYLAND_EVENT_OP_END(self) pthread_mutex_unlock(&self->wayland.eventsMutex); +#define WAYLAND_EVENT_OP_END(self) /* the two decoration manager constructs */ typedef struct zxdg_decoration_manager_v1_context { diff --git a/src/backend/wayland/buffer.c b/src/backend/wayland/buffer.c index a37fd34..69c375c 100644 --- a/src/backend/wayland/buffer.c +++ b/src/backend/wayland/buffer.c @@ -12,7 +12,7 @@ void MwLLWaylandFramebufferSetup(struct _MwLLWayland* wayland) { wl_surface_attach(wayland->framebuffer.surface, wayland->framebuffer.shm_buffer, 0, 0); wl_surface_commit(wayland->framebuffer.surface); - MwLLWaylandHangUntilConfigured((MwLL)wayland); + // MwLLWaylandHangUntilConfigured((MwLL)wayland); MwLLWaylandBufferUpdate((MwLL)wayland, &wayland->framebuffer); }; void MwLLWaylandFramebufferDestroy(struct _MwLLWayland* wayland) { diff --git a/src/backend/wayland/wayland.c b/src/backend/wayland/wayland.c index 8625c60..93d0686 100644 --- a/src/backend/wayland/wayland.c +++ b/src/backend/wayland/wayland.c @@ -201,6 +201,96 @@ void MwLLWaylandBufferUpdate(MwLL self, struct _MwLLWaylandShmBuffer* buffer) { } } +/* shitdecor */ +static void libdecor_error(struct libdecor* context, + enum libdecor_error error, + const char* message) { + printf("libdecor error (%d): %s\n", error, message); +} + +static void +handle_configure(struct libdecor_frame* frame, + struct libdecor_configuration* configuration, + void* user_data) { + MwLL self = user_data; + int width, height; + enum libdecor_window_state window_state; + struct libdecor_state* state; + + if(!wl_call_tbl.libdecor_configuration_get_content_size(configuration, frame, + &width, &height)) { + width = self->wayland.ww; + height = self->wayland.wh; + } + + width = (width == 0) ? 1 : width; + height = (height == 0) ? 1 : height; + + // region_invalidate(self); + self->wayland.ww = width; + self->wayland.wh = height; + + if(self->wayland.resizing == 0) { + MwLLWaylandFramebufferDestroy(&self->wayland); + MwLLWaylandFramebufferSetup(&self->wayland); + MwLLWaylandRegionSetup(self); + MwLLDispatch(self, resize, NULL); + + recursive_dispatch_resize(self); + + if(self->wayland.pointer_constrained) { + zwp_locked_pointer_v1_set_cursor_position_hint(self->wayland.locked_pointer, 0, CSD_BORDER_FRAME_TOP); + wl_surface_commit(self->wayland.framebuffer.surface); + } + + self->wayland.resizing = 1; + } + + MwLLDispatch(self, draw, NULL); + + if(!wl_call_tbl.libdecor_configuration_get_window_state(configuration, + &window_state)) + window_state = LIBDECOR_WINDOW_STATE_NONE; + + self->wayland.decor_state = window_state; + + state = wl_call_tbl.libdecor_state_new(width, height); + wl_call_tbl.libdecor_frame_commit(frame, state, configuration); + wl_call_tbl.libdecor_state_free(state); + + self->wayland.configured = MwTRUE; +} + +static void +handle_close(struct libdecor_frame* frame, + void* user_data) { + MwLL self = user_data; + MwLLDispatch(self, close, NULL); +} + +static void +handle_commit(struct libdecor_frame* frame, + void* user_data) { + MwLL self = user_data; + wl_surface_commit(self->wayland.framebuffer.surface); +} + +static void +handle_dismiss_popup(struct libdecor_frame* frame, + const char* seat_name, + void* user_data) { +} + +static struct libdecor_frame_interface libdecor_frame_iface = { + handle_configure, + handle_close, + handle_commit, + handle_dismiss_popup, +}; + +static struct libdecor_interface libdecor_interface = { + libdecor_error}; + /* Toplevel setup function */ static void setup_toplevel(MwLL r, int x, int y) { r->wayland.type = MwLL_WAYLAND_TOPLEVEL; @@ -234,15 +324,14 @@ static void setup_toplevel(MwLL r, int x, int y) { r->wayland.xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); - /* Create a wl_surface, a xdg_surface and a xdg_toplevel */ r->wayland.framebuffer.surface = wl_compositor_create_surface(r->wayland.compositor); r->wayland.backbuffer.surface = wl_compositor_create_surface(r->wayland.compositor); - r->wayland.toplevel->ssurface = wl_subcompositor_get_subsurface(r->wayland.toplevel->scompositor, r->wayland.framebuffer.surface, r->wayland.backbuffer.surface); - wl_subsurface_set_desync(r->wayland.toplevel->ssurface); - r->wayland.toplevel->xdg_surface = - xdg_wm_base_get_xdg_surface(WAYLAND_GET_INTERFACE(r->wayland, xdg_wm_base)->context, r->wayland.backbuffer.surface); - r->wayland.toplevel->xdg_top_level = xdg_surface_get_toplevel(r->wayland.toplevel->xdg_surface); + if(r->wayland.has_decorations) { + r->wayland.toplevel->xdg_surface = + xdg_wm_base_get_xdg_surface(WAYLAND_GET_INTERFACE(r->wayland, xdg_wm_base)->context, r->wayland.framebuffer.surface); + r->wayland.toplevel->xdg_top_level = xdg_surface_get_toplevel(r->wayland.toplevel->xdg_surface); + } /* setup mandatory listeners */ r->wayland.toplevel->xdg_surface_listener.configure = xdg_surface_configure; @@ -250,16 +339,26 @@ static void setup_toplevel(MwLL r, int x, int y) { r->wayland.toplevel->xdg_toplevel_listener.configure = xdg_toplevel_configure; r->wayland.toplevel->xdg_toplevel_listener.close = xdg_toplevel_close; - xdg_surface_add_listener(r->wayland.toplevel->xdg_surface, &r->wayland.toplevel->xdg_surface_listener, r); - xdg_toplevel_add_listener(r->wayland.toplevel->xdg_top_level, &r->wayland.toplevel->xdg_toplevel_listener, - r); + if(r->wayland.has_decorations) { + xdg_surface_add_listener(r->wayland.toplevel->xdg_surface, &r->wayland.toplevel->xdg_surface_listener, r); + xdg_toplevel_add_listener(r->wayland.toplevel->xdg_top_level, &r->wayland.toplevel->xdg_toplevel_listener, + r); - xdg_toplevel_set_title(r->wayland.toplevel->xdg_top_level, "Milsko App"); - xdg_toplevel_set_app_id(r->wayland.toplevel->xdg_top_level, "MilskoWaylandApp"); + xdg_toplevel_set_title(r->wayland.toplevel->xdg_top_level, "Milsko App"); + xdg_toplevel_set_app_id(r->wayland.toplevel->xdg_top_level, "MilskoWaylandApp"); + } else { + r->wayland.decor = wl_call_tbl.libdecor_new(r->wayland.display, &libdecor_interface); + r->wayland.decor_frame = wl_call_tbl.libdecor_decorate(r->wayland.decor, r->wayland.framebuffer.surface, + &libdecor_frame_iface, r); + wl_call_tbl.libdecor_frame_set_app_id(r->wayland.decor_frame, "milsko-app"); + wl_call_tbl.libdecor_frame_set_title(r->wayland.decor_frame, "Milsko App"); + wl_call_tbl.libdecor_frame_map(r->wayland.decor_frame); + } /* Perform the initial commit and wait for the first configure event */ - wl_surface_commit(r->wayland.backbuffer.surface); wl_surface_commit(r->wayland.framebuffer.surface); + wl_surface_commit(r->wayland.backbuffer.surface); + while(!r->wayland.configured) { event_loop(r); } @@ -273,8 +372,6 @@ static void setup_toplevel(MwLL r, int x, int y) { zxdg_toplevel_decoration_v1_set_mode( dec->decoration, ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); - } else { - wl_subsurface_set_position(r->wayland.toplevel->ssurface, CSD_BORDER_FRAME_LEFT, CSD_BORDER_FRAME_TOP); } } @@ -406,6 +503,7 @@ struct xdg_popup_listener popup_listener = { /* Popup setup function */ static void setup_popup(MwLL r, int x, int y, MwLL parent) { struct xdg_surface* xdg_surface; + struct xdg_surface* parent_xdg_surface; MwLL topmost_parent = r->wayland.parent; r->wayland.type = MwLL_WAYLAND_POPUP; @@ -437,7 +535,7 @@ static void setup_popup(MwLL r, int x, int y, MwLL parent) { /* check now if we have decorations so that everything else can act accordingly */ if(shget(r->wayland.wl_protocol_map, zxdg_decoration_manager_v1_interface.name) != NULL) { - r->wayland.has_decorations = MwTRUE; + // r->wayland.has_decorations = MwTRUE; } r->wayland.popup->xdg_wm_base = WAYLAND_GET_INTERFACE(r->wayland, xdg_wm_base)->context; @@ -455,16 +553,20 @@ static void setup_popup(MwLL r, int x, int y, MwLL parent) { r->wayland.framebuffer.surface = wl_compositor_create_surface(r->wayland.compositor); + if(r->wayland.has_decorations) { + parent_xdg_surface = topmost_parent->wayland.toplevel->xdg_surface; + } else { + parent_xdg_surface = wl_call_tbl.libdecor_frame_get_xdg_surface(topmost_parent->wayland.decor_frame); + } + r->wayland.popup->xdg_surface = xdg_wm_base_get_xdg_surface(WAYLAND_GET_INTERFACE(r->wayland, xdg_wm_base)->context, r->wayland.framebuffer.surface); r->wayland.popup->xdg_popup = xdg_surface_get_popup( r->wayland.popup->xdg_surface, - xdg_surface, + parent_xdg_surface, r->wayland.popup->xdg_positioner); - uint32_t ver = wl_proxy_get_version((struct wl_proxy*)r->wayland.popup->xdg_popup); - xdg_popup_add_listener(r->wayland.popup->xdg_popup, &popup_listener, r); r->wayland.popup->xdg_surface_listener.configure = xdg_surface_configure; @@ -783,7 +885,11 @@ static void actually_set_wh(MwLL handle) { MwLLWaylandRegionInvalidate(handle); if(handle->wayland.type == MwLL_WAYLAND_TOPLEVEL && handle->wayland.configured) { - xdg_surface_set_window_geometry(handle->wayland.toplevel->xdg_surface, 0, 0, handle->wayland.ww, handle->wayland.wh); + if(handle->wayland.has_decorations) { + xdg_surface_set_window_geometry(handle->wayland.toplevel->xdg_surface, 0, 0, handle->wayland.ww, handle->wayland.wh); + } else { + xdg_surface_set_window_geometry(wl_call_tbl.libdecor_frame_get_xdg_surface(handle->wayland.decor_frame), 0, 0, handle->wayland.ww, handle->wayland.wh); + } } if(handle->wayland.type == MwLL_WAYLAND_SUBLEVEL && handle->wayland.configured && !handle->wayland.dispatching_resize) { @@ -831,101 +937,6 @@ static void MwLLSetWHImpl(MwLL handle, int w, int h) { } static void MwLLBeginDrawImpl(MwLL handle) { - cairo_save(handle->wayland.front_cairo); - cairo_set_source_rgba(handle->wayland.front_cairo, 0, 0, 0, 0); - cairo_set_operator(handle->wayland.front_cairo, CAIRO_OPERATOR_SOURCE); - cairo_rectangle(handle->wayland.front_cairo, 0, 0, handle->wayland.ww, handle->wayland.wh); - cairo_fill(handle->wayland.front_cairo); - cairo_restore(handle->wayland.front_cairo); - - if(handle->wayland.type != MwLL_WAYLAND_TOPLEVEL) { - handle->wayland.selected_cairo = handle->wayland.front_cairo; - return; - } - - if(!handle->wayland.has_decorations) { - int x; - MwU32 w = handle->wayland.ww + CSD_BORDER_FRAME_LEFT + CSD_BORDER_FRAME_RIGHT; - MwU32 h = handle->wayland.wh + CSD_BORDER_FRAME_TOP + CSD_BORDER_FRAME_BOTTOM; - cairo_set_line_width(handle->wayland.back_cairo, 4.0); - for(x = 0; x < w; x++) { - float placeholder = (float)x / (float)w; - cairo_set_source_rgb(handle->wayland.back_cairo, placeholder, 0.25, 0.25); - - cairo_move_to(handle->wayland.back_cairo, w - x, 0); - cairo_line_to(handle->wayland.back_cairo, w - x, CSD_BORDER_FRAME_TOP); - cairo_stroke(handle->wayland.back_cairo); - - cairo_move_to(handle->wayland.back_cairo, x, CSD_BORDER_FRAME_TOP); - cairo_line_to(handle->wayland.back_cairo, x, h); - cairo_stroke(handle->wayland.back_cairo); - } - cairo_set_source_rgba(handle->wayland.back_cairo, 1, 1, 1, 0.5); - cairo_move_to(handle->wayland.back_cairo, 0, 0); - cairo_line_to(handle->wayland.back_cairo, 0, h); - cairo_stroke(handle->wayland.back_cairo); - cairo_move_to(handle->wayland.back_cairo, 0, 0); - cairo_line_to(handle->wayland.back_cairo, w, 0); - cairo_stroke(handle->wayland.back_cairo); - - cairo_set_source_rgba(handle->wayland.back_cairo, 0, 0, 0, 0.5); - cairo_move_to(handle->wayland.back_cairo, 0, h); - cairo_line_to(handle->wayland.back_cairo, w, h); - cairo_stroke(handle->wayland.back_cairo); - cairo_move_to(handle->wayland.back_cairo, w, 0); - cairo_line_to(handle->wayland.back_cairo, w, h); - cairo_stroke(handle->wayland.back_cairo); - - if(strlen(handle->wayland.title) != 0) { - int y, x; - int i = 0, sx = 0, sy = 0; - int tw, th; - unsigned char* px; - MwRect r; - MwLLPixmap p; - tw = strlen(handle->wayland.title) * 7; - th = 14; - px = malloc(tw * th * 4); - assert(px); - - memset(px, 0, tw * th * 4); - - handle->wayland.selected_cairo = handle->wayland.back_cairo; - - while(handle->wayland.title[i]) { - int out; - i += MwUTF8ToUTF32(handle->wayland.title + i, &out); - - if(out > 0xff) { - out = 0; - } - - for(y = 0; y < 14; y++) { - for(x = 0; x < 7; x++) { - unsigned char* ppx = &px[((sy + y) * tw + sx + x) * 4]; - int col = MwFontData[out].data[y] & (1 << ((7 - 1) - x)) ? 255 : 0; - ppx[0] = col; - ppx[1] = col; - ppx[2] = col; - ppx[3] = col; - } - } - sx += 7; - } - - p = MwLLCreatePixmap(handle, px, tw, th); - r.x = 5; - r.y = 5; - r.width = tw; - r.height = th; - - MwLLDrawPixmap(handle, &r, p); - MwLLDestroyPixmap(p); - free(px); - } - MwLLWaylandBufferUpdate(handle, &handle->wayland.backbuffer); - } - handle->wayland.selected_cairo = handle->wayland.front_cairo; } static void MwLLEndDrawImpl(MwLL handle) { @@ -1074,6 +1085,10 @@ static void MwLLNextEventImpl(MwLL handle) { } } + if(handle->wayland.decor) { + wl_call_tbl.libdecor_dispatch(handle->wayland.decor, 0); + } + if(handle->wayland.force_render) { MwLLDispatch(handle, draw, NULL); if(handle->wayland.configured) MwLLWaylandBufferUpdate(handle, &handle->wayland.framebuffer); @@ -1086,7 +1101,11 @@ static void MwLLNextEventImpl(MwLL handle) { static void MwLLSetTitleImpl(MwLL handle, const char* title) { if(handle->wayland.type == MwLL_WAYLAND_TOPLEVEL) { - xdg_toplevel_set_title(handle->wayland.toplevel->xdg_top_level, title); + if(handle->wayland.has_decorations) { + xdg_toplevel_set_title(handle->wayland.toplevel->xdg_top_level, title); + } else { + wl_call_tbl.libdecor_frame_set_title(handle->wayland.decor_frame, title); + } } if(!handle->wayland.has_decorations) { strncpy(handle->wayland.title, title, 255); @@ -1143,7 +1162,6 @@ static void MwLLDestroyPixmapImpl(MwLLPixmap pixmap) { static void MwLLDrawPixmapImpl(MwLL handle, MwRect* rect, MwLLPixmap pixmap) { cairo_t* c; cairo_surface_t* cs; - cairo_t* selected_cairo = handle->wayland.selected_cairo ? handle->wayland.selected_cairo : handle->wayland.front_cairo; if(rect->width <= 0 || rect->height <= 0 || pixmap->common.width <= 0 || pixmap->common.height <= 0) return; if(rect->width >= INT16_MAX) rect->width = INT16_MAX; @@ -1163,8 +1181,8 @@ static void MwLLDrawPixmapImpl(MwLL handle, MwRect* rect, MwLLPixmap pixmap) { cairo_paint(c); - cairo_set_source_surface(selected_cairo, cs, rect->x, rect->y); - cairo_paint(selected_cairo); + cairo_set_source_surface(handle->wayland.front_cairo, cs, rect->x, rect->y); + cairo_paint(handle->wayland.front_cairo); cairo_destroy(c); cairo_surface_destroy(cs);