From 8d862f25cef71ee22d6734ae2cf3f8c2f99146e7 Mon Sep 17 00:00:00 2001 From: Lars-Dominik Braun Date: Wed, 25 Nov 2020 14:06:36 +0100 Subject: keyboard: Move scancodes to yaml file Also add serialization and tests. --- lulua/data/keyboards/ibmpc105.yaml | 436 ++++++++++++++++++++++++++++++++++++- lulua/keyboard.py | 360 +++++------------------------- lulua/render.py | 19 +- lulua/test_keyboard.py | 18 +- 4 files changed, 519 insertions(+), 314 deletions(-) diff --git a/lulua/data/keyboards/ibmpc105.yaml b/lulua/data/keyboards/ibmpc105.yaml index d9dcb76..0938262 100644 --- a/lulua/data/keyboards/ibmpc105.yaml +++ b/lulua/data/keyboards/ibmpc105.yaml @@ -3,130 +3,558 @@ description: Standard IBM PC 105 key layout (European) rows: - - - kind: letter name: Bl1 + scancode: + keyman: K_BKSLASH + macos: 50 + windows: + - 41 + xorg: 49 + width: 1 - kind: letter name: Bl2 + scancode: + keyman: K_1 + macos: 18 + windows: + - 2 + xorg: 10 + width: 1 - kind: letter name: Bl3 + scancode: + keyman: K_2 + macos: 19 + windows: + - 3 + xorg: 11 + width: 1 - kind: letter name: Bl4 + scancode: + keyman: K_3 + macos: 20 + windows: + - 4 + xorg: 12 + width: 1 - kind: letter name: Bl5 + scancode: + keyman: K_4 + macos: 21 + windows: + - 5 + xorg: 13 + width: 1 - kind: letter name: Bl6 + scancode: + keyman: K_5 + macos: 23 + windows: + - 6 + xorg: 14 + width: 1 - kind: letter name: Bl7 + scancode: + keyman: K_6 + macos: 22 + windows: + - 7 + xorg: 15 + width: 1 - - kind: letter name: Br6 + scancode: + keyman: K_7 + macos: 26 + windows: + - 8 + xorg: 16 + width: 1 - kind: letter name: Br5 + scancode: + keyman: K_8 + macos: 28 + windows: + - 9 + xorg: 17 + width: 1 - kind: letter name: Br4 + scancode: + keyman: K_9 + macos: 25 + windows: + - 10 + xorg: 18 + width: 1 - kind: letter name: Br3 + scancode: + keyman: K_0 + macos: 29 + windows: + - 11 + xorg: 19 + width: 1 - kind: letter name: Br2 + scancode: + keyman: K_LBRKT + macos: 27 + windows: + - 12 + xorg: 20 + width: 1 - kind: letter name: Br1 + scancode: + keyman: K_RBRKT + macos: 24 + windows: + - 13 + xorg: 21 + width: 1 - name: Br_bs + scancode: + keyman: K_BKSP + macos: 51 + windows: + - 14 + xorg: 22 width: 1.75 - - - name: Cl_tab + scancode: + keyman: K_TAB + macos: 48 + windows: + - 15 + xorg: 23 width: 1.75 - kind: letter name: Cl1 + scancode: + keyman: K_Q + macos: 12 + windows: + - 16 + xorg: 24 + width: 1 - kind: letter name: Cl2 + scancode: + keyman: K_W + macos: 13 + windows: + - 17 + xorg: 25 + width: 1 - kind: letter name: Cl3 + scancode: + keyman: K_E + macos: 14 + windows: + - 18 + xorg: 26 + width: 1 - kind: letter name: Cl4 + scancode: + keyman: K_R + macos: 15 + windows: + - 19 + xorg: 27 + width: 1 - kind: letter name: Cl5 + scancode: + keyman: K_T + macos: 17 + windows: + - 20 + xorg: 28 + width: 1 - - kind: letter name: Cr7 + scancode: + keyman: K_Z + macos: 16 + windows: + - 21 + xorg: 29 + width: 1 - kind: letter name: Cr6 + scancode: + keyman: K_U + macos: 32 + windows: + - 22 + xorg: 30 + width: 1 - kind: letter name: Cr5 + scancode: + keyman: K_I + macos: 34 + windows: + - 23 + xorg: 31 + width: 1 - kind: letter name: Cr4 + scancode: + keyman: K_O + macos: 31 + windows: + - 24 + xorg: 32 + width: 1 - kind: letter name: Cr3 + scancode: + keyman: K_P + macos: 35 + windows: + - 25 + xorg: 33 + width: 1 - kind: letter name: Cr2 + scancode: + keyman: K_COLON + macos: 33 + windows: + - 26 + xorg: 34 + width: 1 - kind: letter name: Cr1 + scancode: + keyman: K_EQUAL + macos: 30 + windows: + - 27 + xorg: 35 + width: 1 - kind: multi name: CD_ret + scancode: + keyman: K_ENTER + macos: 36 + windows: + - 28 + xorg: 36 span: 2 + width: 1 - - - name: Dl_caps + scancode: + keyman: CAPS + macos: 57 + windows: + - 58 + xorg: 66 width: 2 - kind: letter name: Dl1 + scancode: + keyman: K_A + macos: 0 + windows: + - 30 + xorg: 38 + width: 1 - kind: letter name: Dl2 + scancode: + keyman: K_S + macos: 1 + windows: + - 31 + xorg: 39 + width: 1 - kind: letter name: Dl3 - - kind: letter - isMarked: true + scancode: + keyman: K_D + macos: 2 + windows: + - 32 + xorg: 40 + width: 1 + - isMarked: true + kind: letter name: Dl4 + scancode: + keyman: K_F + macos: 3 + windows: + - 33 + xorg: 41 + width: 1 - kind: letter name: Dl5 + scancode: + keyman: K_G + macos: 5 + windows: + - 34 + xorg: 42 + width: 1 - - kind: letter name: Dr7 - - kind: letter - isMarked: true + scancode: + keyman: K_H + macos: 4 + windows: + - 35 + xorg: 43 + width: 1 + - isMarked: true + kind: letter name: Dr6 + scancode: + keyman: K_J + macos: 38 + windows: + - 36 + xorg: 44 + width: 1 - kind: letter name: Dr5 + scancode: + keyman: K_K + macos: 40 + windows: + - 37 + xorg: 45 + width: 1 - kind: letter name: Dr4 + scancode: + keyman: K_L + macos: 37 + windows: + - 38 + xorg: 46 + width: 1 - kind: letter name: Dr3 + scancode: + keyman: K_BKQUOTE + macos: 41 + windows: + - 39 + xorg: 47 + width: 1 - kind: letter name: Dr2 + scancode: + keyman: K_QUOTE + macos: 39 + windows: + - 40 + xorg: 48 + width: 1 - kind: letter name: Dr1 + scancode: + keyman: K_SLASH + windows: + - 43 + xorg: 51 + width: 1 - - - name: El_shift + scancode: + keyman: SHIFT + macos: 57 + windows: + - 42 + xorg: 50 width: 1.5 - kind: letter name: El1 + scancode: + keyman: K_oE2 + windows: + - 86 + xorg: 94 + width: 1 - kind: letter name: El2 + scancode: + keyman: K_Y + macos: 6 + windows: + - 44 + xorg: 52 + width: 1 - kind: letter name: El3 + scancode: + keyman: K_X + macos: 7 + windows: + - 45 + xorg: 53 + width: 1 - kind: letter name: El4 + scancode: + keyman: K_C + macos: 8 + windows: + - 46 + xorg: 54 + width: 1 - kind: letter name: El5 + scancode: + keyman: K_V + macos: 9 + windows: + - 47 + xorg: 55 + width: 1 - kind: letter name: El6 + scancode: + keyman: K_B + macos: 11 + windows: + - 48 + xorg: 56 + width: 1 - - kind: letter name: Er5 + scancode: + keyman: K_N + macos: 45 + windows: + - 49 + xorg: 57 + width: 1 - kind: letter name: Er4 + scancode: + keyman: K_M + macos: 46 + windows: + - 50 + xorg: 58 + width: 1 - kind: letter name: Er3 + scancode: + keyman: K_COMMA + macos: 43 + windows: + - 51 + xorg: 59 + width: 1 - kind: letter name: Er2 + scancode: + keyman: K_PERIOD + macos: 47 + windows: + - 52 + xorg: 60 + width: 1 - kind: letter name: Er1 + scancode: + keyman: K_HYPHEN + macos: 44 + windows: + - 53 + xorg: 61 + width: 1 - name: Er_shift + scancode: + keyman: SHIFT + macos: 60 + windows: + - 54 + xorg: 62 width: 2.35 - - - name: Fl_ctrl + scancode: + keyman: LCTRL + macos: 59 + windows: + - 29 + xorg: 37 width: 1.75 - name: Fl_win + scancode: + keyman: K_?5B + macos: 55 + windows: + - 224 + - 91 + xorg: 133 width: 1.25 - name: Fl_alt + scancode: + keyman: LALT + macos: 58 + windows: + - 56 + xorg: 64 width: 1.25 - name: Fl_space + scancode: + keyman: K_SPACE + macos: 49 + windows: + - 57 + xorg: 65 width: 3 - - name: Fr_space + scancode: + keyman: K_SPACE + macos: 49 + windows: + - 57 + xorg: 65 width: 3 - name: Fr_altgr + scancode: + keyman: RALT + macos: 61 + windows: + - 224 + - 56 + xorg: 108 width: 1.25 - name: Fr_win + scancode: + keyman: K_?5C + macos: 55 + windows: + - 224 + - 92 + xorg: 105 width: 1.25 - name: Fr_menu + scancode: + keyman: K_?5D + windows: + - 224 + - 93 + xorg: 135 width: 1.25 - name: Fr_ctrl + scancode: + keyman: RCTRL + windows: + - 224 + - 29 + xorg: 105 width: 1.25 diff --git a/lulua/keyboard.py b/lulua/keyboard.py index cf96efc..eed58a7 100644 --- a/lulua/keyboard.py +++ b/lulua/keyboard.py @@ -24,281 +24,16 @@ from typing import Text, Dict, Iterator, List from .util import YamlLoader -# XXX move this to keyboard.yaml? -_buttonToXorgKeycode = { - 'Bl1': 49, - 'Bl2': 10, - 'Bl3': 11, - 'Bl4': 12, - 'Bl5': 13, - 'Bl6': 14, - 'Bl7': 15, - 'Br6': 16, - 'Br5': 17, - 'Br4': 18, - 'Br3': 19, - 'Br2': 20, - 'Br1': 21, - 'Br_bs': 22, - 'Cl_tab': 23, - 'Cl1': 24, - 'Cl2': 25, - 'Cl3': 26, - 'Cl4': 27, - 'Cl5': 28, - 'Cr7': 29, - 'Cr6': 30, - 'Cr5': 31, - 'Cr4': 32, - 'Cr3': 33, - 'Cr2': 34, - 'Cr1': 35, - 'CD_ret': 36, - 'Dl_caps': 66, - 'Dl1': 38, - 'Dl2': 39, - 'Dl3': 40, - 'Dl4': 41, - 'Dl5': 42, - 'Dr7': 43, - 'Dr6': 44, - 'Dr5': 45, - 'Dr4': 46, - 'Dr3': 47, - 'Dr2': 48, - 'Dr1': 51, - 'El_shift': 50, - 'El1': 94, - 'El2': 52, - 'El3': 53, - 'El4': 54, - 'El5': 55, - 'El6': 56, - 'Er5': 57, - 'Er4': 58, - 'Er3': 59, - 'Er2': 60, - 'Er1': 61, - 'Er_shift': 62, - 'Fl_ctrl': 37, - 'Fl_win': 133, - 'Fl_alt': 64, - 'Fl_space': 65, - 'Fr_space': 65, - 'Fr_altgr': 108, - 'Fr_win': 105, - 'Fr_menu': 135, - 'Fr_ctrl': 105, - } - -_buttonToKeyman = { - 'Bl1': 'K_BKSLASH', - 'Bl2': 'K_1', - 'Bl3': 'K_2', - 'Bl4': 'K_3', - 'Bl5': 'K_4', - 'Bl6': 'K_5', - 'Bl7': 'K_6', - 'Br6': 'K_7', - 'Br5': 'K_8', - 'Br4': 'K_9', - 'Br3': 'K_0', - 'Br2': 'K_LBRKT', - 'Br1': 'K_RBRKT', - 'Br_bs': 'K_BKSP', - 'Cl_tab': 'K_TAB', - 'Cl1': 'K_Q', - 'Cl2': 'K_W', - 'Cl3': 'K_E', - 'Cl4': 'K_R', - 'Cl5': 'K_T', - 'Cr7': 'K_Z', - 'Cr6': 'K_U', - 'Cr5': 'K_I', - 'Cr4': 'K_O', - 'Cr3': 'K_P', - 'Cr2': 'K_COLON', - 'Cr1': 'K_EQUAL', - 'CD_ret': 'K_ENTER', - 'Dl_caps': 'CAPS', - 'Dl1': 'K_A', - 'Dl2': 'K_S', - 'Dl3': 'K_D', - 'Dl4': 'K_F', - 'Dl5': 'K_G', - 'Dr7': 'K_H', - 'Dr6': 'K_J', - 'Dr5': 'K_K', - 'Dr4': 'K_L', - 'Dr3': 'K_BKQUOTE', - 'Dr2': 'K_QUOTE', - 'Dr1': 'K_SLASH', - 'El_shift': 'SHIFT', # XXX: there is no distinction between left/right - 'El1': 'K_oE2', - 'El2': 'K_Y', - 'El3': 'K_X', - 'El4': 'K_C', - 'El5': 'K_V', - 'El6': 'K_B', - 'Er5': 'K_N', - 'Er4': 'K_M', - 'Er3': 'K_COMMA', - 'Er2': 'K_PERIOD', - 'Er1': 'K_HYPHEN', - 'Er_shift': 'SHIFT', - 'Fl_ctrl': 'LCTRL', - 'Fl_win': 'K_?5B', - 'Fl_alt': 'LALT', - 'Fl_space': 'K_SPACE', - 'Fr_space': 'K_SPACE', - 'Fr_altgr': 'RALT', - 'Fr_win': 'K_?5C', - 'Fr_menu': 'K_?5D', - 'Fr_ctrl': 'RCTRL', - } - -# button windows scancode. See Keyboard Scan Code Specification Revision 1.3a -# (published in 2000) from the Windows Platform Design Notes for example. -_buttonToWinScancode = { - 'Bl1': (0x29, ), - 'Bl2': (0x02, ), - 'Bl3': (0x03, ), - 'Bl4': (0x04, ), - 'Bl5': (0x05, ), - 'Bl6': (0x06, ), - 'Bl7': (0x07, ), - 'Br6': (0x08, ), - 'Br5': (0x09, ), - 'Br4': (0x0A, ), - 'Br3': (0x0B, ), - 'Br2': (0x0C, ), - 'Br1': (0x0D, ), - 'Br_bs': (0x0E, ), - 'Cl_tab': (0x0F, ), - 'Cl1': (0x10, ), - 'Cl2': (0x11, ), - 'Cl3': (0x12, ), - 'Cl4': (0x13, ), - 'Cl5': (0x14, ), - 'Cr7': (0x15, ), - 'Cr6': (0x16, ), - 'Cr5': (0x17, ), - 'Cr4': (0x18, ), - 'Cr3': (0x19, ), - 'Cr2': (0x1A, ), - 'Cr1': (0x1B, ), - 'CD_ret': (0x1C, ), - 'Dl_caps': (0x3A, ), - 'Dl1': (0x1E, ), - 'Dl2': (0x1F, ), - 'Dl3': (0x20, ), - 'Dl4': (0x21, ), - 'Dl5': (0x22, ), - 'Dr7': (0x23, ), - 'Dr6': (0x24, ), - 'Dr5': (0x25, ), - 'Dr4': (0x26, ), - 'Dr3': (0x27, ), - 'Dr2': (0x28, ), - 'Dr1': (0x2B, ), - 'El_shift': (0x2A, ), - 'El1': (0x56, ), - 'El2': (0x2C, ), - 'El3': (0x2D, ), - 'El4': (0x2E, ), - 'El5': (0x2F, ), - 'El6': (0x30, ), - 'Er5': (0x31, ), - 'Er4': (0x32, ), - 'Er3': (0x33, ), - 'Er2': (0x34, ), - 'Er1': (0x35, ), - 'Er_shift': (0x36, ), - 'Fl_ctrl': (0x1D, ), - 'Fl_win': (0xe0, 0x5B, ), - 'Fl_alt': (0x38, ), - 'Fl_space': (0x39, ), - 'Fr_space': (0x39, ), - 'Fr_altgr': (0xe0, 0x38, ), - 'Fr_win': (0xe0, 0x5C, ), - 'Fr_menu': (0xe0, 0x5D, ), - 'Fr_ctrl': (0xe0, 0x1D, ), - } - -# see https://eastmanreference.com/complete-list-of-applescript-key-codes -_buttonToOsxKeycode = { - 'Bl1': 50, - 'Bl2': 18, - 'Bl3': 19, - 'Bl4': 20, - 'Bl5': 21, - 'Bl6': 23, - 'Bl7': 22, - 'Br6': 26, - 'Br5': 28, - 'Br4': 25, - 'Br3': 29, - 'Br2': 27, - 'Br1': 24, - 'Br_bs': 51, - 'Cl_tab': 48, - 'Cl1': 12, - 'Cl2': 13, - 'Cl3': 14, - 'Cl4': 15, - 'Cl5': 17, - 'Cr7': 16, - 'Cr6': 32, - 'Cr5': 34, - 'Cr4': 31, - 'Cr3': 35, - 'Cr2': 33, - 'Cr1': 30, - 'CD_ret': 36, - 'Dl_caps': 57, - 'Dl1': 0, - 'Dl2': 1, - 'Dl3': 2, - 'Dl4': 3, - 'Dl5': 5, - 'Dr7': 4, - 'Dr6': 38, - 'Dr5': 40, - 'Dr4': 37, - 'Dr3': 41, - 'Dr2': 39, - #'Dr1': 51, - 'El_shift': 57, - #'El1': 6, - 'El2': 6, - 'El3': 7, - 'El4': 8, - 'El5': 9, - 'El6': 11, - 'Er5': 45, - 'Er4': 46, - 'Er3': 43, - 'Er2': 47, - 'Er1': 44, - 'Er_shift': 60, - 'Fl_ctrl': 59, - 'Fl_win': 55, - 'Fl_alt': 58, - 'Fl_space': 49, - 'Fr_space': 49, - 'Fr_altgr': 61, - 'Fr_win': 55, - #'Fr_menu': , - #'Fr_ctrl': 105, - } - class Button: - __slots__ = ('width', 'isMarked', 'i') + """ A single physical button on the keyboard """ + + __slots__ = ('width', 'isMarked', 'i', 'scancode') _idToName : Dict[int, Text] = {} _nameToId : Dict[Text, int] = {} _nextNameId = 0 + serializedName = 'standard' - def __init__ (self, name: Text, width: float = 1, isMarked: bool = False): + def __init__ (self, name: Text, width: float = 1, isMarked: bool = False, scancode = None): # map names to integers for fast comparison/hashing i = Button._nameToId.get (name) if i is None: @@ -310,8 +45,14 @@ class Button: self.width = width # marked with an haptic line, for better orientation self.isMarked = isMarked - - def __repr__ (self): + # scancode map, although they are not all technically scancodes, they + # are some low-level representation of the physical key + self.scancode = scancode + # special case for windows + if self.scancode and 'windows' in self.scancode: + self.scancode['windows'] = tuple (self.scancode['windows']) + + def __repr__ (self): # pragma: no cover return f'Button({self.name!r}, {self.width}, {self.isMarked})' def __eq__ (self, other): @@ -326,25 +67,10 @@ class Button: def name (self): return Button._idToName[self.i] - @property - def xorgKeycode (self): - return _buttonToXorgKeycode[self.name] - - @property - def keymanCode (self): - return _buttonToKeyman[self.name] - - @property - def windowsScancode (self): - return _buttonToWinScancode[self.name] - - @property - def osxKeycode (self): - return _buttonToOsxKeycode[self.name] - @classmethod def deserialize (self, data: Dict): - kindMap = {'standard': Button, 'letter': LetterButton, 'multi': MultiRowButton} + kindMap = dict (map (lambda x: (x.serializedName, x), + (Button, LetterButton, MultiRowButton))) try: kind = data['kind'] del data['kind'] @@ -352,15 +78,28 @@ class Button: kind = 'standard' return kindMap[kind] (**data) + def serialize (self): + d = dict (name=self.name, width=self.width, scancode=self.scancode) + if self.__class__ is not Button: + d['kind'] = self.serializedName + if self.isMarked: + d['isMarked'] = self.isMarked + # turn the tuple back into a list + if d['scancode'] and 'windows' in d['scancode']: + d['scancode']['windows'] = list (d['scancode']['windows']) + return d + class LetterButton (Button): """ A letter, number or symbol button, but not special keys like modifier, tab, … """ - def __init__ (self, name, isMarked=False): - super().__init__ (name, width=1, isMarked=isMarked) + serializedName = 'letter' - def __repr__ (self): + def __init__ (self, name, width=1, isMarked=False, scancode=None): + super().__init__ (name, width=width, isMarked=isMarked, scancode=scancode) + + def __repr__ (self): # pragma: no cover return f'LetterButton({self.name!r}, {self.isMarked})' class MultiRowButton (Button): @@ -370,19 +109,26 @@ class MultiRowButton (Button): """ __slots__ = ('span', ) + serializedName = 'multi' - def __init__ (self, name, span, isMarked=False): - super ().__init__ (name, width=1, isMarked=isMarked) + def __init__ (self, name, span, width=1, isMarked=False, scancode=None): + super ().__init__ (name, width=width, isMarked=isMarked, scancode=scancode) self.span = span - def __repr__ (self): + def __repr__ (self): # pragma: no cover return f'MultiRowButton({self.name!r}, {self.span!r}, {self.isMarked!r})' + def serialize (self): + d = super ().serialize () + d['span'] = self.span + return d + class PhysicalKeyboard: - __slots__ = ('name', 'rows', '_buttonToRow') + __slots__ = ('name', 'description', 'rows', '_buttonToRow') - def __init__ (self, name: Text, rows): + def __init__ (self, name: Text, description: Text, rows): self.name = name + self.description = description self.rows = rows self._buttonToRow = dict () @@ -393,7 +139,7 @@ class PhysicalKeyboard: def __iter__ (self): return iter (self.rows) - def __repr__ (self): + def __repr__ (self): # pragma: no cover return f'' def __len__ (self): @@ -405,7 +151,7 @@ class PhysicalKeyboard: for k in self.keys (): if k.name == name: return k - raise AttributeError (f'{name} is not a valid button name') + raise KeyError (f'{name} is not a valid button name') def keys (self) -> Iterator[Button]: """ Iterate over all keys """ @@ -428,7 +174,19 @@ class PhysicalKeyboard: for btn in r: row[1].append (Button.deserialize (btn)) rows.append (row) - return cls (data['name'], rows) + return cls (data['name'], data['description'], rows) + + def serialize (self): + rows = [] + for l, r in self.rows: + newRow = [[], []] + for btn in l: + newRow[0].append (btn.serialize ()) + for btn in r: + newRow[1].append (btn.serialize ()) + rows.append (newRow) + return dict (name=self.name, description=self.description, rows=rows) -defaultKeyboards = YamlLoader ('data/keyboards', PhysicalKeyboard.deserialize) +dataDirectory = 'data/keyboards' +defaultKeyboards = YamlLoader (dataDirectory, PhysicalKeyboard.deserialize) diff --git a/lulua/render.py b/lulua/render.py index 41a6bd5..93d197e 100644 --- a/lulua/render.py +++ b/lulua/render.py @@ -302,6 +302,8 @@ def renderXmodmap (args): keyboard = defaultKeyboards[args.keyboard] layout = defaultLayouts[args.layout].specialize (keyboard) + xorgGetter = lambda x: x.scancode['xorg'] + with open (args.output, 'w') as fd: # inspired by https://neo-layout.org/neo_de.xmodmap fd.write ('\n'.join ([ @@ -321,12 +323,12 @@ def renderXmodmap (args): # layers: 1, 2, 3, 5, 4, None, 6, 7 for i in (0, 1, 2, 4, 3, 99999, 5, 6): if i >= len (layout.layers): - for btn in unique (keyboard.keys (), key=attrgetter ('xorgKeycode')): + for btn in unique (keyboard.keys (), key=xorgGetter): keycodeMap[btn].append ('NoSymbol') continue l = layout.layers[i] # space button shares the same keycode and must be removed - for btn in unique (keyboard.keys (), key=attrgetter ('xorgKeycode')): + for btn in unique (keyboard.keys (), key=xorgGetter): if not layout.isModifier (frozenset ([btn])): text = l.layout.get (btn) if not text: @@ -359,7 +361,7 @@ def renderXmodmap (args): for btn, v in keycodeMap.items (): v = '\t'.join (v) - fd.write (f'!! {btn.name}\nkeycode {btn.xorgKeycode} = {v}\n') + fd.write (f'!! {btn.name}\nkeycode {xorgGetter (btn)} = {v}\n') fd.write ('\n'.join (['add Mod3 = ISO_First_Group', 'add Mod5 = ISO_Level3_Shift', ''])) def renderKeyman (args): @@ -388,11 +390,12 @@ def renderKeyman (args): for i, l in enumerate (layout.layers): for m in l.modifier: for x in m: - if x.keymanCode.startswith ('K_') or x.keymanCode == 'CAPS': + keymanCode = x.scancode['keyman'] + if keymanCode.startswith ('K_') or keymanCode == 'CAPS': logging.error (f'Keyman does not support custom modifier like {m}. Your layout will not work correctly.') break for btn, text in l.layout.items (): - comb = ' '.join ([x.keymanCode for x in m] + [btn.keymanCode]) + comb = ' '.join ([x.scancode['keyman'] for x in m] + [btn.scancode['keyman']]) text = ' '.join ([f'U+{ord (x):04X}' for x in text]) fd.write (f'+ [{comb}] > {text}\n') @@ -494,7 +497,7 @@ def renderWinKbd (args): s = '\r' return s wcharMap = [] - for btn in unique (keyboard.keys (), key=attrgetter ('windowsScancode')): + for btn in unique (keyboard.keys (), key=lambda x: x.scancode['windows']): text = list (layout.getButtonText (btn)) # skip unused keys @@ -502,7 +505,7 @@ def renderWinKbd (args): continue mappedText = [toWindows (s) for s in text] - vk = next (filter (lambda x: isinstance (x, VirtualKey), scancodeToVk[btn.windowsScancode])) + vk = next (filter (lambda x: isinstance (x, VirtualKey), scancodeToVk[btn.scancode['windows']])) wcharMap.append ((vk, 0, mappedText)) fd.write (makeDriverSources (scancodeToVk, wcharMap)) @@ -539,7 +542,7 @@ def renderKeylayout (args): for i, l in enumerate (layout.layers): keymap = ET.SubElement (keymapSet, 'keyMap', index=str (i)) for btn, text in l.layout.items (): - ET.SubElement (keymap, 'key', code=str (btn.osxKeycode), output=text) + ET.SubElement (keymap, 'key', code=str (btn.scancode['macos']), output=text) layouts = ET.SubElement (docroot, 'layouts') layout = ET.SubElement (layouts, 'layout', first='0', last='0', modifiers=str (modmapId), mapSet=str (keymapSetId)) diff --git a/lulua/test_keyboard.py b/lulua/test_keyboard.py index 7537266..d08f6d1 100644 --- a/lulua/test_keyboard.py +++ b/lulua/test_keyboard.py @@ -20,7 +20,8 @@ import pytest -from .keyboard import defaultKeyboards, Button +from .keyboard import defaultKeyboards, Button, dataDirectory +from .util import YamlLoader def test_defaults (): k = defaultKeyboards['ibmpc105'] @@ -54,6 +55,9 @@ def test_keyboard_getattr (): assert k['CD_ret'] == k.find ('CD_ret') assert k['Cr1'] != k.find ('El1') + with pytest.raises (KeyError): + k['nonexistent_button'] + def test_button_uniqname (): a = Button ('a') assert a.name == 'a' @@ -77,3 +81,15 @@ def test_button_uniqname (): d[b] = 2 assert b in d + # make sure we can only compare to Buttons + assert a != 'hello' + assert a != 1 + assert a != dict () + +def test_serialize (): + """ Make sure serialize (deserialize (x)) of keyboards is identity """ + + rawKeyboards = YamlLoader (dataDirectory, lambda x: x) + name = 'ibmpc105' + assert defaultKeyboards[name].serialize () == rawKeyboards[name] + -- cgit v1.2.3