From 6a2262a7f9bf2ab16ecfdfc52e548ef010c76e00 Mon Sep 17 00:00:00 2001 From: Lars-Dominik Braun Date: Sat, 17 Oct 2020 10:27:36 +0200 Subject: winkbd: Improve rendering Create self-containted, minimal, but still human-readable C file with all typedefs. Gets rid of the copied header file and all of its weird ifdef logic. Simplifies virtual scancode to virtual key mapping. --- lulua/winkbd.py | 825 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 825 insertions(+) create mode 100644 lulua/winkbd.py (limited to 'lulua/winkbd.py') diff --git a/lulua/winkbd.py b/lulua/winkbd.py new file mode 100644 index 0000000..0f4dffc --- /dev/null +++ b/lulua/winkbd.py @@ -0,0 +1,825 @@ +# Copyright (c) 2019 lulua contributors +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +""" +Windows keyboard layout driver generation +""" + +from enum import IntEnum +from operator import attrgetter +from itertools import groupby + +class CDefEnum (IntEnum): + @property + def cdefName (self): + return f'{self.__class__.__name__.upper()}_{self.name.upper()}' + +# Virtal key definitions, see +# https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes +vkval = dict ( + LBUTTON = 0x01, + RBUTTON = 0x02, + CANCEL = 0x03, + MBUTTON = 0x04, + XBUTTON1 = 0x05, + XBUTTON2 = 0x06, + + BACK = 0x08, + TAB = 0x09, + + CLEAR = 0x0C, + RETURN = 0x0D, + + SHIFT = 0x10, + CONTROL = 0x11, + MENU = 0x12, + PAUSE = 0x13, + CAPITAL = 0x14, + + KANA = 0x15, + HANGEUL = 0x15, + HANGUL = 0x15, + JUNJA = 0x17, + FINAL = 0x18, + HANJA = 0x19, + KANJI = 0x19, + + ESCAPE = 0x1B, + + CONVERT = 0x1C, + NONCONVERT = 0x1D, + ACCEPT = 0x1E, + MODECHANGE = 0x1F, + + SPACE = 0x20, + PRIOR = 0x21, + NEXT = 0x22, + END = 0x23, + HOME = 0x24, + LEFT = 0x25, + UP = 0x26, + RIGHT = 0x27, + DOWN = 0x28, + SELECT = 0x29, + PRINT = 0x2A, + EXECUTE = 0x2B, + SNAPSHOT = 0x2C, + INSERT = 0x2D, + DELETE = 0x2E, + HELP = 0x2F, + + LWIN = 0x5B, + RWIN = 0x5C, + APPS = 0x5D, + + SLEEP = 0x5F, + + NUMPAD0 = 0x60, + NUMPAD1 = 0x61, + NUMPAD2 = 0x62, + NUMPAD3 = 0x63, + NUMPAD4 = 0x64, + NUMPAD5 = 0x65, + NUMPAD6 = 0x66, + NUMPAD7 = 0x67, + NUMPAD8 = 0x68, + NUMPAD9 = 0x69, + MULTIPLY = 0x6A, + ADD = 0x6B, + SEPARATOR = 0x6C, + SUBTRACT = 0x6D, + DECIMAL = 0x6E, + DIVIDE = 0x6F, + F1 = 0x70, + F2 = 0x71, + F3 = 0x72, + F4 = 0x73, + F5 = 0x74, + F6 = 0x75, + F7 = 0x76, + F8 = 0x77, + F9 = 0x78, + F10 = 0x79, + F11 = 0x7A, + F12 = 0x7B, + F13 = 0x7C, + F14 = 0x7D, + F15 = 0x7E, + F16 = 0x7F, + F17 = 0x80, + F18 = 0x81, + F19 = 0x82, + F20 = 0x83, + F21 = 0x84, + F22 = 0x85, + F23 = 0x86, + F24 = 0x87, + + NUMLOCK = 0x90, + SCROLL = 0x91, + + OEM_NEC_EQUAL = 0x92, + + OEM_FJ_JISHO = 0x92, + OEM_FJ_MASSHOU = 0x93, + OEM_FJ_TOUROKU = 0x94, + OEM_FJ_LOYA = 0x95, + OEM_FJ_ROYA = 0x96, + + LSHIFT = 0xA0, + RSHIFT = 0xA1, + LCONTROL = 0xA2, + RCONTROL = 0xA3, + LMENU = 0xA4, + RMENU = 0xA5, + + BROWSER_BACK = 0xA6, + BROWSER_FORWARD = 0xA7, + BROWSER_REFRESH = 0xA8, + BROWSER_STOP = 0xA9, + BROWSER_SEARCH = 0xAA, + BROWSER_FAVORITES = 0xAB, + BROWSER_HOME = 0xAC, + + VOLUME_MUTE = 0xAD, + VOLUME_DOWN = 0xAE, + VOLUME_UP = 0xAF, + MEDIA_NEXT_TRACK = 0xB0, + MEDIA_PREV_TRACK = 0xB1, + MEDIA_STOP = 0xB2, + MEDIA_PLAY_PAUSE = 0xB3, + LAUNCH_MAIL = 0xB4, + LAUNCH_MEDIA_SELECT = 0xB5, + LAUNCH_APP1 = 0xB6, + LAUNCH_APP2 = 0xB7, + + OEM_1 = 0xBA, + OEM_PLUS = 0xBB, + OEM_COMMA = 0xBC, + OEM_MINUS = 0xBD, + OEM_PERIOD = 0xBE, + OEM_2 = 0xBF, + OEM_3 = 0xC0, + + ABNT_C1 = 0xC1, + ABNT_C2 = 0xC2, + + OEM_4 = 0xDB, + OEM_5 = 0xDC, + OEM_6 = 0xDD, + OEM_7 = 0xDE, + OEM_8 = 0xDF, + + OEM_AX = 0xE1, + OEM_102 = 0xE2, + ICO_HELP = 0xE3, + ICO_00 = 0xE4, + + PROCESSKEY = 0xE5, + + ICO_CLEAR = 0xE6, + + PACKET = 0xE7, + + OEM_RESET = 0xE9, + OEM_JUMP = 0xEA, + OEM_PA1 = 0xEB, + OEM_PA2 = 0xEC, + OEM_PA3 = 0xED, + OEM_WSCTRL = 0xEE, + OEM_CUSEL = 0xEF, + OEM_ATTN = 0xF0, + OEM_FINISH = 0xF1, + OEM_COPY = 0xF2, + OEM_AUTO = 0xF3, + OEM_ENLW = 0xF4, + OEM_BACKTAB = 0xF5, + + ATTN = 0xF6, + CRSEL = 0xF7, + EXSEL = 0xF8, + EREOF = 0xF9, + PLAY = 0xFA, + ZOOM = 0xFB, + NONAME = 0xFC, + PA1 = 0xFD, + OEM_CLEAR = 0xFE, + + # invalid + NULL = 0xFF, + ) + +# Add ASCII numbers and letters +for i in range (ord ('0'), ord ('9')+1): + vkval[f'NUMBER{chr (i)}'] = i +for i in range (ord ('A'), ord ('Z')+1): + vkval[chr (i)] = i + +VirtualKey = CDefEnum ('VirtualKey', vkval) + +class VirtualKeyFlag (CDefEnum): + # The Windows API returns EXT as KEYEVENTF_EXTENDEDKEY, see + # https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-keybdinput + EXT = 0x0100 + MULTIVK = 0x0200 + SPECIAL = 0x0400 + NUMPAD = 0x0800 + UNICODE = 0x1000 + INJECTEDVK = 0x2000 + MAPPEDVK = 0x4000 + BREAK = 0x8000 + +class WChar (CDefEnum): + """ Wide character symbols """ + NONE = 0xF000 # unused slot + +# Default scancode to VirtualKey translation for qwerty +qwertyScancodeToVk = { + (0x01, ): (VirtualKey.ESCAPE, ), + (0x02, ): (VirtualKey.NUMBER1, ), + (0x03, ): (VirtualKey.NUMBER2, ), + (0x04, ): (VirtualKey.NUMBER3, ), + (0x05, ): (VirtualKey.NUMBER4, ), + (0x06, ): (VirtualKey.NUMBER5, ), + (0x07, ): (VirtualKey.NUMBER6, ), + (0x08, ): (VirtualKey.NUMBER7, ), + (0x09, ): (VirtualKey.NUMBER8, ), + (0x0A, ): (VirtualKey.NUMBER9, ), + (0x0B, ): (VirtualKey.NUMBER0, ), + (0x0C, ): (VirtualKey.OEM_MINUS, ), + (0x0D, ): (VirtualKey.OEM_PLUS, ), + (0x0E, ): (VirtualKey.BACK, ), + (0x0F, ): (VirtualKey.TAB, ), + (0x10, ): (VirtualKey.Q, ), + (0x11, ): (VirtualKey.W, ), + (0x12, ): (VirtualKey.E, ), + (0x13, ): (VirtualKey.R, ), + (0x14, ): (VirtualKey.T, ), + (0x15, ): (VirtualKey.Y, ), + (0x16, ): (VirtualKey.U, ), + (0x17, ): (VirtualKey.I, ), + (0x18, ): (VirtualKey.O, ), + (0x19, ): (VirtualKey.P, ), + (0x1A, ): (VirtualKey.OEM_4, ), + (0x1B, ): (VirtualKey.OEM_6, ), + (0x1C, ): (VirtualKey.RETURN, ), + (0x1D, ): (VirtualKey.LCONTROL, ), + (0x1E, ): (VirtualKey.A, ), + (0x1F, ): (VirtualKey.S, ), + (0x20, ): (VirtualKey.D, ), + (0x21, ): (VirtualKey.F, ), + (0x22, ): (VirtualKey.G, ), + (0x23, ): (VirtualKey.H, ), + (0x24, ): (VirtualKey.J, ), + (0x25, ): (VirtualKey.K, ), + (0x26, ): (VirtualKey.L, ), + (0x27, ): (VirtualKey.OEM_1, ), + (0x28, ): (VirtualKey.OEM_7, ), + (0x29, ): (VirtualKey.OEM_3, ), + (0x2A, ): (VirtualKey.LSHIFT, ), + (0x2B, ): (VirtualKey.OEM_5, ), + (0x2C, ): (VirtualKey.Z, ), + (0x2D, ): (VirtualKey.X, ), + (0x2E, ): (VirtualKey.C, ), + (0x2F, ): (VirtualKey.V, ), + (0x30, ): (VirtualKey.B, ), + (0x31, ): (VirtualKey.N, ), + (0x32, ): (VirtualKey.M, ), + (0x33, ): (VirtualKey.OEM_COMMA, ), + (0x34, ): (VirtualKey.OEM_PERIOD, ), + (0x35, ): (VirtualKey.OEM_2, ), + (0x36, ): (VirtualKey.RSHIFT, VirtualKeyFlag.EXT), # Right-hand shift needs ext bit (XXX why?) + (0x37, ): (VirtualKey.MULTIPLY, VirtualKeyFlag.MULTIVK), + (0x38, ): (VirtualKey.LMENU, ), + (0x39, ): (VirtualKey.SPACE, ), + (0x3A, ): (VirtualKey.CAPITAL, ), + (0x3B, ): (VirtualKey.F1, ), + (0x3C, ): (VirtualKey.F2, ), + (0x3D, ): (VirtualKey.F3, ), + (0x3E, ): (VirtualKey.F4, ), + (0x3F, ): (VirtualKey.F5, ), + (0x40, ): (VirtualKey.F6, ), + (0x41, ): (VirtualKey.F7, ), + (0x42, ): (VirtualKey.F8, ), + (0x43, ): (VirtualKey.F9, ), + (0x44, ): (VirtualKey.F10, ), + (0x45, ): (VirtualKey.NUMLOCK, VirtualKeyFlag.EXT, VirtualKeyFlag.MULTIVK), + (0x46, ): (VirtualKey.SCROLL, VirtualKeyFlag.MULTIVK), + (0x47, ): (VirtualKey.HOME, VirtualKeyFlag.NUMPAD, VirtualKeyFlag.SPECIAL), + (0x48, ): (VirtualKey.UP, VirtualKeyFlag.NUMPAD, VirtualKeyFlag.SPECIAL), + (0x49, ): (VirtualKey.PRIOR, VirtualKeyFlag.NUMPAD, VirtualKeyFlag.SPECIAL), + (0x4A, ): (VirtualKey.SUBTRACT, ), + (0x4B, ): (VirtualKey.LEFT, VirtualKeyFlag.NUMPAD, VirtualKeyFlag.SPECIAL), + (0x4C, ): (VirtualKey.CLEAR, VirtualKeyFlag.NUMPAD, VirtualKeyFlag.SPECIAL), + (0x4D, ): (VirtualKey.RIGHT, VirtualKeyFlag.NUMPAD, VirtualKeyFlag.SPECIAL), + (0x4E, ): (VirtualKey.ADD, ), + (0x4F, ): (VirtualKey.END, VirtualKeyFlag.NUMPAD, VirtualKeyFlag.SPECIAL), + (0x50, ): (VirtualKey.DOWN, VirtualKeyFlag.NUMPAD, VirtualKeyFlag.SPECIAL), + (0x51, ): (VirtualKey.NEXT, VirtualKeyFlag.NUMPAD, VirtualKeyFlag.SPECIAL), + (0x52, ): (VirtualKey.INSERT, VirtualKeyFlag.NUMPAD, VirtualKeyFlag.SPECIAL), + (0x53, ): (VirtualKey.DELETE, VirtualKeyFlag.NUMPAD, VirtualKeyFlag.SPECIAL), + (0x54, ): (VirtualKey.SNAPSHOT, ), + (0x56, ): (VirtualKey.OEM_102, ), + (0x57, ): (VirtualKey.F11, ), + (0x58, ): (VirtualKey.F12, ), + (0x59, ): (VirtualKey.CLEAR, ), + (0x5A, ): (VirtualKey.OEM_WSCTRL, ), + (0x5B, ): (VirtualKey.OEM_FINISH, ), + (0x5C, ): (VirtualKey.OEM_JUMP, ), + (0x5D, ): (VirtualKey.EREOF, ), + (0x5E, ): (VirtualKey.OEM_BACKTAB, ), + (0x5F, ): (VirtualKey.OEM_AUTO, ), + (0x62, ): (VirtualKey.ZOOM, ), + (0x63, ): (VirtualKey.HELP, ), + (0x64, ): (VirtualKey.F13, ), + (0x65, ): (VirtualKey.F14, ), + (0x66, ): (VirtualKey.F15, ), + (0x67, ): (VirtualKey.F16, ), + (0x68, ): (VirtualKey.F17, ), + (0x69, ): (VirtualKey.F18, ), + (0x6A, ): (VirtualKey.F19, ), + (0x6B, ): (VirtualKey.F20, ), + (0x6C, ): (VirtualKey.F21, ), + (0x6D, ): (VirtualKey.F22, ), + (0x6E, ): (VirtualKey.F23, ), + (0x6F, ): (VirtualKey.OEM_PA3, ), + (0x71, ): (VirtualKey.OEM_RESET, ), + (0x73, ): (VirtualKey.ABNT_C1, ), + (0x76, ): (VirtualKey.F24, ), + (0x7B, ): (VirtualKey.OEM_PA1, ), + (0x7C, ): (VirtualKey.TAB, ), + (0x7E, ): (VirtualKey.ABNT_C2, ), + (0x7F, ): (VirtualKey.OEM_PA2, ), + + # Prefixed codes: E0 + (0xe0, 0x10): (VirtualKey.MEDIA_PREV_TRACK, VirtualKeyFlag.EXT), + (0xe0, 0x19): (VirtualKey.MEDIA_NEXT_TRACK, VirtualKeyFlag.EXT), + (0xe0, 0x1C): (VirtualKey.RETURN, VirtualKeyFlag.EXT), + (0xe0, 0x1D): (VirtualKey.RCONTROL, VirtualKeyFlag.EXT), + (0xe0, 0x20): (VirtualKey.VOLUME_MUTE, VirtualKeyFlag.EXT), + (0xe0, 0x21): (VirtualKey.LAUNCH_APP2, VirtualKeyFlag.EXT), + (0xe0, 0x22): (VirtualKey.MEDIA_PLAY_PAUSE, VirtualKeyFlag.EXT), + (0xe0, 0x24): (VirtualKey.MEDIA_STOP, VirtualKeyFlag.EXT), + (0xe0, 0x2E): (VirtualKey.VOLUME_DOWN, VirtualKeyFlag.EXT), + (0xe0, 0x30): (VirtualKey.VOLUME_UP, VirtualKeyFlag.EXT), + (0xe0, 0x32): (VirtualKey.BROWSER_HOME, VirtualKeyFlag.EXT), + (0xe0, 0x35): (VirtualKey.DIVIDE, VirtualKeyFlag.EXT), + (0xe0, 0x37): (VirtualKey.SNAPSHOT, VirtualKeyFlag.EXT), + (0xe0, 0x38): (VirtualKey.RMENU, VirtualKeyFlag.EXT), + (0xe0, 0x46): (VirtualKey.CANCEL, VirtualKeyFlag.EXT), + (0xe0, 0x47): (VirtualKey.HOME, VirtualKeyFlag.EXT), + (0xe0, 0x48): (VirtualKey.UP, VirtualKeyFlag.EXT), + (0xe0, 0x49): (VirtualKey.PRIOR, VirtualKeyFlag.EXT), + (0xe0, 0x4B): (VirtualKey.LEFT, VirtualKeyFlag.EXT), + (0xe0, 0x4D): (VirtualKey.RIGHT, VirtualKeyFlag.EXT), + (0xe0, 0x4F): (VirtualKey.END, VirtualKeyFlag.EXT), + (0xe0, 0x50): (VirtualKey.DOWN, VirtualKeyFlag.EXT), + (0xe0, 0x51): (VirtualKey.NEXT, VirtualKeyFlag.EXT), + (0xe0, 0x52): (VirtualKey.INSERT, VirtualKeyFlag.EXT), + (0xe0, 0x53): (VirtualKey.DELETE, VirtualKeyFlag.EXT), + (0xe0, 0x5B): (VirtualKey.LWIN, VirtualKeyFlag.EXT), + (0xe0, 0x5C): (VirtualKey.RWIN, VirtualKeyFlag.EXT), + (0xe0, 0x5D): (VirtualKey.APPS, VirtualKeyFlag.EXT), + # XXX what is VK_POWER’s value? + #(0xe0, 0x5E): (VirtualKey.POWER, VirtualKeyFlag.EXT), + (0xe0, 0x5F): (VirtualKey.SLEEP, VirtualKeyFlag.EXT), + (0xe0, 0x65): (VirtualKey.BROWSER_SEARCH, VirtualKeyFlag.EXT), + (0xe0, 0x66): (VirtualKey.BROWSER_FAVORITES, VirtualKeyFlag.EXT), + (0xe0, 0x67): (VirtualKey.BROWSER_REFRESH, VirtualKeyFlag.EXT), + (0xe0, 0x68): (VirtualKey.BROWSER_STOP, VirtualKeyFlag.EXT), + (0xe0, 0x69): (VirtualKey.BROWSER_FORWARD, VirtualKeyFlag.EXT), + (0xe0, 0x6A): (VirtualKey.BROWSER_BACK, VirtualKeyFlag.EXT), + (0xe0, 0x6B): (VirtualKey.LAUNCH_APP1, VirtualKeyFlag.EXT), + (0xe0, 0x6C): (VirtualKey.LAUNCH_MAIL, VirtualKeyFlag.EXT), + (0xe0, 0x6D): (VirtualKey.LAUNCH_MEDIA_SELECT, VirtualKeyFlag.EXT), + + # Prefixed codes: E1 + (0xe1, 0x1d): (VirtualKey.PAUSE, ), + } + +def enumToCDefine (e): + """ + Transform Python Enum into a bunch of #define statements + """ + prefix = e.__name__.upper () + ret = [f'/* {e.__name__} */'] + for name, member in e.__members__.items (): + ret.append (f'#define {member.cdefName} (0x{member:X}u)') + ret.append ('') + return '\n'.join (ret) + +def enumOr (v): + """ + Turn list of enum values v into C OR statement + """ + return ' | '.join (map (attrgetter ('cdefName'), v)) + +def scancodeToVkTables (m): + """ + Transform scancode to virtual key map m into C arrays + """ + # non-prefixed scancodes + ret = ['/* mappings from scancode to virtual key */', + '/* non-prefixed scancodes */', + 'static unsigned short ausVK[] = {'] + for i in range (0, 0x7f+1): + v = m.get ((i, ), (VirtualKey.NULL, )) + ret.append (f'\t /* {i:02X} */ {enumOr (v)},') + ret.append ('\t};\n') + + # now E0 and E1, must be sorted + for escape in (0xe0, 0xe1): + ret.extend ([f'/* scancodes prefixed by {escape:X} */', f'static VSC_VK a{escape:X}VscToVk[] = {{']) + f = lambda x: len (x[0]) == 2 and x[0][0] == escape + for k, v in sorted (filter (f, m.items ()), key=lambda x: x[0]): + ret.append (f'\t{{ 0x{k[1]:x}, {enumOr (v)} }},') + ret.append ('\t};\n') + + return '\n'.join (ret) + +qwertyScancodeToName = { + (0x01, ): "Esc", + (0x0e, ): "Backspace", + (0x0f, ): "Tab", + (0x1c, ): "Enter", + (0x1d, ): "Ctrl", + (0x2a, ): "Shift", + (0x36, ): "Right Shift", + (0x37, ): "Num *", + (0x38, ): "Alt", + (0x39, ): "Space", + (0x3a, ): "Caps Lock", + (0x3b, ): "F1", + (0x3c, ): "F2", + (0x3d, ): "F3", + (0x3e, ): "F4", + (0x3f, ): "F5", + (0x40, ): "F6", + (0x41, ): "F7", + (0x42, ): "F8", + (0x43, ): "F9", + (0x44, ): "F10", + (0x45, ): "Pause", + (0x46, ): "Scroll Lock", + (0x47, ): "Num 7", + (0x48, ): "Num 8", + (0x49, ): "Num 9", + (0x4a, ): "Num -", + (0x4b, ): "Num 4", + (0x4c, ): "Num 5", + (0x4d, ): "Num 6", + (0x4e, ): "Num +", + (0x4f, ): "Num 1", + (0x50, ): "Num 2", + (0x51, ): "Num 3", + (0x52, ): "Num 0", + (0x53, ): "Num Del", + (0x54, ): "Sys Req", + (0x57, ): "F11", + (0x58, ): "F12", + (0x7c, ): "F13", + (0x7d, ): "F14", + (0x7e, ): "F15", + (0x7f, ): "F16", + (0x80, ): "F17", + (0x81, ): "F18", + (0x82, ): "F19", + (0x83, ): "F20", + (0x84, ): "F21", + (0x85, ): "F22", + (0x86, ): "F23", + (0x87, ): "F24", + + # With E0 prefix + (0xe0, 0x1c): "Num Enter", + (0xe0, 0x1d): "Right Ctrl", + (0xe0, 0x35): "Num /", + (0xe0, 0x37): "Prnt Scrn", + (0xe0, 0x38): "Right Alt", + (0xe0, 0x45): "Num Lock", + (0xe0, 0x46): "Break", + (0xe0, 0x47): "Home", + (0xe0, 0x48): "Up", + (0xe0, 0x49): "Page Up", + (0xe0, 0x4b): "Left", + (0xe0, 0x4d): "Right", + (0xe0, 0x4f): "End", + (0xe0, 0x50): "Down", + (0xe0, 0x51): "Page Down", + (0xe0, 0x52): "Insert", + (0xe0, 0x53): "Delete", + (0xe0, 0x54): "<00>", + (0xe0, 0x56): "Help", + (0xe0, 0x5b): "Left Windows", + (0xe0, 0x5c): "Right Windows", + (0xe0, 0x5d): "Application", + } + +def scancodeToName (m): + """ Create virtual scancode to name mapping tables """ + + ret = [] + + # first unprefixed keys + ret.extend (['/* Virtual scancode to key name */', + 'static VSC_LPWSTR aKeyNames[] = {']) + f = lambda x: len (x[0]) == 1 + for k, v in sorted (filter (f, m.items ()), key=lambda x: x[0]): + ret.append (f'\t{{0x{k[0]:02x}, L"{v}"}},') + ret.extend (['\t{0x00, NULL},', '};', '']) + + ret.extend (['/* Virtual scan code (E0 prefixed) to key name */', + 'static VSC_LPWSTR aKeyNamesExt[] = {']) + f = lambda x: len (x[0]) == 2 and x[0][0] == 0xe0 + for k, v in sorted (filter (f, m.items ()), key=lambda x: x[0]): + ret.append (f'\t{{0x{k[1]:02x}, L"{v}"}},') + ret.extend (['\t{0x00, NULL},', '};', '']) + + return '\n'.join (ret) + +vkToBitsTable = [ + (VirtualKey.SHIFT, (1<<0)), + (VirtualKey.CONTROL, (1<<1)), + (VirtualKey.MENU, (1<<2)), + (VirtualKey.OEM_8, (1<<3)), + (VirtualKey.OEM_102, (1<<4)), + ] + +def vkToBits (): + ret = ['/* maps virtual keys (first value) to shift bitfield value (second value) */', + 'static VK_TO_BIT aVkToBits[] = {'] + for vk, bits in vkToBitsTable: + ret.append (f'\t{{{vk.cdefName}, 0x{bits:x}}},') + ret.append ('\t{0, 0x0}') + ret.append ('\t};\n') + return '\n'.join (ret) + +def charModifiers (): + # array index is layer number + vkToLayer = list (map (set, [ + tuple (), # base + (VirtualKey.SHIFT, ), + (VirtualKey.OEM_102, ), + (VirtualKey.OEM_8, ), + (VirtualKey.SHIFT, VirtualKey.OEM_102), + (VirtualKey.OEM_8, VirtualKey.OEM_102), + (VirtualKey.CONTROL, ), + (VirtualKey.SHIFT, VirtualKey.CONTROL), + (VirtualKey.SHIFT, VirtualKey.OEM_8), + ])) + disabled = 0x0F + + ret = ['/* maps a shift bitfield value (array index) to a layer number in', + 'virtual key translation (VK_TO_WCHARS, array value) */', + 'static MODIFIERS CharModifiers = {', + '\t&aVkToBits[0],', + '\t24,', + ] + ret.append ('\t{') + for i in range (25): + keys = set (map (lambda x: x[0], filter (lambda x: i & x[1], vkToBitsTable))) + try: + layer = vkToLayer.index (keys) + except ValueError: + layer = disabled + keysComment = enumOr (keys) if not layer == disabled else "disabled" + ret.append (f'\t\t0x{layer:x}, /* {keysComment} */') + ret.append ('\t}};\n') + return '\n'.join (ret) + +def vkToWchar (m): + """ Mapping from virtual key to character """ + + ret = [] + retTbl = ['/* table of virtual key to wchar mapping tables */', + 'static VK_TO_WCHAR_TABLE aVkToWcharTable[] = {'] + + def generate (n, g, defPrefix=''): + defname = f'aVkToWch{defPrefix}{n}' + ret.extend ([f'/* map virtual key to flags and {n} unicode output characters */', + f'static VK_TO_WCHARS{n} {defname}[] = {{']) + for vk, flags, chars in g: + def toRepr (s): + if s is None: + return WChar.NONE.cdefName + elif len (s) != 1: + # everything else belongs to ligature tables, which we + # don’t support. + raise Exception (f'only single-character strings are supported ({s!r})') + else: + return f'0x{ord (s):04X}u /*{repr (s)}*/' + chars = ', '.join (map (toRepr, chars)) + ret.append (f'\t{{{vk.cdefName}, {flags}, {{{chars}}}}},') + ret.extend ([f'\t{{0, 0, {{{("0, "*n)}}}}},', '\t};', '']) + # add the new table + retTbl.append (f'\t{{(PVK_TO_WCHARS1) {defname}, {n}, sizeof({defname}[0])}},') + + f = lambda x: len (x[2]) + m = groupby (sorted (m, key=f), key=f) + for n, g in m: + generate (n, g) + + # We are almost always going to need the numpad keys. They also need to be + # last, so translation from string to virtual key does not map them. + numpad = [ + (VirtualKey.NUMPAD0, 0, '0'), + (VirtualKey.NUMPAD1, 0, '1'), + (VirtualKey.NUMPAD2, 0, '2'), + (VirtualKey.NUMPAD3, 0, '3'), + (VirtualKey.NUMPAD4, 0, '4'), + (VirtualKey.NUMPAD5, 0, '5'), + (VirtualKey.NUMPAD6, 0, '6'), + (VirtualKey.NUMPAD7, 0, '7'), + (VirtualKey.NUMPAD8, 0, '8'), + (VirtualKey.NUMPAD9, 0, '9'), + ] + generate (1, numpad, 'Num') + + retTbl.extend (['\t{NULL, 0, 0},', '\t};']) + return '\n'.join (ret + retTbl) + +typedefs = """ +#include + +typedef struct { + unsigned char Vk; + unsigned char ModBits; +} VK_TO_BIT, *PVK_TO_BIT; + +typedef struct { + PVK_TO_BIT pVkToBit; + unsigned short wMaxModBits; + unsigned char ModNumber[]; +} MODIFIERS, *PMODIFIERS; + +typedef struct _VSC_VK { + unsigned char Vsc; + unsigned short Vk; +} VSC_VK, *PVSC_VK; +""" + +for n in range (1, 9): + typedefs += f""" +typedef struct _VK_TO_WCHARS{n} {{ + unsigned char VirtualKey; + unsigned char Attributes; + wchar_t wch[{n}]; +}} VK_TO_WCHARS{n}, *PVK_TO_WCHARS{n}; +""" + +for n in (1, ): + typedefs += f""" +typedef struct _LIGATURE{n} {{ + unsigned char VirtualKey; + unsigned short ModificationNumber; + wchar_t wch[{n}]; +}} LIGATURE{n}, *PLIGATURE{n}; +""" + +typedefs += """ +typedef struct _VK_TO_WCHAR_TABLE { + PVK_TO_WCHARS1 pVkToWchars; + unsigned char nModifications; + unsigned char cbSize; +} VK_TO_WCHAR_TABLE, *PVK_TO_WCHAR_TABLE; + +typedef struct { + unsigned long dwBoth; + wchar_t wchComposed; + unsigned short uFlags; +} DEADKEY, *PDEADKEY; + +typedef struct { + unsigned char vsc; + wchar_t *pwsz; +} VSC_LPWSTR, *PVSC_LPWSTR; + +typedef struct tagKbdLayer { + /* + * Modifier keys + */ + PMODIFIERS pCharModifiers; + + /* + * Characters + */ + PVK_TO_WCHAR_TABLE pVkToWcharTable; // ptr to tbl of ptrs to tbl + + /* + * Diacritics + */ + PDEADKEY pDeadKey; + + /* + * Names of Keys + */ + PVSC_LPWSTR pKeyNames; + PVSC_LPWSTR pKeyNamesExt; + wchar_t **pKeyNamesDead; + + /* + * Scan codes to Virtual Keys + */ + unsigned short *pusVSCtoVK; + unsigned char bMaxVSCtoVK; + PVSC_VK pVSCtoVK_E0; // Scancode has E0 prefix + PVSC_VK pVSCtoVK_E1; // Scancode has E1 prefix + + /* + * Locale-specific special processing + */ + unsigned long fLocaleFlags; + + /* + * Ligatures + */ + unsigned char nLgMax; + unsigned char cbLgEntry; + PLIGATURE1 pLigature; + + /* + * Type and subtype. These are optional. + */ + unsigned long dwType; // Keyboard Type + unsigned long dwSubType; // Keyboard SubType: may contain OemId +} KBDTABLES, *PKBDTABLES; +""" + +entrypoint = """ +#define KBD_VERSION (1) + +static KBDTABLES KbdTables = { + pCharModifiers: &CharModifiers, + + pVkToWcharTable: aVkToWcharTable, + + pDeadKey: NULL, + + pKeyNames: aKeyNames, + pKeyNamesExt: aKeyNamesExt, + pKeyNamesDead: NULL, + + pusVSCtoVK: ausVK, + bMaxVSCtoVK: sizeof(ausVK) / sizeof(*ausVK), + pVSCtoVK_E0: aE0VscToVk, + pVSCtoVK_E1: aE1VscToVk, + + fLocaleFlags: (0 & 0xffffUL) | ((KBD_VERSION & 0xffffUL) << 16), + + nLgMax: 0, + cbLgEntry: 0, + pLigature: NULL, + + dwType: 0, + dwSubType: 0, +}; + +/* The main entry point of the driver + */ +PKBDTABLES KbdLayerDescriptor() { + return &KbdTables; +} +""" + +def makeDriverSources (scancodes, charmap): + """ + Create a single file keyboard driver. + + scancodes is a mapping like qwertyScancodeToVk from virtual scancode to + virtual key. charmap is a list of triples (virtual key, flags, [list of + strings]). + """ + + ret = [ + typedefs, + enumToCDefine (VirtualKey), + enumToCDefine (VirtualKeyFlag), + enumToCDefine (WChar), + scancodeToVkTables (scancodes), + scancodeToName (qwertyScancodeToName), + vkToBits (), + charModifiers (), + vkToWchar (charmap), + entrypoint + ] + return '\n'.join (ret) + +__all__ = ('VirtualKey', 'VirtualKeyFlag') + -- cgit v1.2.3