Fix input dispatch: custom event loop + modal menu support
The deadlock: frame_timer_cb called WMHandleEvent which entered menuMouseDown's modal WMMaskEvent loop. That blocked inside wl_event_loop_dispatch (reentrant), preventing new events. Fix: 1. Replace wl_display_run() with custom loop that drains WINGs events BETWEEN wl_event_loop_dispatch calls (not inside timer) 2. Make WINGs wl_flush() pump wlroots (call dispatch_fn) so WMMaskEvent modal loops get new events without reentrancy 3. Fix wBalloonHide NULL dereference (scr->balloon not allocated) 4. Remove XEvent dependency from wSelectWindows (takes x,y now) 5. Remove debug traces Right-click on background now opens the root menu.
This commit is contained in:
parent
ac739a1a5c
commit
d93f4935ca
|
|
@ -150,7 +150,12 @@ wl_check_typed_window(void *ectx, WNativeWindow w, int type, WMEvent *ev)
|
|||
static void
|
||||
wl_flush(void *ectx, Bool discard)
|
||||
{
|
||||
(void)ectx; (void)discard;
|
||||
WLEventCtx *ctx = ectx;
|
||||
(void)discard;
|
||||
/* Pump the wlroots event loop so pointer/key events arrive in our queue.
|
||||
* This is called from waitForEvent/WMMaskEvent when the queue is empty. */
|
||||
if (ctx->dispatch_fn)
|
||||
ctx->dispatch_fn(0, ctx->dispatch_ud);
|
||||
}
|
||||
|
||||
static int
|
||||
|
|
|
|||
|
|
@ -194,12 +194,13 @@
|
|||
#include "../../icon.h" /* WIcon, TILE_* */
|
||||
#include "../../appicon.h" /* WAppIcon */
|
||||
#include "../../menu.h" /* WMenu, WMenuEntry */
|
||||
#include "../../balloon.h" /* WBalloon */
|
||||
#include "../backend.h"
|
||||
#include "wl_backend.h"
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* WINGs event backend interface (declared in WINGs/WINGsP.h) */
|
||||
/* Forward-declared here to avoid pulling the full private header. */
|
||||
/* WINGs private header — needed for W_EventBackend, W_Screen fields */
|
||||
#include <WINGs/WINGsP.h>
|
||||
/* ------------------------------------------------------------------ */
|
||||
#include <WINGs/WMEvent.h>
|
||||
struct W_Screen;
|
||||
|
|
@ -364,6 +365,7 @@ struct wl_compositor_state {
|
|||
struct wl_event_source *x11_event_source; /* wl_event_loop fd watcher for x_display */
|
||||
pthread_t startup_pump_thread; /* pumps Wayland event loop during StartUp() */
|
||||
volatile int startup_pump_running; /* 1 while pump thread is active */
|
||||
volatile int terminate; /* set to exit main loop */
|
||||
/* Phase 20: real input devices from nested backend + their listeners */
|
||||
struct wlr_input_device *real_keyboard_dev;
|
||||
struct wlr_input_device *real_pointer_dev;
|
||||
|
|
@ -545,8 +547,7 @@ wl_shutdown_pipe_handler(int fd, uint32_t mask, void *data)
|
|||
/* drain the pipe so the fd does not stay readable */
|
||||
while (read(fd, buf, sizeof(buf)) > 0)
|
||||
;
|
||||
if (wl_state.display)
|
||||
wl_display_terminate(wl_state.display);
|
||||
wl_state.terminate = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -554,13 +555,12 @@ static int
|
|||
frame_timer_cb(void *data)
|
||||
{
|
||||
(void)data;
|
||||
/* Skip wlroots rendering calls while the startup pump thread is running —
|
||||
* wlr_output_send_frame involves EGL context operations that are not safe
|
||||
* to call from a non-main thread. */
|
||||
|
||||
if (!wl_state.startup_pump_running) {
|
||||
if (!wl_state.is_nested && wl_state.output)
|
||||
wlr_output_send_frame(wl_state.output);
|
||||
}
|
||||
|
||||
if (wl_state.frame_timer)
|
||||
wl_event_source_timer_update(wl_state.frame_timer, 16);
|
||||
return 0;
|
||||
|
|
@ -2797,9 +2797,8 @@ wl_signalfd_handler(int fd, uint32_t mask, void *data)
|
|||
/* Set the exit state flag so post-loop cleanup paths see it. */
|
||||
SIG_WCHANGE_STATE(WSTATE_NEED_EXIT);
|
||||
|
||||
/* Terminate the event loop from within loop context. */
|
||||
if (wl_state.display)
|
||||
wl_display_terminate(wl_state.display);
|
||||
/* Terminate the event loop. */
|
||||
wl_state.terminate = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -2919,7 +2918,33 @@ wl_event_loop_run(void)
|
|||
}
|
||||
|
||||
wwarning("wl_backend: event loop running");
|
||||
wl_display_run(wl_state.display);
|
||||
|
||||
/* Custom event loop: pump wlroots, then drain WINGs events.
|
||||
* We can't use wl_display_run() because WM event handlers (OpenRootMenu,
|
||||
* etc.) call WMMaskEvent which re-enters the wlroots loop. By draining
|
||||
* WINGs events BETWEEN dispatches (not inside a timer callback), modal
|
||||
* loops in WMMaskEvent can safely call wl_event_loop_dispatch. */
|
||||
{
|
||||
struct wl_event_loop *loop = wl_display_get_event_loop(wl_state.display);
|
||||
while (!wl_state.terminate) {
|
||||
wl_display_flush_clients(wl_state.display);
|
||||
wl_event_loop_dispatch(loop, 16);
|
||||
|
||||
/* Drain WINGs event queue */
|
||||
WMScreen *wmscr = wl_get_wmscreen();
|
||||
if (wmscr) {
|
||||
const W_EventBackend *be = wmscr->eventBackend;
|
||||
void *ectx = wmscr->eventBackendCtx;
|
||||
WMEvent wev;
|
||||
int limit = 64;
|
||||
while (be->pending(ectx) && limit-- > 0) {
|
||||
be->fetch(ectx, &wev);
|
||||
WMHandleEvent(&wev);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wwarning("wl_backend: event loop exited");
|
||||
}
|
||||
|
||||
|
|
@ -6414,7 +6439,10 @@ static void wl_noop_icon_select(WIcon *icon) { (void)icon; }
|
|||
static void wl_noop_icon_change_title(WIcon *icon, WWindow *wwin) { (void)icon; (void)wwin; }
|
||||
static void wl_clip_icon_paint(WAppIcon *aicon) { wl_appicon_paint(aicon); }
|
||||
static void wl_drawer_icon_paint(WAppIcon *aicon) { wl_appicon_paint(aicon); }
|
||||
static void wl_noop_balloon_init(WScreen *scr) { (void)scr; }
|
||||
static void wl_balloon_init(WScreen *scr)
|
||||
{
|
||||
scr->balloon = wmalloc(sizeof(WBalloon));
|
||||
}
|
||||
static void wl_noop_do_kaboom(WScreen *scr, WNativeWindow win, int x, int y)
|
||||
{ (void)scr; (void)win; (void)x; (void)y; }
|
||||
static void wl_menu_paint_entry(WMenu *menu, int index, int selected)
|
||||
|
|
@ -6887,7 +6915,7 @@ const WMBackend wl_backend = {
|
|||
.icon_change_title = wl_noop_icon_change_title,
|
||||
.clip_icon_paint = wl_clip_icon_paint,
|
||||
.drawer_icon_paint = wl_drawer_icon_paint,
|
||||
.balloon_init = wl_noop_balloon_init,
|
||||
.balloon_init = wl_balloon_init,
|
||||
.do_kaboom = wl_noop_do_kaboom,
|
||||
.menu_paint_entry = wl_menu_paint_entry,
|
||||
.menu_draw_frame = wl_menu_draw_frame,
|
||||
|
|
|
|||
|
|
@ -262,7 +262,7 @@ void wBalloonEnteredObject(WScreen *scr, WObjDescriptor *object)
|
|||
|
||||
void wBalloonHide(WScreen *scr)
|
||||
{
|
||||
if (scr) {
|
||||
if (scr && scr->balloon) {
|
||||
if (scr->balloon->mapped) {
|
||||
wm_backend->window_unmap((WNativeWindow)scr->balloon->window);
|
||||
scr->balloon->mapped = 0;
|
||||
|
|
|
|||
|
|
@ -149,6 +149,7 @@ static void wdelete_death_handler(WMagicNumber id)
|
|||
|
||||
void DispatchEvent(WMEvent * event)
|
||||
{
|
||||
|
||||
if (deathHandlers)
|
||||
handleDeadProcess();
|
||||
|
||||
|
|
|
|||
|
|
@ -379,11 +379,9 @@ void StartUp(Bool defaultScreenOnly)
|
|||
/* handle X shutdowns and errors via backend */
|
||||
wm_backend->register_error_handlers();
|
||||
|
||||
/* set hook for our event dispatcher in WINGs event dispatcher (X11 only).
|
||||
* Skip for Wayland: we run a wlroots event loop, not a WINGs/X11 loop.
|
||||
* dpy may be set (XWayland bridge), but X11 event dispatch is not used. */
|
||||
if (!wm_backend->is_wayland)
|
||||
WMHookEventHandler(DispatchEvent);
|
||||
/* set hook for our event dispatcher in WINGs event dispatcher.
|
||||
* In Wayland mode, events are drained from the frame timer. */
|
||||
WMHookEventHandler(DispatchEvent);
|
||||
|
||||
/* initialize defaults stuff */
|
||||
w_global.domain.wmaker = wDefaultsInitDomain("WindowMaker", True);
|
||||
|
|
|
|||
Loading…
Reference in New Issue