diff --git a/src/backend/x11/x11_backend.c b/src/backend/x11/x11_backend.c index 9be03f2..76688d3 100644 --- a/src/backend/x11/x11_backend.c +++ b/src/backend/x11/x11_backend.c @@ -2424,6 +2424,109 @@ x11_client_get_gnustep_attrs(WNativeWindow win, GNUstepWMAttributes **attr_out) return True; } +/* + * Walk the X server's modifier map to discover which X11 modifier + * indices the Meta/Hyper/Super/Alt keysyms are bound to, then call + * wSetModifierMasks so shared code (rootmenu, defaults, misc) can + * answer wXModifierFromKey / wXModifierToShortcutLabel correctly. + * + * Originally lived in src/xmodifier.c (taken from XEmacs 20.3-b17, + * Sept 1997); moved here so shared code carries no Xlib dependency. + */ +static const char *x11_modindex_to_name(int idx) +{ + switch (idx) { + case ShiftMapIndex: return "ModShift"; + case LockMapIndex: return "ModLock"; + case ControlMapIndex: return "ModControl"; + case Mod1MapIndex: return "Mod1"; + case Mod2MapIndex: return "Mod2"; + case Mod3MapIndex: return "Mod3"; + case Mod4MapIndex: return "Mod4"; + case Mod5MapIndex: return "Mod5"; + default: return "???"; + } +} + +static void x11_reset_modifier_mapping(void) +{ + int meta_bit = 0, hyper_bit = 0, super_bit = 0, alt_bit = 0, mode_bit = 0; + XModifierKeymap *kmap = XGetModifierMapping(dpy); + int mkpm = kmap->max_keypermod; + + for (int idx = 0; idx < 8; idx++) { + for (int k = 0; k < mkpm; k++) { + KeySym last_sym = 0; + for (int col = 0; col < 4; col += 2) { + KeyCode code = kmap->modifiermap[idx * mkpm + k]; + KeySym sym = W_KeycodeToKeysym(dpy, code, col); + if (sym == last_sym) continue; + last_sym = sym; + + int *target = NULL; + int fixed_mask = 0; + const char *name = NULL; + switch (sym) { + case WM_KEY_MODE_SWITCH: name = "Mode_switch"; target = &mode_bit; break; + case WM_KEY_META_L: name = "Meta_L"; target = &meta_bit; break; + case WM_KEY_META_R: name = "Meta_R"; target = &meta_bit; break; + case WM_KEY_SUPER_L: name = "Super_L"; target = &super_bit; break; + case WM_KEY_SUPER_R: name = "Super_R"; target = &super_bit; break; + case WM_KEY_HYPER_L: name = "Hyper_L"; target = &hyper_bit; break; + case WM_KEY_HYPER_R: name = "Hyper_R"; target = &hyper_bit; break; + case WM_KEY_ALT_L: name = "Alt_L"; target = &alt_bit; break; + case WM_KEY_ALT_R: name = "Alt_R"; target = &alt_bit; break; + case WM_KEY_CONTROL_L: name = "Control_L"; fixed_mask = ControlMask; break; + case WM_KEY_CONTROL_R: name = "Control_R"; fixed_mask = ControlMask; break; + case WM_KEY_SHIFT_L: name = "Shift_L"; fixed_mask = ShiftMask; break; + case WM_KEY_SHIFT_R: name = "Shift_R"; fixed_mask = ShiftMask; break; + case WM_KEY_SHIFT_LOCK: name = "Shift_Lock"; fixed_mask = LockMask; break; + case WM_KEY_CAPS_LOCK: name = "Caps_Lock"; fixed_mask = LockMask; break; + default: continue; + } + + if (fixed_mask) { + if ((1 << idx) != fixed_mask) + wwarning(_("key %s (0x%x) generates %s, which is nonsensical"), + name, code, x11_modindex_to_name(idx)); + } else if (target) { + if (*target && *target != idx) + wwarning(_("key %s (0x%x) generates both %s and %s, " + "which is nonsensical"), + name, code, x11_modindex_to_name(*target), + x11_modindex_to_name(idx)); + else + *target = idx; + } + } + } + } + + /* If there was no Meta key, try using Alt instead. */ + if (!meta_bit && alt_bit) { meta_bit = alt_bit; alt_bit = 0; } + + /* mode_bit overrides everything: XLookupString interprets it + * unconditionally, so we cannot also call it Meta/Super/Hyper/Alt. */ + if (mode_bit) { + const char *warn = NULL; + if (mode_bit == meta_bit) { warn = "Meta"; meta_bit = 0; } + else if (mode_bit == hyper_bit) { warn = "Hyper"; hyper_bit = 0; } + else if (mode_bit == super_bit) { warn = "Super"; super_bit = 0; } + else if (mode_bit == alt_bit) { warn = "Alt"; alt_bit = 0; } + if (warn) + wwarning("%s is being used for both Mode_switch and %s.", + x11_modindex_to_name(mode_bit), warn); + } + + wSetModifierMasks( + alt_bit ? (1 << alt_bit) : 0, + meta_bit ? (1 << meta_bit) : 0, + super_bit ? (1 << super_bit) : 0, + hyper_bit ? (1 << hyper_bit) : 0); + + XFreeModifiermap(kmap); +} + static void x11_extensions_init(void) { @@ -2442,7 +2545,7 @@ x11_extensions_init(void) } #endif (void) j; - wXModifierInitialize(); + x11_reset_modifier_mapping(); } /* -------------------------------------------------------------------- */ diff --git a/src/xmodifier.c b/src/xmodifier.c index f352525..23beec7 100644 --- a/src/xmodifier.c +++ b/src/xmodifier.c @@ -1,276 +1,30 @@ -/* Grok X modifier mappings for shortcuts. - -Most of this code was taken from src/event-Xt.c in XEmacs 20.3-b17. -The copyright(s) from the original XEmacs code are included below. - -Perpetrator: Sudish Joseph , Sept. 1997. */ - -/* The event_stream interface for X11 with Xt, and/or tty frames. - Copyright (C) 1991, 1992, 1993, 1994, 1995 Free Software Foundation, Inc. - Copyright (C) 1995 Sun Microsystems, Inc. - Copyright (C) 1996 Ben Wing. - - This file is part of XEmacs. - - XEmacs is free software; you can redistribute it and/or modify it - under the terms of the GNU General Public License as published by the - Free Software Foundation; either version 2, or (at your option) any - later version. - - XEmacs is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - for more details. - - You should have received a copy of the GNU General Public License along - with XEmacs; see the file COPYING. if not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301 USA. */ +/* + * xmodifier.c — modifier mask state shared between backends. + * + * The actual X11 keymap discovery (XGetModifierMapping et al.) lives in + * the X11 backend; on Wayland the masks come from xkb via + * wSetModifierMasks called from the Wayland backend. This file only + * holds the cached masks and the portable name↔mask conversions used + * by shared policy code (rootmenu.c, defaults.c, misc.c, usermenu.c). + * + * History: this file used to contain the modifier-keymap-walking code + * (taken from XEmacs 20.3, original copyright Sudish Joseph, 1997, and + * Free Software Foundation 1991-1995). That code now lives in + * src/backend/x11/x11_backend.c (function x11_reset_modifier_mapping). + */ #include "wconfig.h" #include #include +#include /* ShiftMask, ControlMask, ModNMask constants */ + #include -#include #include "WindowMaker.h" #include "xmodifier.h" - -/************************************************************************/ -/* keymap handling */ -/************************************************************************/ - -/* X bogusly doesn't define the interpretations of any bits besides - ModControl, ModShift, and ModLock; so the Interclient Communication - Conventions Manual says that we have to bend over backwards to figure - out what the other modifier bits mean. According to ICCCM: - - - Any keycode which is assigned ModControl is a "control" key. - - - Any modifier bit which is assigned to a keycode which generates Meta_L - or Meta_R is the modifier bit meaning "meta". Likewise for Super, Hyper, - etc. - - - Any keypress event which contains ModControl in its state should be - interpreted as a "control" character. - - - Any keypress event which contains a modifier bit in its state which is - generated by a keycode whose corresponding keysym is Meta_L or Meta_R - should be interpreted as a "meta" character. Likewise for Super, Hyper, - etc. - - - It is illegal for a keysym to be associated with more than one modifier - bit. - - This means that the only thing that emacs can reasonably interpret as a - "meta" key is a key whose keysym is Meta_L or Meta_R, and which generates - one of the modifier bits Mod1-Mod5. - - Unfortunately, many keyboards don't have Meta keys in their default - configuration. So, if there are no Meta keys, but there are "Alt" keys, - emacs will interpret Alt as Meta. If there are both Meta and Alt keys, - then the Meta keys mean "Meta", and the Alt keys mean "Alt" (it used to - mean "Symbol," but that just confused the hell out of way too many people). - - This works with the default configurations of the 19 keyboard-types I've - checked. - - Emacs detects keyboard configurations which violate the above rules, and - prints an error message on the standard-error-output. (Perhaps it should - use a pop-up-window instead.) - */ - -static int MetaMask, HyperMask, SuperMask, AltMask, ModeMask; - -static const char *index_to_name(int indice) -{ - switch (indice) { - case ShiftMapIndex: - return "ModShift"; - case LockMapIndex: - return "ModLock"; - case ControlMapIndex: - return "ModControl"; - case Mod1MapIndex: - return "Mod1"; - case Mod2MapIndex: - return "Mod2"; - case Mod3MapIndex: - return "Mod3"; - case Mod4MapIndex: - return "Mod4"; - case Mod5MapIndex: - return "Mod5"; - default: - return "???"; - } -} - -static void x_reset_modifier_mapping(Display * display) -{ - int modifier_index, modifier_key, column, mkpm; - int meta_bit = 0; - int hyper_bit = 0; - int super_bit = 0; - int alt_bit = 0; - int mode_bit = 0; - XModifierKeymap *x_modifier_keymap = XGetModifierMapping(display); - - mkpm = x_modifier_keymap->max_keypermod; - for (modifier_index = 0; modifier_index < 8; modifier_index++) - for (modifier_key = 0; modifier_key < mkpm; modifier_key++) { - KeySym last_sym = 0; - - for (column = 0; column < 4; column += 2) { - KeyCode code; - KeySym sym; - - inline void modwarn(const char *key_name, int old_mod, const char *other_key) - { - wwarning(_("key %s (0x%x) generates %s, which is generated by %s"), - key_name, code, index_to_name(old_mod), other_key); - } - - inline void modbarf(const char *key_name, const char *other_mod) - { - wwarning(_("key %s (0x%x) generates %s, which is nonsensical"), - key_name, code, other_mod); - } - - inline void check_modifier(const char *key_name, int mask) - { - if ((1 << modifier_index) != mask) - modbarf(key_name, index_to_name(modifier_index)); - } - - inline void store_modifier(const char *key_name, int *old_mod) - { - if (*old_mod && *old_mod != modifier_index) - wwarning(_("key %s (0x%x) generates both %s and %s, which is nonsensical"), - key_name, code, index_to_name(*old_mod), index_to_name(modifier_index)); - if (modifier_index == ShiftMapIndex) { - modbarf(key_name, "ModShift"); - } else if (modifier_index == LockMapIndex) { - modbarf(key_name, "ModLock"); - } else if (modifier_index == ControlMapIndex) { - modbarf(key_name, "ModControl"); - } else if (sym == WM_KEY_MODE_SWITCH) { - mode_bit = modifier_index; /* Mode_switch is special, see below... */ - } else if (modifier_index == meta_bit && *old_mod != meta_bit) { - modwarn(key_name, meta_bit, "Meta"); - } else if (modifier_index == super_bit && *old_mod != super_bit) { - modwarn(key_name, super_bit, "Super"); - } else if (modifier_index == hyper_bit && *old_mod != hyper_bit) { - modwarn(key_name, hyper_bit, "Hyper"); - } else if (modifier_index == alt_bit && *old_mod != alt_bit) { - modwarn(key_name, alt_bit, "Alt"); - } else { - *(old_mod) = modifier_index; - } - } - - code = x_modifier_keymap->modifiermap[modifier_index * mkpm + modifier_key]; - sym = W_KeycodeToKeysym(display, code, column); - - if (sym == last_sym) - continue; - last_sym = sym; - - switch (sym) { - case WM_KEY_MODE_SWITCH: - store_modifier("Mode_switch", &mode_bit); - break; - case WM_KEY_META_L: - store_modifier("Meta_L", &meta_bit); - break; - case WM_KEY_META_R: - store_modifier("Meta_R", &meta_bit); - break; - case WM_KEY_SUPER_L: - store_modifier("Super_L", &super_bit); - break; - case WM_KEY_SUPER_R: - store_modifier("Super_R", &super_bit); - break; - case WM_KEY_HYPER_L: - store_modifier("Hyper_L", &hyper_bit); - break; - case WM_KEY_HYPER_R: - store_modifier("Hyper_R", &hyper_bit); - break; - case WM_KEY_ALT_L: - store_modifier("Alt_L", &alt_bit); - break; - case WM_KEY_ALT_R: - store_modifier("Alt_R", &alt_bit); - break; - case WM_KEY_CONTROL_L: - check_modifier("Control_L", ControlMask); - break; - case WM_KEY_CONTROL_R: - check_modifier("Control_R", ControlMask); - break; - case WM_KEY_SHIFT_L: - check_modifier("Shift_L", ShiftMask); - break; - case WM_KEY_SHIFT_R: - check_modifier("Shift_R", ShiftMask); - break; - case WM_KEY_SHIFT_LOCK: - check_modifier("Shift_Lock", LockMask); - break; - case WM_KEY_CAPS_LOCK: - check_modifier("Caps_Lock", LockMask); - break; - - /* It probably doesn't make any sense for a modifier bit to be - assigned to a key that is not one of the above, but OpenWindows - assigns modifier bits to a couple of random function keys for - no reason that I can discern, so printing a warning here would - be annoying. */ - } - } - } - - /* If there was no Meta key, then try using the Alt key instead. - If there is both a Meta key and an Alt key, then the Alt key - is not disturbed and remains an Alt key. */ - if (!meta_bit && alt_bit) - meta_bit = alt_bit, alt_bit = 0; - - /* mode_bit overrides everything, since it's processed down inside of - XLookupString() instead of by us. If Meta and Mode_switch both - generate the same modifier bit (which is an error), then we don't - interpret that bit as Meta, because we can't make XLookupString() - not interpret it as Mode_switch; and interpreting it as both would - be totally wrong. */ - if (mode_bit) { - const char *warn = NULL; - - if (mode_bit == meta_bit) - warn = "Meta", meta_bit = 0; - else if (mode_bit == hyper_bit) - warn = "Hyper", hyper_bit = 0; - else if (mode_bit == super_bit) - warn = "Super", super_bit = 0; - else if (mode_bit == alt_bit) - warn = "Alt", alt_bit = 0; - if (warn) { - wwarning("%s is being used for both Mode_switch and %s.", - index_to_name(mode_bit), warn); - } - } - - MetaMask = (meta_bit ? (1 << meta_bit) : 0); - HyperMask = (hyper_bit ? (1 << hyper_bit) : 0); - SuperMask = (super_bit ? (1 << super_bit) : 0); - AltMask = (alt_bit ? (1 << alt_bit) : 0); - ModeMask = (mode_bit ? (1 << mode_bit) : 0); /* unused */ - - XFreeModifiermap(x_modifier_keymap); -} +static int MetaMask, HyperMask, SuperMask, AltMask; const char *wXModifierToShortcutLabel(int mask) { @@ -279,21 +33,21 @@ const char *wXModifierToShortcutLabel(int mask) if (mask == ShiftMask) return "Sh+"; - if (mask == ControlMask) + if (mask == ControlMask) return "^"; - if (mask == AltMask) + if (mask == AltMask) return "A+"; - if (mask == Mod1Mask) + if (mask == Mod1Mask) return "M1+"; - if (mask == Mod2Mask) + if (mask == Mod2Mask) return "M2+"; - if (mask == Mod3Mask) + if (mask == Mod3Mask) return "M3+"; - if (mask == Mod4Mask) + if (mask == Mod4Mask) return "M4+"; - if (mask == Mod5Mask) + if (mask == Mod5Mask) return "M5+"; - if (mask == MetaMask) + if (mask == MetaMask) return "M+"; wwarning(_("Can't convert keymask 0x%04X to a shortcut label"), mask); @@ -328,9 +82,11 @@ 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. */ +/* + * Set the standard modifier masks. Called from x11_backend.c after + * XGetModifierMapping discovery, or from wl_backend.c with XKB defaults + * (Mod1=Alt=Meta, Mod4=Super, Hyper=0). + */ void wSetModifierMasks(int alt, int meta, int super, int hyper) { AltMask = alt; @@ -338,10 +94,3 @@ void wSetModifierMasks(int alt, int meta, int super, int hyper) 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) -{ - x_reset_modifier_mapping(dpy); -} diff --git a/src/xmodifier.h b/src/xmodifier.h index 06f2f7f..c229a8c 100644 --- a/src/xmodifier.h +++ b/src/xmodifier.h @@ -20,10 +20,13 @@ #ifndef _XMODIFIER_H_INCLUDED #define _XMODIFIER_H_INCLUDED -void wXModifierInitialize(void); int wXModifierFromKey(const char *key); const char *wXModifierToShortcutLabel(int mask); -/* Phase 77: set modifier masks for non-X11 backends (Wayland). */ +/* + * Set standard modifier masks. X11 backend calls this from its + * extension-init path after walking the X server's modifier map; + * Wayland backend calls this with XKB-standard values. + */ void wSetModifierMasks(int alt, int meta, int super, int hyper); #endif /* _XMODIFIER_H_INCLUDED */