Issue #947: Improved layout handling and Unicode -> X11 keysyms translation

This commit is contained in:
Maxim Devaev 2023-03-23 11:50:22 +02:00
parent 22db176ef0
commit 26238e241e
4 changed files with 64 additions and 20 deletions

View File

@ -62,6 +62,7 @@ depends=(
python-pam
"python-pillow>=8.3.1-1"
python-xlib
libxkbcommon
python-hidapi
python-six
python-pyrad

View File

@ -41,15 +41,19 @@ class SymmapModifiers:
CTRL: int = 0x4
def build_symmap(path: str) -> dict[int, dict[int, str]]:
def build_symmap(path: str) -> dict[int, dict[int, str]]: # x11 keysym -> [(modifiers, webkey), ...]
# https://github.com/qemu/qemu/blob/95a9457fd44ad97c518858a4e1586a5498f9773c/ui/keymaps.c
logger = get_logger()
symmap: dict[int, dict[int, str]] = {}
for (src, items) in [
("<builtin>", list(X11_TO_AT1.items())),
(path, list(_read_keyboard_layout(path).items())),
("<builtin>", list(X11_TO_AT1.items())),
]:
# Пока лучшая логика - самые первые записи в файле раскладки
# должны иметь приоритет над следующими, а дефолтный маппинг
# только дополняет отсутствующие значения.
for (code, keys) in items:
for key in keys:
web_name = AT1_TO_WEB.get(key.code)
@ -62,14 +66,15 @@ def build_symmap(path: str) -> dict[int, dict[int, str]]:
logger.error("Invalid modifier key at mapping %s: %s / %s", src, web_name, key)
continue
if code not in symmap:
symmap[code] = {}
symmap[code][
modifiers = (
0
| (SymmapModifiers.SHIFT if key.shift else 0)
| (SymmapModifiers.ALTGR if key.altgr else 0)
| (SymmapModifiers.CTRL if key.ctrl else 0)
] = web_name
)
if code not in symmap:
symmap[code] = {}
symmap[code].setdefault(modifiers, web_name)
return symmap

View File

@ -20,22 +20,50 @@
# ========================================================================== #
import ctypes
import ctypes.util
from typing import Generator
from .keysym import SymmapModifiers
from .mappings import WebModifiers
# =====
def _load_libxkbcommon() -> ctypes.CDLL:
path = ctypes.util.find_library("xkbcommon")
if not path:
raise RuntimeError("Where is libxkbcommon?")
assert path
lib = ctypes.CDLL(path)
for (name, restype, argtypes) in [
("xkb_utf32_to_keysym", ctypes.c_uint32, [ctypes.c_uint32]),
]:
func = getattr(lib, name)
if not func:
raise RuntimeError(f"Where is libc.{name}?")
setattr(func, "restype", restype)
setattr(func, "argtypes", argtypes)
return lib
_libxkbcommon = _load_libxkbcommon()
def _ch_to_keysym(ch: str) -> int:
assert len(ch) == 1
return _libxkbcommon.xkb_utf32_to_keysym(ord(ch))
# =====
def text_to_web_keys( # pylint: disable=too-many-branches
text: str,
symmap: dict[int, dict[int, str]],
shift_key: str=WebModifiers.SHIFT_LEFT,
) -> Generator[tuple[str, bool], None, None]:
assert shift_key in WebModifiers.SHIFTS
shift = False
altgr = False
shifted = False
for ch in text:
# https://stackoverflow.com/questions/12343987/convert-ascii-character-to-x11-keycode
# https://www.ascii-code.com
@ -57,25 +85,34 @@ def text_to_web_keys( # pylint: disable=too-many-branches
if not ch.isprintable():
continue
try:
keys = symmap[ord(ch)]
keys = symmap[_ch_to_keysym(ch)]
except Exception:
continue
for (modifiers, key) in reversed(keys.items()):
if (modifiers & SymmapModifiers.ALTGR) or (modifiers & SymmapModifiers.CTRL):
for (modifiers, key) in keys.items():
if modifiers & SymmapModifiers.CTRL:
# Not supported yet
continue
if modifiers & SymmapModifiers.SHIFT and not shifted:
yield (shift_key, True)
shifted = True
elif not (modifiers & SymmapModifiers.SHIFT) and shifted:
yield (shift_key, False)
shifted = False
if modifiers & SymmapModifiers.SHIFT and not shift:
yield (WebModifiers.SHIFT_LEFT, True)
shift = True
elif not (modifiers & SymmapModifiers.SHIFT) and shift:
yield (WebModifiers.SHIFT_LEFT, False)
shift = False
if modifiers & SymmapModifiers.ALTGR and not altgr:
yield (WebModifiers.ALT_RIGHT, True)
altgr = True
elif not (modifiers & SymmapModifiers.ALTGR) and altgr:
yield (WebModifiers.ALT_RIGHT, False)
altgr = False
yield (key, True)
yield (key, False)
break
if shifted:
yield (shift_key, False)
if shift:
yield (WebModifiers.SHIFT_LEFT, False)
if altgr:
yield (WebModifiers.ALT_RIGHT, False)

View File

@ -56,6 +56,7 @@ RUN pacman --noconfirm --ask=4 -Syy \
python-pam \
python-pillow \
python-xlib \
libxkbcommon \
python-hidapi \
python-zstandard \
freetype2 \