diff --git a/src/backend/wayland/wl_backend.c b/src/backend/wayland/wl_backend.c index 12060ba..d6db683 100644 --- a/src/backend/wayland/wl_backend.c +++ b/src/backend/wayland/wl_backend.c @@ -88,10 +88,13 @@ /* Generated xdg-shell protocol implementation (Wayland glue code) */ #include "protocols/xdg-shell-protocol.c" #include +/* Phase 77: XKB for keysym→keycode translation and modifier mask setup */ +#include #include "../../WindowMaker.h" #include "../../screen.h" #include "../../window.h" +#include "../../xmodifier.h" /* Phase 77: wSetModifierMasks */ #include "../backend.h" #include "wl_backend.h" @@ -500,8 +503,27 @@ wl_display_open(const char *name) * wlr_seat_keyboard_notify_enter() can deliver keyboard focus. */ wl_state.keyboard_dev = wlr_headless_add_input_device(wl_state.backend, WLR_INPUT_DEVICE_KEYBOARD); - if (wl_state.keyboard_dev) + if (wl_state.keyboard_dev) { + /* Phase 77: set a default XKB keymap on the headless keyboard so that + * wl_keysym_to_keycode() can populate wKeyBindings during StartUp(). + * The xkb_keymap_new_from_names(NULL) produces the system default + * layout (usually "us"), giving us the same keycode space as X11. */ + struct wlr_keyboard *kb = wl_state.keyboard_dev ? wl_state.keyboard_dev->keyboard : NULL; + if (kb) { + struct xkb_context *xkb_ctx = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + if (xkb_ctx) { + struct xkb_keymap *keymap = xkb_keymap_new_from_names( + xkb_ctx, NULL, XKB_KEYMAP_COMPILE_NO_FLAGS); + if (keymap) { + wlr_keyboard_set_keymap(kb, keymap); + xkb_keymap_unref(keymap); + wwarning("wl_backend: XKB keymap set on headless keyboard"); + } + xkb_context_unref(xkb_ctx); + } + } wlr_seat_set_keyboard(wl_state.seat, wl_state.keyboard_dev); + } /* Phase 53: create a headless pointer so the seat advertises pointer * capability to clients (required for clients that bind wl_pointer). */ @@ -1299,6 +1321,11 @@ wl_screen_create_internals(WScreen *scr) wPreferences.auto_focus = 1; if (wl_state.seat) wlr_seat_keyboard_clear_focus(wl_state.seat); + + /* Phase 77: initialise modifier masks to standard XKB/X11 values so + * that wXModifierFromKey("ALT") etc. return non-zero values. + * Mod1Mask=8 (Alt), Mod4Mask=64 (Super/Win); Meta aliases Alt. */ + wSetModifierMasks(Mod1Mask, Mod1Mask, Mod4Mask, 0); } static void wl_screen_create_pixmaps(WScreen *scr) { (void)scr; } static void wl_screen_load_tech_font(WScreen *scr) { (void)scr; } @@ -1368,8 +1395,59 @@ wl_workspace_badge_update(WScreen *scr, WNativeWindow badge_win, RImage *img) /* XKB */ /* ------------------------------------------------------------------ */ -static WKeyCode wl_keysym_to_keycode(unsigned long keysym) { (void)keysym; return 0; } -static unsigned long wl_keysym_from_keycode(unsigned int keycode, unsigned int index) { (void)keycode; (void)index; return 0; } +static WKeyCode +wl_keysym_to_keycode(unsigned long keysym) +{ + /* Phase 77: scan the XKB keymap on the headless keyboard to find the first + * keycode that produces the given keysym at any level/layout. + * The XKB keycode == evdev_scancode + 8, which equals the X11 keycode — + * so the values stored in wKeyBindings are directly compatible. */ + if (!wl_state.keyboard_dev || !wl_state.keyboard_dev->keyboard) + return 0; + struct wlr_keyboard *kb = wl_state.keyboard_dev->keyboard; + if (!kb->keymap) + return 0; + + xkb_keycode_t min_kc = xkb_keymap_min_keycode(kb->keymap); + xkb_keycode_t max_kc = xkb_keymap_max_keycode(kb->keymap); + + for (xkb_keycode_t kc = min_kc; kc <= max_kc; kc++) { + xkb_layout_index_t nlayouts = xkb_keymap_num_layouts_for_key(kb->keymap, kc); + xkb_layout_index_t layout; + for (layout = 0; layout < nlayouts; layout++) { + xkb_level_index_t nlevels = xkb_keymap_num_levels_for_key(kb->keymap, kc, layout); + xkb_level_index_t level; + for (level = 0; level < nlevels; level++) { + const xkb_keysym_t *syms = NULL; + int n = xkb_keymap_key_get_syms_by_level(kb->keymap, kc, layout, level, &syms); + int si; + for (si = 0; si < n; si++) { + if (syms[si] == (xkb_keysym_t)keysym) + return (WKeyCode)kc; + } + } + } + } + return 0; +} + +static unsigned long +wl_keysym_from_keycode(unsigned int keycode, unsigned int index) +{ + /* Phase 77: reverse lookup — return the keysym at the given keycode/level. */ + if (!wl_state.keyboard_dev || !wl_state.keyboard_dev->keyboard) + return 0; + struct wlr_keyboard *kb = wl_state.keyboard_dev->keyboard; + if (!kb->keymap) + return 0; + const xkb_keysym_t *syms = NULL; + int n = xkb_keymap_key_get_syms_by_level(kb->keymap, (xkb_keycode_t)keycode, + 0, (xkb_level_index_t)index, &syms); + if (n > 0 && syms) + return (unsigned long)syms[0]; + return 0; +} + static void wl_xkb_lock_group(int group) { (void)group; } static int wl_xkb_get_group(void) { return 0; } diff --git a/src/defaults.c b/src/defaults.c index 7715b6d..4d4cb11 100644 --- a/src/defaults.c +++ b/src/defaults.c @@ -2225,7 +2225,8 @@ static int getKeybind(WScreen * scr, WDefaultEntry * entry, WMPropList * value, return False; } - shortcut.keycode = XKeysymToKeycode(dpy, ksym); + /* Phase 77: use vtable so keysym lookup works for both X11 and Wayland. */ + shortcut.keycode = wm_backend->keysym_to_keycode((unsigned long)ksym); if (shortcut.keycode == 0) { wwarning(_("%s:invalid key in shortcut \"%s\""), entry->key, val); return False; diff --git a/src/rootmenu.c b/src/rootmenu.c index 0a4a411..371b12d 100644 --- a/src/rootmenu.c +++ b/src/rootmenu.c @@ -433,7 +433,8 @@ static Bool addShortcut(const char *file, const char *shortcutDefinition, WMenu return False; } - ptr->keycode = XKeysymToKeycode(dpy, ksym); + /* Phase 77: use vtable so keysym lookup works for both X11 and Wayland. */ + ptr->keycode = wm_backend->keysym_to_keycode((unsigned long)ksym); if (ptr->keycode == 0) { wwarning(_("%s:invalid key in shortcut \"%s\" for entry %s"), file, shortcutDefinition, entry->text); diff --git a/src/usermenu.c b/src/usermenu.c index 4ef5cff..e1b2434 100644 --- a/src/usermenu.c +++ b/src/usermenu.c @@ -173,7 +173,8 @@ static WUserMenuData *convertShortcuts(WScreen *scr, WMPropList *shortcut) if (ksym == NoSymbol) continue; - data->key[j].keycode = XKeysymToKeycode(dpy, ksym); + /* Phase 77: use vtable so keysym lookup works for both X11 and Wayland. */ + data->key[j].keycode = wm_backend->keysym_to_keycode((unsigned long)ksym); if (data->key[j].keycode) j++; } diff --git a/src/xmodifier.c b/src/xmodifier.c index a4383e0..f352525 100644 --- a/src/xmodifier.c +++ b/src/xmodifier.c @@ -328,6 +328,17 @@ int wXModifierFromKey(const char *key) return -1; } +/* Phase 77: allow non-X11 backends to set standard modifier masks directly. + * Called from wl_screen_create_internals with XKB-standard values: + * AltMask=Mod1Mask, MetaMask=Mod1Mask, SuperMask=Mod4Mask, HyperMask=0. */ +void wSetModifierMasks(int alt, int meta, int super, int hyper) +{ + AltMask = alt; + MetaMask = meta; + SuperMask = super; + HyperMask = hyper; +} + /* Wrapper so that we may fit the WM naming conventions, yet leave the original XEmacs function name in place. */ void wXModifierInitialize(void) diff --git a/src/xmodifier.h b/src/xmodifier.h index 915b113..06f2f7f 100644 --- a/src/xmodifier.h +++ b/src/xmodifier.h @@ -23,5 +23,7 @@ void wXModifierInitialize(void); int wXModifierFromKey(const char *key); const char *wXModifierToShortcutLabel(int mask); +/* Phase 77: set modifier masks for non-X11 backends (Wayland). */ +void wSetModifierMasks(int alt, int meta, int super, int hyper); #endif /* _XMODIFIER_H_INCLUDED */ diff --git a/tests/wl-smoke-test.sh b/tests/wl-smoke-test.sh index 3b950ee..020af90 100755 --- a/tests/wl-smoke-test.sh +++ b/tests/wl-smoke-test.sh @@ -91,6 +91,12 @@ # 34. (Phase 76) switchpanel.c: Alt-Tab panel title falls back to # wwin->wm_class when frame is NULL so Wayland windows appear in the # switcher without crashing (build-only check). +# 35. (Phase 77) XKB keymap set on headless keyboard so wl_keysym_to_keycode +# produces real keycodes; modifier masks (Alt=Mod1Mask, Super=Mod4Mask) +# set via wSetModifierMasks so wKeyBindings are populated correctly; +# XKeysymToKeycode(dpy,...) replaced by vtable in defaults.c/rootmenu.c/ +# usermenu.c so keyboard shortcuts are correctly configured in Wayland mode. +# Verified by "XKB keymap set on headless keyboard" log. # # This test grows as wl_backend vtable slots are implemented. # @@ -203,6 +209,14 @@ grep -q 'xdg-decoration-manager created' /tmp/wl-smoke-stderr \ || die "compositor did not log xdg-decoration-manager creation" echo " decoration check: xdg-decoration-manager created" +# -- 4d. Phase 77: XKB keymap must be set on headless keyboard ------------ +# +# wl_display_open() logs "XKB keymap set on headless keyboard" after +# Phase 77 changes; verify the log line is present. +grep -q 'XKB keymap set on headless keyboard' /tmp/wl-smoke-stderr \ + || die "compositor did not log XKB keymap setup (wl_keysym_to_keycode will return 0)" +echo " xkb check: XKB keymap set on headless keyboard" + # -- 5. Core Wayland globals must be advertised --------------------------- # # Phase 35: wl_compositor, wl_seat, and xdg_wm_base must appear in the