summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMaxim Devaev <[email protected]>2023-03-23 11:50:22 +0200
committerMaxim Devaev <[email protected]>2023-03-23 11:50:22 +0200
commit26238e241e2d470ddda0798f95bb5e855bd11ecc (patch)
tree088811b1df8b434fc6c770232ccdbd5a709c2a34
parent22db176ef05df0b49a9e40e3c31c886dfcb710b5 (diff)
Issue #947: Improved layout handling and Unicode -> X11 keysyms translation
-rw-r--r--PKGBUILD1
-rw-r--r--kvmd/keyboard/keysym.py17
-rw-r--r--kvmd/keyboard/printer.py65
-rw-r--r--testenv/Dockerfile1
4 files changed, 64 insertions, 20 deletions
diff --git a/PKGBUILD b/PKGBUILD
index cdaa6ef7..fc66c919 100644
--- a/PKGBUILD
+++ b/PKGBUILD
@@ -62,6 +62,7 @@ depends=(
python-pam
"python-pillow>=8.3.1-1"
python-xlib
+ libxkbcommon
python-hidapi
python-six
python-pyrad
diff --git a/kvmd/keyboard/keysym.py b/kvmd/keyboard/keysym.py
index c744e675..6ba7bd48 100644
--- a/kvmd/keyboard/keysym.py
+++ b/kvmd/keyboard/keysym.py
@@ -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
diff --git a/kvmd/keyboard/printer.py b/kvmd/keyboard/printer.py
index a2003a18..a29355b6 100644
--- a/kvmd/keyboard/printer.py
+++ b/kvmd/keyboard/printer.py
@@ -20,6 +20,9 @@
# ========================================================================== #
+import ctypes
+import ctypes.util
+
from typing import Generator
from .keysym import SymmapModifiers
@@ -27,15 +30,40 @@ 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)
diff --git a/testenv/Dockerfile b/testenv/Dockerfile
index c9bb5628..92b82e26 100644
--- a/testenv/Dockerfile
+++ b/testenv/Dockerfile
@@ -56,6 +56,7 @@ RUN pacman --noconfirm --ask=4 -Syy \
python-pam \
python-pillow \
python-xlib \
+ libxkbcommon \
python-hidapi \
python-zstandard \
freetype2 \