Path: blob/main/tools/maint/create_dom_pk_codes.py
4150 views
#!/usr/bin/env python31# Copyright 2017 The Emscripten Authors. All rights reserved.2# Emscripten is available under two separate licenses, the MIT license and the3# University of Illinois/NCSA Open Source License. Both these licenses can be4# found in the LICENSE file.56# The DOM KeyboardEvent field 'code' contains a locale/language independent7# identifier of a pressed key on the keyboard, i.e. a "physical" keyboard code, which8# is often useful for games that want to provide a physical keyboard layout that does9# not get confused by the language setting the user has.1011# For example, in Unreal Engine 4 developer mode, the physical keyboard key above the Tab12# but below the Esc key should open up the developer console, independent of which keyboard13# layout is active. This key produces the backquote (`) character on US keyboard layout,14# whereas on the Finnish/Swedish keyboard layout, it generates but the section sign (ยง)15# character. Other keyboard layouts might give different characters, and independent of16# which character is produced, we would like to generate a layout-agnostic identifier for17# the key at this physical location on the keyboard.1819# The DOM KeyboardEvent field 'code' provides such a layout-agnostic identifier.20# Unfortunately this identifier is not an integral ID that could be used as an enum21# or #define, but it is a human-readable English language string that represents the22# physical key. This is very inconvenient for most applications.2324# This utility script creates a mapping from the different documented values of the25# KeyboardEvent 'code' field, to integral IDs that can be easily used to identify26# physical key locations. This mapping is implemented by constructing a hash function27# that is a perfect hash (https://en.wikipedia.org/wiki/Perfect_hash_function) of the28# known strings.2930# Use #include <emscripten/dom_pk_codes.h> in your code to access these IDs.3132import os33import sys34import random3536input_strings = [37(0x0, 'Unidentified', 'DOM_PK_UNKNOWN'),38(0x1, 'Escape', 'DOM_PK_ESCAPE'),39(0x2, 'Digit0', 'DOM_PK_0'),40(0x3, 'Digit1', 'DOM_PK_1'),41(0x4, 'Digit2', 'DOM_PK_2'),42(0x5, 'Digit3', 'DOM_PK_3'),43(0x6, 'Digit4', 'DOM_PK_4'),44(0x7, 'Digit5', 'DOM_PK_5'),45(0x8, 'Digit6', 'DOM_PK_6'),46(0x9, 'Digit7', 'DOM_PK_7'),47(0xA, 'Digit8', 'DOM_PK_8'),48(0xB, 'Digit9', 'DOM_PK_9'),49(0xC, 'Minus', 'DOM_PK_MINUS'),50(0xD, 'Equal', 'DOM_PK_EQUAL'),51(0xE, 'Backspace', 'DOM_PK_BACKSPACE'),52(0xF, 'Tab', 'DOM_PK_TAB'),53(0x10, 'KeyQ', 'DOM_PK_Q'),54(0x11, 'KeyW', 'DOM_PK_W'),55(0x12, 'KeyE', 'DOM_PK_E'),56(0x13, 'KeyR', 'DOM_PK_R'),57(0x14, 'KeyT', 'DOM_PK_T'),58(0x15, 'KeyY', 'DOM_PK_Y'),59(0x16, 'KeyU', 'DOM_PK_U'),60(0x17, 'KeyI', 'DOM_PK_I'),61(0x18, 'KeyO', 'DOM_PK_O'),62(0x19, 'KeyP', 'DOM_PK_P'),63(0x1A, 'BracketLeft', 'DOM_PK_BRACKET_LEFT'),64(0x1B, 'BracketRight', 'DOM_PK_BRACKET_RIGHT'),65(0x1C, 'Enter', 'DOM_PK_ENTER'),66(0x1D, 'ControlLeft', 'DOM_PK_CONTROL_LEFT'),67(0x1E, 'KeyA', 'DOM_PK_A'),68(0x1F, 'KeyS', 'DOM_PK_S'),69(0x20, 'KeyD', 'DOM_PK_D'),70(0x21, 'KeyF', 'DOM_PK_F'),71(0x22, 'KeyG', 'DOM_PK_G'),72(0x23, 'KeyH', 'DOM_PK_H'),73(0x24, 'KeyJ', 'DOM_PK_J'),74(0x25, 'KeyK', 'DOM_PK_K'),75(0x26, 'KeyL', 'DOM_PK_L'),76(0x27, 'Semicolon', 'DOM_PK_SEMICOLON'),77(0x28, 'Quote', 'DOM_PK_QUOTE'),78(0x29, 'Backquote', 'DOM_PK_BACKQUOTE'),79(0x2A, 'ShiftLeft', 'DOM_PK_SHIFT_LEFT'),80(0x2B, 'Backslash', 'DOM_PK_BACKSLASH'),81(0x2C, 'KeyZ', 'DOM_PK_Z'),82(0x2D, 'KeyX', 'DOM_PK_X'),83(0x2E, 'KeyC', 'DOM_PK_C'),84(0x2F, 'KeyV', 'DOM_PK_V'),85(0x30, 'KeyB', 'DOM_PK_B'),86(0x31, 'KeyN', 'DOM_PK_N'),87(0x32, 'KeyM', 'DOM_PK_M'),88(0x33, 'Comma', 'DOM_PK_COMMA'),89(0x34, 'Period', 'DOM_PK_PERIOD'),90(0x35, 'Slash', 'DOM_PK_SLASH'),91(0x36, 'ShiftRight', 'DOM_PK_SHIFT_RIGHT'),92(0x37, 'NumpadMultiply', 'DOM_PK_NUMPAD_MULTIPLY'),93(0x38, 'AltLeft', 'DOM_PK_ALT_LEFT'),94(0x39, 'Space', 'DOM_PK_SPACE'),95(0x3A, 'CapsLock', 'DOM_PK_CAPS_LOCK'),96(0x3B, 'F1', 'DOM_PK_F1'),97(0x3C, 'F2', 'DOM_PK_F2'),98(0x3D, 'F3', 'DOM_PK_F3'),99(0x3E, 'F4', 'DOM_PK_F4'),100(0x3F, 'F5', 'DOM_PK_F5'),101(0x40, 'F6', 'DOM_PK_F6'),102(0x41, 'F7', 'DOM_PK_F7'),103(0x42, 'F8', 'DOM_PK_F8'),104(0x43, 'F9', 'DOM_PK_F9'),105(0x44, 'F10', 'DOM_PK_F10'),106(0x45, 'Pause', 'DOM_PK_PAUSE'),107(0x46, 'ScrollLock', 'DOM_PK_SCROLL_LOCK'),108(0x47, 'Numpad7', 'DOM_PK_NUMPAD_7'),109(0x48, 'Numpad8', 'DOM_PK_NUMPAD_8'),110(0x49, 'Numpad9', 'DOM_PK_NUMPAD_9'),111(0x4A, 'NumpadSubtract', 'DOM_PK_NUMPAD_SUBTRACT'),112(0x4B, 'Numpad4', 'DOM_PK_NUMPAD_4'),113(0x4C, 'Numpad5', 'DOM_PK_NUMPAD_5'),114(0x4D, 'Numpad6', 'DOM_PK_NUMPAD_6'),115(0x4E, 'NumpadAdd', 'DOM_PK_NUMPAD_ADD'),116(0x4F, 'Numpad1', 'DOM_PK_NUMPAD_1'),117(0x50, 'Numpad2', 'DOM_PK_NUMPAD_2'),118(0x51, 'Numpad3', 'DOM_PK_NUMPAD_3'),119(0x52, 'Numpad0', 'DOM_PK_NUMPAD_0'),120(0x53, 'NumpadDecimal', 'DOM_PK_NUMPAD_DECIMAL'),121(0x54, 'PrintScreen', 'DOM_PK_PRINT_SCREEN'),122# 0x0055 'Unidentified', ''123(0x56, 'IntlBackslash', 'DOM_PK_INTL_BACKSLASH'),124(0x57, 'F11', 'DOM_PK_F11'),125(0x58, 'F12', 'DOM_PK_F12'),126(0x59, 'NumpadEqual', 'DOM_PK_NUMPAD_EQUAL'),127# 0x005A 'Unidentified', ''128# 0x005B 'Unidentified', ''129# 0x005C 'Unidentified', ''130# 0x005D 'Unidentified', ''131# 0x005E 'Unidentified', ''132# 0x005F 'Unidentified', ''133# 0x0060 'Unidentified', ''134# 0x0061 'Unidentified', ''135# 0x0062 'Unidentified', ''136# 0x0063 'Unidentified', ''137(0x64, 'F13', 'DOM_PK_F13'),138(0x65, 'F14', 'DOM_PK_F14'),139(0x66, 'F15', 'DOM_PK_F15'),140(0x67, 'F16', 'DOM_PK_F16'),141(0x68, 'F17', 'DOM_PK_F17'),142(0x69, 'F18', 'DOM_PK_F18'),143(0x6A, 'F19', 'DOM_PK_F19'),144(0x6B, 'F20', 'DOM_PK_F20'),145(0x6C, 'F21', 'DOM_PK_F21'),146(0x6D, 'F22', 'DOM_PK_F22'),147(0x6E, 'F23', 'DOM_PK_F23'),148# 0x006F 'Unidentified', ''149(0x70, 'KanaMode', 'DOM_PK_KANA_MODE'),150(0x71, 'Lang2', 'DOM_PK_LANG_2'),151(0x72, 'Lang1', 'DOM_PK_LANG_1'),152(0x73, 'IntlRo', 'DOM_PK_INTL_RO'),153# 0x0074 'Unidentified', ''154# 0x0075 'Unidentified', ''155(0x76, 'F24', 'DOM_PK_F24'),156# 0x0077 'Unidentified', ''157# 0x0078 'Unidentified', ''158(0x79, 'Convert', 'DOM_PK_CONVERT'),159# 0x007A 'Unidentified', ''160(0x7B, 'NonConvert', 'DOM_PK_NON_CONVERT'),161# 0x007C 'Unidentified', ''162(0x7D, 'IntlYen', 'DOM_PK_INTL_YEN'),163(0x7E, 'NumpadComma', 'DOM_PK_NUMPAD_COMMA'),164# 0x007F 'Unidentified', ''165(0xE00A, 'Paste', 'DOM_PK_PASTE'),166(0xE010, 'MediaTrackPrevious', 'DOM_PK_MEDIA_TRACK_PREVIOUS'),167(0xE017, 'Cut', 'DOM_PK_CUT'),168(0xE018, 'Copy', 'DOM_PK_COPY'),169(0xE019, 'MediaTrackNext', 'DOM_PK_MEDIA_TRACK_NEXT'),170(0xE01C, 'NumpadEnter', 'DOM_PK_NUMPAD_ENTER'),171(0xE01D, 'ControlRight', 'DOM_PK_CONTROL_RIGHT'),172(0xE020, 'AudioVolumeMute', 'DOM_PK_AUDIO_VOLUME_MUTE'),173(0xE020, 'VolumeMute', 'DOM_PK_AUDIO_VOLUME_MUTE', 'duplicate'),174(0xE021, 'LaunchApp2', 'DOM_PK_LAUNCH_APP_2'),175(0xE022, 'MediaPlayPause', 'DOM_PK_MEDIA_PLAY_PAUSE'),176(0xE024, 'MediaStop', 'DOM_PK_MEDIA_STOP'),177(0xE02C, 'Eject', 'DOM_PK_EJECT'),178(0xE02E, 'AudioVolumeDown', 'DOM_PK_AUDIO_VOLUME_DOWN'),179(0xE02E, 'VolumeDown', 'DOM_PK_AUDIO_VOLUME_DOWN', 'duplicate'),180(0xE030, 'AudioVolumeUp', 'DOM_PK_AUDIO_VOLUME_UP'),181(0xE030, 'VolumeUp', 'DOM_PK_AUDIO_VOLUME_UP', 'duplicate'),182(0xE032, 'BrowserHome', 'DOM_PK_BROWSER_HOME'),183(0xE035, 'NumpadDivide', 'DOM_PK_NUMPAD_DIVIDE'),184# (0xE037, 'PrintScreen', 'DOM_PK_PRINT_SCREEN'),185(0xE038, 'AltRight', 'DOM_PK_ALT_RIGHT'),186(0xE03B, 'Help', 'DOM_PK_HELP'),187(0xE045, 'NumLock', 'DOM_PK_NUM_LOCK'),188# (0xE046, 'Pause', 'DOM_PK_'), # Says Ctrl+Pause189(0xE047, 'Home', 'DOM_PK_HOME'),190(0xE048, 'ArrowUp', 'DOM_PK_ARROW_UP'),191(0xE049, 'PageUp', 'DOM_PK_PAGE_UP'),192(0xE04B, 'ArrowLeft', 'DOM_PK_ARROW_LEFT'),193(0xE04D, 'ArrowRight', 'DOM_PK_ARROW_RIGHT'),194(0xE04F, 'End', 'DOM_PK_END'),195(0xE050, 'ArrowDown', 'DOM_PK_ARROW_DOWN'),196(0xE051, 'PageDown', 'DOM_PK_PAGE_DOWN'),197(0xE052, 'Insert', 'DOM_PK_INSERT'),198(0xE053, 'Delete', 'DOM_PK_DELETE'),199(0xE05B, 'MetaLeft', 'DOM_PK_META_LEFT'),200(0xE05B, 'OSLeft', 'DOM_PK_OS_LEFT', 'duplicate'),201(0xE05C, 'MetaRight', 'DOM_PK_META_RIGHT'),202(0xE05C, 'OSRight', 'DOM_PK_OS_RIGHT', 'duplicate'),203(0xE05D, 'ContextMenu', 'DOM_PK_CONTEXT_MENU'),204(0xE05E, 'Power', 'DOM_PK_POWER'),205(0xE065, 'BrowserSearch', 'DOM_PK_BROWSER_SEARCH'),206(0xE066, 'BrowserFavorites', 'DOM_PK_BROWSER_FAVORITES'),207(0xE067, 'BrowserRefresh', 'DOM_PK_BROWSER_REFRESH'),208(0xE068, 'BrowserStop', 'DOM_PK_BROWSER_STOP'),209(0xE069, 'BrowserForward', 'DOM_PK_BROWSER_FORWARD'),210(0xE06A, 'BrowserBack', 'DOM_PK_BROWSER_BACK'),211(0xE06B, 'LaunchApp1', 'DOM_PK_LAUNCH_APP_1'),212(0xE06C, 'LaunchMail', 'DOM_PK_LAUNCH_MAIL'),213(0xE06D, 'LaunchMediaPlayer', 'DOM_PK_LAUNCH_MEDIA_PLAYER'),214(0xE06D, 'MediaSelect', 'DOM_PK_MEDIA_SELECT', 'duplicate'),215# (0xE0F1, 'Lang2', 'DOM_PK_'), Hanja key216# (0xE0F2, 'Lang2', 'DOM_PK_'), Han/Yeong217]218219220def hash(s, k1, k2):221h = 0222for c in s:223h = int(int(int(h ^ k1) << k2) ^ ord(c)) & 0xFFFFFFFF224return h225226227def hash_all(k1, k2):228hashes = {}229str_to_hash = {}230for s in input_strings:231h = hash(s[1], k1, k2)232print('String "' + s[1] + '" hashes to %s ' % hex(h), file=sys.stderr)233if h in hashes:234print('Collision! Earlier string ' + hashes[h] + ' also hashed to %s!' % hex(h), file=sys.stderr)235return None236else:237hashes[h] = s[1]238str_to_hash[s[1]] = h239return (hashes, str_to_hash)240241242# Find an appropriate hash function that is collision free within the set of all input strings243# Try hash function format h_i = ((h_(i-1) ^ k_1) << k_2) ^ s_i, where h_i is the hash function244# value at step i, k_1 and k_2 are the constants we are searching, and s_i is the i'th input245# character246perfect_hash_table = None247248# Last used perfect hash constants. Stored here so that this script will249# produce the same output it did when the current output was generated.250k1 = 0x7E057D79251k2 = 3252perfect_hash_table = hash_all(k1, k2)253254while not perfect_hash_table:255# The search space is super-narrow, but since there are so few items to hash, practically256# almost any choice gives a collision free hash.257k1 = int(random.randint(0, 0x7FFFFFFF))258k2 = int(random.uniform(1, 8))259perfect_hash_table = hash_all(k1, k2)260261hash_to_str, str_to_hash = perfect_hash_table262263print('Found collision-free hash function!', file=sys.stderr)264print('h_i = ((h_(i-1) ^ %s) << %s) ^ s_i' % (hex(k1), hex(k2)), file=sys.stderr)265266267def pad_to_length(s, length):268return s + max(0, length - len(s)) * ' '269270271def longest_dom_pk_code_length():272return max(map(len, [x[2] for x in input_strings]))273274275def longest_key_code_length():276return max(map(len, [x[1] for x in input_strings]))277278279script_dir = os.path.dirname(os.path.abspath(__file__))280root = os.path.dirname(os.path.dirname(script_dir))281282h_filename = os.path.join(root, 'system/include/emscripten/dom_pk_codes.h')283c_filename = os.path.join(root, 'system/lib/html5/dom_pk_codes.c')284print(f'Writing: {h_filename}')285print(f'Writing: {c_filename}')286h_file = open(h_filename, 'w')287c_file = open(c_filename, 'w')288289290# Generate the output file:291292h_file.write('''\293/*294* Copyright 2018 The Emscripten Authors. All rights reserved.295* Emscripten is available under two separate licenses, the MIT license and the296* University of Illinois/NCSA Open Source License. Both these licenses can be297* found in the LICENSE file.298*299* This file was automatically generated from script300* tools/maint/create_dom_pk_codes.py. Edit that file to make changes here.301* Then run:302*303* tools/maint/create_dom_pk_codes.py304*305* in Emscripten root directory to regenerate this file.306*/307308#pragma once309310#define DOM_PK_CODE_TYPE int311312''')313314c_file.write('''\315/*316* This file was automatically generated from script317* tools/maint/create_dom_pk_codes.py. Edit that file to make changes here.318* Then run:319*320* tools/maint/create_dom_pk_codes.py321*322* in Emscripten root directory to regenerate this file.323*/324325#include <emscripten/dom_pk_codes.h>326''')327328for s in input_strings:329h_file.write('#define ' + pad_to_length(s[2], longest_dom_pk_code_length()) + ' 0x%04X /* "%s */' % (s[0], pad_to_length(s[1] + '"', longest_key_code_length() + 1)) + '\n')330331h_file.write('''332#ifdef __cplusplus333extern "C" {334#endif335/* Maps the EmscriptenKeyboardEvent::code field from emscripten/html5.h to one of the DOM_PK codes above. */336DOM_PK_CODE_TYPE emscripten_compute_dom_pk_code(const char *keyCodeString);337338/* Returns the string representation of the given key code ID. Useful for debug printing. */339const char *emscripten_dom_pk_code_to_string(DOM_PK_CODE_TYPE code);340#ifdef __cplusplus341}342#endif343''')344345c_file.write('''346DOM_PK_CODE_TYPE emscripten_compute_dom_pk_code(const char *keyCodeString) {347if (!keyCodeString) return 0;348349/* Compute the collision free hash. */350unsigned int hash = 0;351while (*keyCodeString) hash = ((hash ^ 0x%04XU) << %d) ^ (unsigned int)*keyCodeString++;352353/*354* Don't expose the hash values out to the application, but map to fixed IDs.355* This is useful for mapping back codes to MDN documentation page at356*357* https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code358*/359switch (hash) {360''' % (k1, k2))361362for s in input_strings:363c_file.write(' case 0x%08XU /* %s */: return %s /* 0x%04X */' % (str_to_hash[s[1]], pad_to_length(s[1], longest_key_code_length()), pad_to_length(s[2] + ';', longest_dom_pk_code_length() + 1), s[0]) + '\n')364365c_file.write(''' default: return DOM_PK_UNKNOWN;366}367}368369const char *emscripten_dom_pk_code_to_string(DOM_PK_CODE_TYPE code) {370switch (code) {371''')372373for s in input_strings:374if len(s) == 3:375c_file.write(' case %s return "%s";' % (pad_to_length(s[2] + ':', longest_dom_pk_code_length() + 1), s[2]) + '\n')376377c_file.write(''' default: return "Unknown DOM_PK code";378}379}380''')381382383