From a71d5529d7c5164956c95c915fc9f03629b77fbe Mon Sep 17 00:00:00 2001 From: IoIxD Date: Sun, 27 Sep 2026 07:49:49 +0900 Subject: [PATCH] wayland fix --- src/backend/wayland/interfaces.c | 3 ++- src/widget/menu.c | 31 +++++++++++++++++++++++++++---- src/widget/submenu.c | 26 ++++++++++++-------------- 3 files changed, 41 insertions(+), 19 deletions(-) diff --git a/src/backend/wayland/interfaces.c b/src/backend/wayland/interfaces.c index 2740c6f3..02d5a85d 100644 --- a/src/backend/wayland/interfaces.c +++ b/src/backend/wayland/interfaces.c @@ -777,7 +777,8 @@ static void mouse_dispatch(MwLL self, MwMouse p, MwU32 state) { switch(state) { case WL_POINTER_BUTTON_STATE_PRESSED: - MwLLDispatch(target, down, &p); + /* otherwise the recursion below reaches the target and dispatches it there */ + if(target == self) MwLLDispatch(target, down, &p); break; case WL_POINTER_BUTTON_STATE_RELEASED: MwLLDispatch(target, up, &p); diff --git a/src/widget/menu.c b/src/widget/menu.c index 7d2f284b..92820548 100644 --- a/src/widget/menu.c +++ b/src/widget/menu.c @@ -45,9 +45,22 @@ static void recursive_free(MwMenu m) { free(m); } +/* submenus are freed after us and would otherwise touch the entries we are about to free */ +static void detach_submenus(MwWidget handle) { + int i; + + for(i = 0; i < arrlen(handle->children); i++) { + if(handle->children[i]->widget_class != MwSubMenuClass) continue; + + handle->children[i]->internal = NULL; + detach_submenus(handle->children[i]); + } +} + static void destroy(MwWidget handle) { MwMenu m = handle->internal; + detach_submenus(handle); recursive_free(m); } @@ -92,15 +105,25 @@ static void destroy(MwWidget handle) { p.x += tw / 2 + 5; \ } +/* some backends (e.g. Wayland) dispatch input while creating a widget, which can re-enter these + * handlers; if a submenu for this item appeared meanwhile, drop ours instead of orphaning that one. + * a release handled during creation saw no submenu yet, so keep it open as mouse_up would have */ #define NEW_SUBMENU \ - MwPoint p2; \ + MwPoint p2; \ + MwWidget sw; \ \ p2.x = p.x - 5 - tw / 2; \ p2.y = p.y + th / 2 + 5; \ \ - m->sub[i]->wsub = MwCreateWidget(MwSubMenuClass, "submenu", handle, 0, MwGetInteger(handle, MwNheight), 0, 0); \ - MwSubMenuAppear(m->sub[i]->wsub, m->sub[i], &p2, 0); \ - m->sub[i]->cleaned = 0; + sw = MwCreateWidget(MwSubMenuClass, "submenu", handle, 0, MwGetInteger(handle, MwNheight), 0, 0); \ + if(sw != NULL && m->sub[i]->wsub != NULL) { \ + MwDestroyWidget(sw); \ + } else if(sw != NULL) { \ + m->sub[i]->wsub = sw; \ + m->sub[i]->keep = handle->pressed ? 0 : 1; \ + m->sub[i]->cleaned = 0; \ + MwSubMenuAppear(sw, m->sub[i], &p2, 0); \ + } static void draw(MwWidget handle) { MwColor base = MwParseColor(handle, MwGetString(handle, MwNbackground)); diff --git a/src/widget/submenu.c b/src/widget/submenu.c index 7ab48ff6..0b320e60 100644 --- a/src/widget/submenu.c +++ b/src/widget/submenu.c @@ -13,19 +13,10 @@ static int wcreate(MwWidget handle) { return 0; } -static void null_all(MwMenu menu) { - int i; - for(i = 0; i < arrlen(menu->sub); i++) { - null_all(menu->sub[i]); - } - menu->wsub = NULL; -} - static void destroy(MwWidget handle) { MwMenu menu = handle->internal; - menu->wsub = NULL; - null_all(menu); + if(menu != NULL && menu->wsub == handle) menu->wsub = NULL; } static void draw(MwWidget handle) { @@ -126,8 +117,9 @@ static void click(MwWidget handle) { if(MwGetInteger(handle, MwNleftPadding) <= handle->mouse_point.x && rc.y <= handle->mouse_point.y && handle->mouse_point.y <= (int)(rc.y + rc.height)) { if(menu->sub[i]->wsub == NULL && arrlen(menu->sub[i]->sub) > 0) { - MwPoint p; - int j; + MwPoint p; + MwWidget sw; + int j; for(j = 0; j < arrlen(menu->sub); j++) { if(menu->sub[j]->wsub != NULL) MwDestroyWidget(menu->sub[j]->wsub); @@ -137,8 +129,14 @@ static void click(MwWidget handle) { p.x = MwGetInteger(handle, MwNwidth); p.y = rc.y - 3; - menu->sub[i]->wsub = MwCreateWidget(MwSubMenuClass, "submenu", handle, 0, 0, 0, 0); - MwSubMenuAppear(menu->sub[i]->wsub, menu->sub[i], &p, 0); + /* some backends (e.g. Wayland) dispatch input while creating a widget, which can re-enter this handler */ + sw = MwCreateWidget(MwSubMenuClass, "submenu", handle, 0, 0, 0, 0); + if(sw != NULL && (menu->sub[i]->wsub != NULL || handle->destroyed)) { + MwDestroyWidget(sw); + } else if(sw != NULL) { + menu->sub[i]->wsub = sw; + MwSubMenuAppear(sw, menu->sub[i], &p, 0); + } i = -1; } else if(menu->sub[i]->wsub != NULL && arrlen(menu->sub[i]->sub) > 0) { while(w->parent->widget_class == MwSubMenuClass) w = w->parent;