From 9c95757f8dbc418a06a3cc84d24ca683e3a9587e Mon Sep 17 00:00:00 2001 From: IoI_xD Date: Thu, 23 Apr 2026 10:45:29 -0700 Subject: [PATCH] wayland: better system for destroyed events --- include/Mw/LowLevel/Wayland.h | 8 +- src/backend/wayland.c | 196 +++++++++++++++++++++++++++------- 2 files changed, 164 insertions(+), 40 deletions(-) diff --git a/include/Mw/LowLevel/Wayland.h b/include/Mw/LowLevel/Wayland.h index 42990a3..39e6718 100644 --- a/include/Mw/LowLevel/Wayland.h +++ b/include/Mw/LowLevel/Wayland.h @@ -456,6 +456,7 @@ struct _MwLLWayland { struct zwp_relative_pointer_v1* relative_pointer; struct zwp_locked_pointer_v1* locked_pointer; MwBool pointer_constrained; + MwBool valid; #ifdef USE_DBUS MwLLDBusContext dbus; @@ -519,12 +520,11 @@ struct _MwLLWayland { struct _MwLLWaylandShmBuffer cursor; struct _MwLLWaylandShmBuffer* icon; - /* - Events mutex. Any time a keyboard/mouse event happens, we try to lock this for 100 milliseconds, then give up if we can't do it. This is used in conjunction with some code in the MwLLDestroyImpl to make sure that destroy is NEVER interferes with an ongoing event. - - IOI_XD: This sounds like a hilariously rare edge case, so it's almost funnier that this happened with 100% certainty for me and I spent day(s) trying to figure out what was happening. */ pthread_mutex_t eventsMutex; + int cancelEvent; + int numCallbacksRunning; + uint32_t last_time; MwBool moving; diff --git a/src/backend/wayland.c b/src/backend/wayland.c index c2a5ebd..cfd6c5e 100644 --- a/src/backend/wayland.c +++ b/src/backend/wayland.c @@ -17,17 +17,37 @@ wayland_call_table_t wl_call_tbl; MwBool MwWaylandAlwaysRender = MwFALSE; -/* Standard procedure before most event callbacks in Wayland ("most" because the setup ones don't need this). Wait for the Mutex to be freed, if we're deadlocking for longer then a quarter of a second then do nothing with the event. */ +static pthread_mutex_t destroyedWidgetsTableMutex; +static MwLL* destroyedWidgetsTable; + +static MwBool is_destroyed(MwLL self) { + int i; + pthread_mutex_lock(&destroyedWidgetsTableMutex); + for(i = 0; i < arrlen(destroyedWidgetsTable); i++) { + if(self == destroyedWidgetsTable[i]) { + pthread_mutex_unlock(&destroyedWidgetsTableMutex); + return MwTRUE; + } + } + pthread_mutex_unlock(&destroyedWidgetsTableMutex); + return MwFALSE; +} +static void undestroy(MwLL self) { + int i; + pthread_mutex_lock(&destroyedWidgetsTableMutex); + for(i = 0; i < arrlen(destroyedWidgetsTable); i++) { + if(self == destroyedWidgetsTable[i]) { + arrdel(destroyedWidgetsTable, i); + break; + } + } + pthread_mutex_unlock(&destroyedWidgetsTableMutex); + return; +} +/* Standard procedure before event callbacks in Wayland */ #define WAYLAND_EVENT_OP_START(self) \ - do { \ - struct timespec t; \ - t.tv_sec = 0; \ - t.tv_nsec = 250; \ - if(pthread_mutex_timedlock(&self->wayland.eventsMutex, &t) != 0) { \ - /*printf("deadlock at line %d\n", __LINE__);*/ \ - return; \ - }; \ - } while(0); + if(is_destroyed(self)) return; \ + pthread_mutex_lock(&self->wayland.eventsMutex); /* Footer for WAYLAND_EVENT_OP_START */ #define WAYLAND_EVENT_OP_END(self) pthread_mutex_unlock(&self->wayland.eventsMutex); @@ -743,6 +763,7 @@ static void keyboard_key(void* data, int i; if(!self->wayland.xkb_keymap) { + WAYLAND_EVENT_OP_END(self); return; } @@ -757,6 +778,7 @@ static void keyboard_key(void* data, } syms_num = xkb_keymap_key_get_syms_by_level(self->wayland.xkb_keymap, keycode, layout, level, &syms_out); if(syms_out == NULL) { + WAYLAND_EVENT_OP_END(self); return; } @@ -1578,6 +1600,12 @@ static void setup_sublevel(MwLL parent, MwLL r, int x, int y) { r->wayland.framebuffer.surface = wl_compositor_create_surface(compositor); + if(is_destroyed(parent) || is_destroyed(r)) { + printf("H\n"); + r->wayland.valid = MwFALSE; + return; + } + r->wayland.sublevel->subsurface = wl_subcompositor_get_subsurface(r->wayland.sublevel->subcompositor, r->wayland.framebuffer.surface, parent_surface); wl_subsurface_set_desync(r->wayland.sublevel->subsurface); @@ -1803,6 +1831,7 @@ static void widget_setup(MwLL r, MwLL parent, int x, int y, int width, int heigh r->wayland.x = x; r->wayland.y = y; r->wayland.parent = parent; + r->wayland.valid = MwTRUE; if(ty == MwLL_WAYLAND_UNKNOWN) { if(parent == NULL) { @@ -1826,6 +1855,10 @@ static void widget_setup(MwLL r, MwLL parent, int x, int y, int width, int heigh } } + if(!r->wayland.valid) { + return; + } + framebuffer_setup(&r->wayland); backbuffer_setup(&r->wayland); @@ -1869,6 +1902,10 @@ static MwLL MwLLCreateImpl(MwLL parent, int x, int y, int width, int height) { memset(r, 0, sizeof(*r)); MwLLCreateCommon(r); + if(is_destroyed(r)) { + undestroy(r); + } + widget_setup(r, parent, x, y, width, height, MwLL_WAYLAND_UNKNOWN); #ifdef USE_DBUS @@ -1900,21 +1937,19 @@ static void MwLLDestroyImpl(MwLL handle) { wl_flush(handle); - if(pthread_mutex_timedlock(&handle->wayland.eventsMutex, &t) != 0) { - printf("WARNING: Couldn't call MwLLDestroyImpl due to deadlock.\n"); - return; + handle->wayland.cancelEvent = MwTRUE; + while(pthread_mutex_trylock(&handle->wayland.eventsMutex)) { + printf("h\n"); }; MwLLDestroyCommon(handle); - /* sleep long enough that any active attempts to wait for the mutex will have given up and we can safely unlock/destroy it */ tv.tv_sec = 0; tv.tv_usec = 1000; do { select_ret = select(1, NULL, NULL, NULL, &tv); } while((select_ret == -1) && (errno == EINTR)); - pthread_mutex_unlock(&handle->wayland.eventsMutex); pthread_mutex_destroy(&handle->wayland.eventsMutex); #ifdef USE_DBUS @@ -1923,25 +1958,29 @@ static void MwLLDestroyImpl(MwLL handle) { } #endif - buffer_destroy(&handle->wayland.cursor); - wl_region_destroy(handle->wayland.region); + if(handle->wayland.valid) { + buffer_destroy(&handle->wayland.cursor); + wl_region_destroy(handle->wayland.region); - if(handle->wayland.supports_zwp) { - zwp_primary_selection_source_v1_destroy(handle->wayland.clipboard_source.zwp); - } else { - wl_data_source_destroy(handle->wayland.clipboard_source.wl); - } - if(handle->wayland.icon != NULL) { - buffer_destroy(handle->wayland.icon); - wl_surface_destroy(handle->wayland.icon->surface); - } + if(handle->wayland.supports_zwp) { + zwp_primary_selection_source_v1_destroy(handle->wayland.clipboard_source.zwp); + } else { + wl_data_source_destroy(handle->wayland.clipboard_source.wl); + } + if(handle->wayland.icon != NULL) { + buffer_destroy(handle->wayland.icon); + wl_surface_destroy(handle->wayland.icon->surface); + } - if(handle->wayland.type == MwLL_WAYLAND_TOPLEVEL) { - destroy_toplevel(handle); - } else if(handle->wayland.type == MwLL_WAYLAND_SUBLEVEL) { - destroy_sublevel(handle); - } else if(handle->wayland.type == MwLL_WAYLAND_POPUP) { - destroy_popup(handle); + if(handle->wayland.type == MwLL_WAYLAND_TOPLEVEL) { + destroy_toplevel(handle); + } else if(handle->wayland.type == MwLL_WAYLAND_SUBLEVEL) { + destroy_sublevel(handle); + } else if(handle->wayland.type == MwLL_WAYLAND_POPUP) { + destroy_popup(handle); + } + wl_keyboard_destroy(handle->wayland.keyboard); + wl_pointer_destroy(handle->wayland.pointer); } for(i = 0; i < shlen(handle->wayland.wl_protocol_setup_map); i++) { @@ -1955,9 +1994,6 @@ static void MwLLDestroyImpl(MwLL handle) { shfree(handle->wayland.wl_protocol_map); shfree(handle->wayland.wl_protocol_setup_map); - wl_keyboard_destroy(handle->wayland.keyboard); - wl_pointer_destroy(handle->wayland.pointer); - wl_flush(handle); if(handle->wayland.parent != NULL) { @@ -1971,7 +2007,11 @@ static void MwLLDestroyImpl(MwLL handle) { arrfree(handle->wayland.children); - // free(handle); + free(handle); + + pthread_mutex_lock(&destroyedWidgetsTableMutex); + arrput(destroyedWidgetsTable, handle); + pthread_mutex_unlock(&destroyedWidgetsTableMutex); if(currentlyHeldWidget == handle) { currentlyHeldWidget = NULL; @@ -1979,6 +2019,13 @@ static void MwLLDestroyImpl(MwLL handle) { } static void MwLLGetXYWHImpl(MwLL handle, int* x, int* y, unsigned int* w, unsigned int* h) { + if(!handle->wayland.valid) { + *x = 0; + *y = 0; + *w = 1; + *h = 1; + return; + } *x = handle->wayland.x; *y = handle->wayland.y; *w = handle->wayland.ww; @@ -1987,6 +2034,9 @@ static void MwLLGetXYWHImpl(MwLL handle, int* x, int* y, unsigned int* w, unsign static void recursive_render(MwLL handle) { int i; + if(!handle->wayland.valid) { + return; + } for(i = 0; i < arrlen(handle->wayland.children); i++) recursive_render(handle->wayland.children[i]); @@ -1994,6 +2044,9 @@ static void recursive_render(MwLL handle) { } static void MwLLSetXYImpl(MwLL handle, int x, int y) { + if(!handle->wayland.valid) { + return; + } region_invalidate(handle); handle->wayland.x = x; handle->wayland.y = y; @@ -2008,6 +2061,9 @@ static void MwLLSetXYImpl(MwLL handle, int x, int y) { } static void MwLLSetWHImpl(MwLL handle, int w, int h) { + if(!handle->wayland.valid) { + return; + } region_invalidate(handle); /* Prevent an integer underflow when the w/h is too low */ @@ -2047,6 +2103,9 @@ static void MwLLSetWHImpl(MwLL handle, int w, int h) { } static void MwLLBeginDrawImpl(MwLL handle) { + if(!handle->wayland.valid) { + return; + } 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); @@ -2155,6 +2214,9 @@ static void MwLLEndDrawImpl(MwLL handle) { static void MwLLPolygonImpl(MwLL handle, MwPoint* points, int points_count, MwLLColor color) { int i; + if(!handle->wayland.valid) { + return; + } clip(handle); @@ -2178,6 +2240,9 @@ static void MwLLPolygonImpl(MwLL handle, MwPoint* points, int points_count, MwLL static void MwLLLineImpl(MwLL handle, MwPoint* points, MwLLColor color) { int i; + if(!handle->wayland.valid) { + return; + } clip(handle); @@ -2230,6 +2295,9 @@ static int MwLLPendingImpl(MwLL handle) { .events = POLLOUT, }; int pending = 0; + if(!handle->wayland.valid) { + return 0; + } handle->wayland.resizing = 0; @@ -2282,6 +2350,9 @@ static int MwLLPendingImpl(MwLL handle) { } static void MwLLNextEventImpl(MwLL handle) { + if(!handle->wayland.valid) { + return; + } if(!MwWaylandAlwaysRender) { if(handle->wayland.did_event_loop_early) { handle->wayland.did_event_loop_early = MwFALSE; @@ -2301,6 +2372,9 @@ static void MwLLNextEventImpl(MwLL handle) { } static void MwLLSetTitleImpl(MwLL handle, const char* title) { + if(!handle->wayland.valid) { + return; + } if(handle->wayland.type == MwLL_WAYLAND_TOPLEVEL) { xdg_toplevel_set_title(handle->wayland.toplevel->xdg_top_level, title); } @@ -2357,6 +2431,9 @@ 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(!handle->wayland.valid) { + return; + } cs = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, rect->width, rect->height); c = cairo_create(cs); @@ -2382,6 +2459,9 @@ static void MwLLDrawPixmapImpl(MwLL handle, MwRect* rect, MwLLPixmap pixmap) { wl_surface_damage(handle->wayland.framebuffer.surface, 0, 0, handle->wayland.ww, handle->wayland.wh); } static void MwLLSetIconImpl(MwLL handle, MwLLPixmap pixmap) { + if(!handle->wayland.valid) { + return; + } if(handle->wayland.type == MwLL_WAYLAND_TOPLEVEL) { if(WAYLAND_GET_INTERFACE(handle->wayland, xdg_toplevel_icon_manager_v1) != NULL) { struct xdg_toplevel_icon_manager_v1* icon_manager = WAYLAND_GET_INTERFACE(handle->wayland, xdg_toplevel_icon_manager_v1)->context; @@ -2423,6 +2503,9 @@ static void MwLLSetIconImpl(MwLL handle, MwLLPixmap pixmap) { } static void MwLLForceRenderImpl(MwLL handle) { + if(!handle->wayland.valid) { + return; + } wl_surface_damage(handle->wayland.framebuffer.surface, 0, 0, handle->wayland.ww, handle->wayland.wh); handle->wayland.force_render = MwTRUE; @@ -2434,6 +2517,10 @@ static void MwLLForceRenderImpl(MwLL handle) { static void MwLLSetCursorImpl(MwLL handle, MwCursor* image, MwCursor* mask) { int x, y, xs, ys; + if(!handle->wayland.valid) { + return; + } + if(handle->wayland.cursor.setup) { buffer_destroy(&handle->wayland.cursor); wl_surface_destroy(handle->wayland.cursor.surface); @@ -2489,6 +2576,9 @@ static void MwLLSetCursorImpl(MwLL handle, MwCursor* image, MwCursor* mask) { static void MwLLDetachImpl(MwLL handle, MwPoint* point) { MwLL p = handle->wayland.parent; int x = 0, y = 0; + if(!handle->wayland.valid) { + return; + } while(p != NULL) { x += p->wayland.x; y += p->wayland.y; @@ -2505,6 +2595,9 @@ static void MwLLShowImpl(MwLL handle, int show) { if(!handle->wayland.configured) { return; } + if(!handle->wayland.valid) { + return; + } /* Some guy on a mailing list said that "abusing" wl_surface_attach for this purpose is bad? This is documented behavior so please I beg of you let me know if there's a compositor that actually has a problem with this. */ if(handle->wayland.framebuffer.surface) { wl_surface_attach(handle->wayland.framebuffer.surface, show ? handle->wayland.framebuffer.shm_buffer : NULL, 0, 0); @@ -2523,6 +2616,9 @@ static void MwLLMakePopupImpl(MwLL handle, MwLL parent) { } static void MwLLSetSizeHintsImpl(MwLL handle, int minx, int miny, int maxx, int maxy) { + if(!handle->wayland.valid) { + return; + } if(handle->wayland.type == MwLL_WAYLAND_TOPLEVEL) { xdg_toplevel_set_min_size(handle->wayland.toplevel->xdg_top_level, minx, miny); xdg_toplevel_set_max_size(handle->wayland.toplevel->xdg_top_level, maxx, maxy); @@ -2531,6 +2627,9 @@ static void MwLLSetSizeHintsImpl(MwLL handle, int minx, int miny, int maxx, int static void MwLLMakeBorderlessImpl(MwLL handle, int toggle) { (void)toggle; + if(!handle->wayland.valid) { + return; + } if(handle->wayland.type == MwLL_WAYLAND_TOPLEVEL) { if(WAYLAND_GET_INTERFACE(handle->wayland, zxdg_decoration_manager_v1) != NULL) { zxdg_decoration_manager_v1_context_t* dec = WAYLAND_GET_INTERFACE(handle->wayland, zxdg_decoration_manager_v1)->context; @@ -2551,6 +2650,9 @@ static void MwLLFocusImpl(MwLL handle) { static void MwLLGrabPointerImpl(MwLL handle, int toggle) { MwLL topmost_parent = handle; + if(!handle->wayland.valid) { + return; + } while(topmost_parent->wayland.parent) topmost_parent = topmost_parent->wayland.parent; if(handle->wayland.pointer_constraints && handle->wayland.relative_pointer_manager) { if(toggle) { @@ -2577,6 +2679,9 @@ static void MwLLGrabPointerImpl(MwLL handle, int toggle) { static void MwLLSetClipboardImpl(MwLL handle, const char* text, int clipboard_type) { int i; + if(!handle->wayland.valid) { + return; + } if(handle->wayland.clipboard_buffer != NULL) { free(handle->wayland.clipboard_buffer); @@ -2603,6 +2708,9 @@ static void MwLLSetClipboardImpl(MwLL handle, const char* text, int clipboard_ty static void MwLLGetClipboardImpl(MwLL handle, int clipboard_type) { int i; + if(!handle->wayland.valid) { + return; + } if(clipboard_type == MwCLIPBOARD_PRIMARY) { if(handle->wayland.supports_zwp) { for(i = 0; i < arrlen(handle->wayland.clipboard_devices_zwp); i++) { @@ -2622,14 +2730,26 @@ static void MwLLGetClipboardImpl(MwLL handle, int clipboard_type) { } static void MwLLMakeToolWindowImpl(MwLL handle) { + if(!handle->wayland.valid) { + return; + } + handle->wayland.type_to_be = MwLL_WAYLAND_POPUP; } static void MwLLGetCursorCoordImpl(MwLL handle, MwPoint* point) { + if(!handle->wayland.valid) { + return; + } + *point = handle->wayland.cur_mouse_pos; } static void MwLLGetScreenSizeImpl(MwLL handle, MwRect* rect) { + if(!handle->wayland.valid) { + return; + } + rect->x = 0; rect->y = 0; rect->width = handle->wayland.mw; @@ -2642,6 +2762,10 @@ static void MwLLBeginStateChangeImpl(MwLL handle) { } static void MwLLEndStateChangeImpl(MwLL handle) { + if(!handle->wayland.valid) { + return; + } + if(handle->wayland.detatching) { MwLL topmost_parent = handle->wayland.parent;