diff --git a/.gitignore b/.gitignore index 9b39d99..95359ff 100644 --- a/.gitignore +++ b/.gitignore @@ -85,3 +85,4 @@ WindowMaker/menu.* WindowMaker/plmenu WindowMaker/plmenu.* WindowMaker/wmmacros +core.* diff --git a/src/backend/backend.h b/src/backend/backend.h index 8b56188..56286b8 100644 --- a/src/backend/backend.h +++ b/src/backend/backend.h @@ -1215,7 +1215,7 @@ typedef struct WMBackend { * Called from the ConfigureNotify case in the main event switch. * No-op if USE_RANDR is not compiled in (or equivalent). */ - void (*event_randr_update)(void *event); + void (*event_randr_update)(WMEvent *event); /* * Return the current XKB keyboard group (layout index, 0-based). @@ -1807,9 +1807,25 @@ typedef struct WMBackend { /* NETWM/XDND mechanism (Phase 80s) */ int (*netwm_process_client_message)(void *xevent); int (*xdnd_process_client_message)(void *xevent); - void (*xdnd_process_selection)(void *xevent); + void (*xdnd_process_selection)(WMEvent *event); void (*xdnd_make_awareness)(WNativeWindow win); void (*netwm_check_client_hint_change)(WWindow *wwin, void *event); + + /* + * ICCCM WM-replacement SelectionRequest / SelectionClear handling. + * + * The X server delivers SelectionRequest/SelectionClear events for + * the WM_Sn selection that wmaker holds when running under X11. + * Both handlers need access to the X display, atom interning, and + * XChangeProperty — all X11-only. They live in the X11 backend. + * + * Wayland: the Wayland backend's wmaker UI does not own a WM_Sn + * selection on the X server, so these are no-ops. XWayland clients + * still receive their own selections via wlroots' data device path + * and never reach these slots. + */ + void (*handle_selection_request)(WMEvent *event); + void (*handle_selection_clear)(WMEvent *event); void (*netwm_position_splash)(WWindow *wwin, int *x, int *y, int width, int height); void (*netwm_check_initial_frame_state)(WWindow *wwin); diff --git a/src/backend/wayland/wl_backend.c b/src/backend/wayland/wl_backend.c index c2664b6..72508e7 100644 --- a/src/backend/wayland/wl_backend.c +++ b/src/backend/wayland/wl_backend.c @@ -6253,7 +6253,7 @@ static int wl_event_pending(void) { return 0; } static void wl_event_drain_motion(void) {} static void wl_event_discard_enter_leave_until(unsigned long timestamp) { (void)timestamp; } static void wl_event_handle_extensions(void *event) { (void)event; } -static void wl_event_randr_update(void *event) { (void)event; } +static void wl_event_randr_update(WMEvent *event) { (void)event; } /* ------------------------------------------------------------------ */ /* Properties */ @@ -8876,8 +8876,10 @@ static const char *wl_noop_keysym_to_string(unsigned long ks) { (void)ks; return static int wl_noop_window_get_command(WNativeWindow win, char ***a, int *c) { (void)win; (void)a; *c=0; return 0; } static int wl_noop_netwm_process_client_message(void *x) { (void)x; return 0; } static int wl_noop_xdnd_process_client_message(void *x) { (void)x; return 0; } -static void wl_noop_xdnd_process_selection(void *x) { (void)x; } +static void wl_noop_xdnd_process_selection(WMEvent *e) { (void)e; } static void wl_noop_xdnd_make_awareness(WNativeWindow w) { (void)w; } +static void wl_noop_handle_selection_request(WMEvent *e) { (void)e; } +static void wl_noop_handle_selection_clear(WMEvent *e) { (void)e; } static void wl_noop_netwm_check_client_hint_change(WWindow *w, void *e) { (void)w; (void)e; } static void wl_noop_netwm_position_splash(WWindow *w, int *x, int *y, int ww, int h) { (void)w;(void)x;(void)y;(void)ww;(void)h; } static void wl_noop_netwm_check_initial_frame_state(WWindow *w) { (void)w; } @@ -9152,9 +9154,10 @@ const WMBackend wl_backend = { .netwm_process_client_message = wl_noop_netwm_process_client_message, .xdnd_process_client_message = wl_noop_xdnd_process_client_message, .xdnd_process_selection = wl_noop_xdnd_process_selection, - .xdnd_make_awareness = wl_noop_xdnd_make_awareness, - .netwm_check_client_hint_change = wl_noop_netwm_check_client_hint_change, + .xdnd_make_awareness = wl_noop_xdnd_make_awareness, .netwm_check_client_hint_change = wl_noop_netwm_check_client_hint_change, .netwm_position_splash = wl_noop_netwm_position_splash, .netwm_check_initial_frame_state = wl_noop_netwm_check_initial_frame_state, + .handle_selection_request = wl_noop_handle_selection_request, + .handle_selection_clear = wl_noop_handle_selection_clear, .create_rcontext = wl_create_rcontext, }; diff --git a/src/backend/x11/x11_backend.c b/src/backend/x11/x11_backend.c index 8095d31..9be03f2 100644 --- a/src/backend/x11/x11_backend.c +++ b/src/backend/x11/x11_backend.c @@ -2339,12 +2339,19 @@ x11_event_handle_extensions(void *raw_event) } static void -x11_event_randr_update(void *raw_event) +x11_event_randr_update(WMEvent *event) { #ifdef USE_RANDR - XRRUpdateConfiguration((XEvent *)raw_event); + if (!event) + return; + /* RandR root events only — ignore configure events on regular windows. */ + if (event->u.configure.window != DefaultRootWindow(dpy)) + return; + XEvent *xev = (XEvent *)event->_x11_raw; + if (xev) + XRRUpdateConfiguration(xev); #else - (void) raw_event; + (void) event; #endif } @@ -3553,9 +3560,86 @@ static int x11_xdnd_process_client_message(void *xevent) { return wXDNDProcessClientMessage(&((XEvent *)xevent)->xclient); } -static void x11_xdnd_process_selection(void *xevent) +static void x11_xdnd_process_selection(WMEvent *event) { - wXDNDProcessSelection((XEvent *)xevent); + XEvent *xev = event ? (XEvent *)event->_x11_raw : NULL; + if (xev) + wXDNDProcessSelection(xev); +} + +static void x11_handle_selection_request(WMEvent *event) +{ + XEvent *xev = event ? (XEvent *)event->_x11_raw : NULL; + if (!xev) return; + XSelectionRequestEvent *xsr = &xev->xselectionrequest; +#ifdef USE_ICCCM_WMREPLACE + static Atom atom_version = None; + WScreen *scr; + XSelectionEvent notify; + + /* Default reply: 'no selection'. */ + notify.type = SelectionNotify; + notify.display = dpy; + notify.requestor = xsr->requestor; + notify.selection = xsr->selection; + notify.target = xsr->target; + notify.property = None; + notify.time = xsr->time; + + scr = wScreenForWindow(xsr->owner); + if (!scr) + goto not_our_selection; + if (xsr->owner != scr->info_window) + goto not_our_selection; + if (xsr->selection != scr->sn_atom) + goto not_our_selection; + + if (atom_version == None) + atom_version = XInternAtom(dpy, "VERSION", False); + + if (xsr->target == atom_version) { + static const long icccm_version[] = { 2, 0 }; + + /* ICCCM 2.0: "Communication with the Window Manager by Means of + * Selections". Setting the property means the content of the + * selection is available. Old clients may pass property=None. */ + notify.property = (xsr->property == None) ? xsr->target : xsr->property; + XChangeProperty(dpy, xsr->requestor, notify.property, + WA_INTEGER, 32, PropModeReplace, + (unsigned char *)icccm_version, wlengthof(icccm_version)); + } + + not_our_selection: + if (notify.property == None) + wwarning("received SelectionRequest(%s) for target=\"%s\" from " + "requestor 0x%lX but we have no answer", + XGetAtomName(dpy, xsr->selection), + XGetAtomName(dpy, xsr->target), + (long)xsr->requestor); + + /* Send the answer to the requestor */ + wm_backend->client_send_event((WNativeWindow)xsr->requestor, (void *)¬ify); +#else + (void)xsr; +#endif +} + +static void x11_handle_selection_clear(WMEvent *event) +{ + XEvent *xev = event ? (XEvent *)event->_x11_raw : NULL; + if (!xev) return; +#ifdef USE_ICCCM_WMREPLACE + XSelectionClearEvent *xsc = &xev->xselectionclear; + WScreen *scr = wScreenForWindow(xsc->window); + if (!scr) + return; + if (xsc->selection != scr->sn_atom) + return; + wmessage(_("another window manager is replacing us!")); + Shutdown(WSExitMode); +#else + (void)xev; +#endif } static void x11_xdnd_make_awareness(WNativeWindow win) { @@ -4470,6 +4554,8 @@ const WMBackend x11_backend = { .xdnd_process_selection = x11_xdnd_process_selection, .xdnd_make_awareness = x11_xdnd_make_awareness, .netwm_check_client_hint_change = x11_netwm_check_client_hint_change, + .handle_selection_request = x11_handle_selection_request, + .handle_selection_clear = x11_handle_selection_clear, .netwm_position_splash = x11_netwm_position_splash, .netwm_check_initial_frame_state = x11_netwm_check_initial_frame_state, .create_rcontext = x11_create_rcontext, diff --git a/src/event.c b/src/event.c index 8c755bf..bd3b6a7 100644 --- a/src/event.c +++ b/src/event.c @@ -91,8 +91,6 @@ static void handleFocusIn(WMEvent *event); static void handleMotionNotify(WMEvent *event); static void handleVisibilityNotify(WMEvent *event); /* inotify handling moved to x11_backend.c (x11_event_loop_run) */ -static void handle_selection_request(XSelectionRequestEvent *event); -static void handle_selection_clear(XSelectionClearEvent *event); static void wdelete_death_handler(WMagicNumber id); @@ -245,10 +243,8 @@ void DispatchEvent(WMEvent * event) break; case WME_MAPPING: - if (event->u.mapping.request == MappingKeyboard || event->u.mapping.request == MappingModifier) { - XEvent *xev = (XEvent *)event->_x11_raw; - if (xev) wm_backend->keyboard_mapping_refresh(event); - } + if (event->u.mapping.request == MappingKeyboard || event->u.mapping.request == MappingModifier) + wm_backend->keyboard_mapping_refresh(event); break; case WME_FOCUS_IN: @@ -260,22 +256,15 @@ void DispatchEvent(WMEvent * event) break; case WME_CONFIGURE: - if (event->u.configure.window == DefaultRootWindow(dpy)) - wm_backend->event_randr_update((void *)event->_x11_raw); + wm_backend->event_randr_update(event); break; case WME_SELECTION_REQUEST: - { - XEvent *xev = (XEvent *)event->_x11_raw; - if (xev) handle_selection_request(&xev->xselectionrequest); - } + wm_backend->handle_selection_request(event); break; case WME_SELECTION_CLEAR: - { - XEvent *xev = (XEvent *)event->_x11_raw; - if (xev) handle_selection_clear(&xev->xselectionclear); - } + wm_backend->handle_selection_clear(event); break; default: @@ -427,10 +416,8 @@ static void saveTimestamp(WMEvent * event) case WME_SELECTION: w_global.timestamp.last_event = event->u.selection.time; #ifdef USE_DOCK_XDND - if (event->type == WME_SELECTION) { - XEvent *xev = (XEvent *)event->_x11_raw; - if (xev) wm_backend->xdnd_process_selection(xev); - } + if (event->type == WME_SELECTION) + wm_backend->xdnd_process_selection(event); #endif break; } @@ -1973,97 +1960,6 @@ static void handleVisibilityNotify(WMEvent * event) wwin->flags.obscured = (event->u.visibility.state == VisibilityFullyObscured); } -static void handle_selection_request(XSelectionRequestEvent *event) -{ -#ifdef USE_ICCCM_WMREPLACE - static Atom atom_version = None; - WScreen *scr; - XSelectionEvent notify; +/* Selection request/clear handling moved to backend + * (X11 backend: x11_handle_selection_request/clear; Wayland: no-op) */ - /* - * This event must be sent to the slection requester to not block him - * - * We create it with the answer 'there is no selection' by default - */ - notify.type = SelectionNotify; - notify.display = dpy; - notify.requestor = event->requestor; - notify.selection = event->selection; - notify.target = event->target; - notify.property = None; /* This says that there is no selection */ - notify.time = event->time; - - scr = wScreenForWindow(event->owner); - if (!scr) - goto not_our_selection; - - if (event->owner != scr->info_window) - goto not_our_selection; - - if (event->selection != scr->sn_atom) - goto not_our_selection; - - if (atom_version == None) - atom_version = XInternAtom(dpy, "VERSION", False); - - if (event->target == atom_version) { - static const long icccm_version[] = { 2, 0 }; - - /* - * This protocol is defined in ICCCM 2.0: - * http://www.x.org/releases/X11R7.7/doc/xorg-docs/icccm/icccm.html - * "Communication with the Window Manager by Means of Selections" - */ - - /* - * Setting the property means the content of the selection is available - * According to the ICCCM spec, we need to support being asked for a property - * set to 'None' for compatibility with old clients - */ - notify.property = (event->property == None)?(event->target):(event->property); - - XChangeProperty(dpy, event->requestor, notify.property, - WA_INTEGER, 32, PropModeReplace, - (unsigned char *) icccm_version, wlengthof(icccm_version)); - } - - not_our_selection: - if (notify.property == None) - wwarning("received SelectionRequest(%s) for target=\"%s\" from requestor 0x%lX but we have no answer", - XGetAtomName(dpy, event->selection), XGetAtomName(dpy, event->target), (long) event->requestor); - - /* Send the answer to the requestor */ - wm_backend->client_send_event((WNativeWindow)event->requestor, (void *)¬ify); - -#else - /* - * If the support for ICCCM window manager replacement was not enabled, we should not receive - * this kind of event, so we just ignore it (Conceptually, we should reply with 'SelectionNotify' - * event with property set to 'None' to tell that we don't have this selection, but that is a bit - * costly for an event that shall never happen). - */ - (void) event; -#endif -} - -static void handle_selection_clear(XSelectionClearEvent *event) -{ -#ifdef USE_ICCCM_WMREPLACE - WScreen *scr = wScreenForWindow(event->window); - - if (!scr) - return; - - if (event->selection != scr->sn_atom) - return; - - wmessage(_("another window manager is replacing us!")); - Shutdown(WSExitMode); -#else - /* - * If the support for ICCCM window manager replacement was not enabled, we should not receive - * this kind of event, so we simply do nothing. - */ - (void) event; -#endif -}