arch: move X11 modifier-keymap discovery into X11 backend

xmodifier.c contained x_reset_modifier_mapping which walked the X
server's modifier map via XGetModifierMapping / XFreeModifiermap to
discover the X11 modifier indices for Meta/Hyper/Super/Alt keysyms.
This was 4 direct Xlib calls in shared src/.

Refactor:
  src/xmodifier.c  — now only holds the modifier-mask state and the
                     portable wXModifierFromKey / wXModifierToShortcut
                     Label / wSetModifierMasks helpers used by shared
                     policy code.  No more Xlib symbols.

  src/backend/x11/x11_backend.c  — owns x11_reset_modifier_mapping,
                     called from x11_extensions_init.  Calls
                     wSetModifierMasks at the end so shared code
                     answers modifier lookups correctly.

  src/xmodifier.h  — drops wXModifierInitialize.  Wayland never needed
                     it; X11 calls the local x11_reset_modifier_mapping
                     directly.

The unused ModeMask static was dropped along the way.
This commit is contained in:
Window Maker 2026-05-24 08:43:32 +02:00
parent a789abc09f
commit 9cca44c765
3 changed files with 139 additions and 284 deletions

View File

@ -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();
}
/* -------------------------------------------------------------------- */

View File

@ -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 <sj@eng.mindspring.net>, 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 <string.h>
#include <strings.h>
#include <X11/X.h> /* ShiftMask, ControlMask, ModNMask constants */
#include <WINGs/WUtil.h>
#include <WINGs/WINGsP.h>
#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);
}

View File

@ -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 */