blob: ac80e1f1249dd39beb883a721af34df39353d1d0 [file] [log] [blame]
#!/usr/bin/env python
# coding=utf-8
# Copyright 2017 The Emscripten Authors. All rights reserved.
# Emscripten is available under two separate licenses, the MIT license and the
# University of Illinois/NCSA Open Source License. Both these licenses can be
# found in the LICENSE file.
# The DOM KeyboardEvent field 'code' contains a locale/language independent
# identifier of a pressed key on the keyboard, i.e. a "physical" keyboard code, which
# is often useful for games that want to provide a physical keyboard layout that does
# not get confused by the language setting the user has.
# For example, in Unreal Engine 4 developer mode, the physical keyboard key above the Tab
# but below the Esc key should open up the developer console, independent of which keyboard
# layout is active. This key produces the backquote (`) character on US keyboard layout,
# whereas on the Finnish/Swedish keyboard layout, it generates but the section sign (ยง)
# character. Other keyboard layouts might give different characters, and independent of
# which character is produced, we would like to generate a layout-agnostic identifier for
# the key at this physical location on the keyboard.
# The DOM KeyboardEvent field 'code' provides such a layout-agnostic identifier.
# Unfortunately this identifier is not an integral ID that could be used as an enum
# or #define, but it is a human-readable English language string that represents the
# physical key. This is very inconvenient for most applications.
# This utility script creates a mapping from the different documented values of the
# KeyboardEvent 'code' field, to integral IDs that can be easily used to identify
# physical key locations. This mapping is implemented by constructing a hash function
# that is a perfect hash (https://en.wikipedia.org/wiki/Perfect_hash_function) of the
# known strings.
# Use #include <emscripten/dom_pk_codes.h> in your code to access these IDs.
from __future__ import print_function
import sys, random
input_strings = [
(0x0, 'Unidentified', 'DOM_PK_UNKNOWN'),
(0x1, 'Escape', 'DOM_PK_ESCAPE'),
(0x2, 'Digit0', 'DOM_PK_0'),
(0x3, 'Digit1', 'DOM_PK_1'),
(0x4, 'Digit2', 'DOM_PK_2'),
(0x5, 'Digit3', 'DOM_PK_3'),
(0x6, 'Digit4', 'DOM_PK_4'),
(0x7, 'Digit5', 'DOM_PK_5'),
(0x8, 'Digit6', 'DOM_PK_6'),
(0x9, 'Digit7', 'DOM_PK_7'),
(0xA, 'Digit8', 'DOM_PK_8'),
(0xB, 'Digit9', 'DOM_PK_9'),
(0xC, 'Minus', 'DOM_PK_MINUS'),
(0xD, 'Equal', 'DOM_PK_EQUAL'),
(0xE, 'Backspace', 'DOM_PK_BACKSPACE'),
(0xF, 'Tab', 'DOM_PK_TAB'),
(0x10, 'KeyQ', 'DOM_PK_Q'),
(0x11, 'KeyW', 'DOM_PK_W'),
(0x12, 'KeyE', 'DOM_PK_E'),
(0x13, 'KeyR', 'DOM_PK_R'),
(0x14, 'KeyT', 'DOM_PK_T'),
(0x15, 'KeyY', 'DOM_PK_Y'),
(0x16, 'KeyU', 'DOM_PK_U'),
(0x17, 'KeyI', 'DOM_PK_I'),
(0x18, 'KeyO', 'DOM_PK_O'),
(0x19, 'KeyP', 'DOM_PK_P'),
(0x1A, 'BracketLeft', 'DOM_PK_BRACKET_LEFT'),
(0x1B, 'BracketRight', 'DOM_PK_BRACKET_RIGHT'),
(0x1C, 'Enter', 'DOM_PK_ENTER'),
(0x1D, 'ControlLeft', 'DOM_PK_CONTROL_LEFT'),
(0x1E, 'KeyA', 'DOM_PK_A'),
(0x1F, 'KeyS', 'DOM_PK_S'),
(0x20, 'KeyD', 'DOM_PK_D'),
(0x21, 'KeyF', 'DOM_PK_F'),
(0x22, 'KeyG', 'DOM_PK_G'),
(0x23, 'KeyH', 'DOM_PK_H'),
(0x24, 'KeyJ', 'DOM_PK_J'),
(0x25, 'KeyK', 'DOM_PK_K'),
(0x26, 'KeyL', 'DOM_PK_L'),
(0x27, 'Semicolon', 'DOM_PK_SEMICOLON'),
(0x28, 'Quote', 'DOM_PK_QUOTE'),
(0x29, 'Backquote', 'DOM_PK_BACKQUOTE'),
(0x2A, 'ShiftLeft', 'DOM_PK_SHIFT_LEFT'),
(0x2B, 'Backslash', 'DOM_PK_BACKSLASH'),
(0x2C, 'KeyZ', 'DOM_PK_Z'),
(0x2D, 'KeyX', 'DOM_PK_X'),
(0x2E, 'KeyC', 'DOM_PK_C'),
(0x2F, 'KeyV', 'DOM_PK_V'),
(0x30, 'KeyB', 'DOM_PK_B'),
(0x31, 'KeyN', 'DOM_PK_N'),
(0x32, 'KeyM', 'DOM_PK_M'),
(0x33, 'Comma', 'DOM_PK_COMMA'),
(0x34, 'Period', 'DOM_PK_PERIOD'),
(0x35, 'Slash', 'DOM_PK_SLASH'),
(0x36, 'ShiftRight', 'DOM_PK_SHIFT_RIGHT'),
(0x37, 'NumpadMultiply', 'DOM_PK_NUMPAD_MULTIPLY'),
(0x38, 'AltLeft', 'DOM_PK_ALT_LEFT'),
(0x39, 'Space', 'DOM_PK_SPACE'),
(0x3A, 'CapsLock', 'DOM_PK_CAPS_LOCK'),
(0x3B, 'F1', 'DOM_PK_F1'),
(0x3C, 'F2', 'DOM_PK_F2'),
(0x3D, 'F3', 'DOM_PK_F3'),
(0x3E, 'F4', 'DOM_PK_F4'),
(0x3F, 'F5', 'DOM_PK_F5'),
(0x40, 'F6', 'DOM_PK_F6'),
(0x41, 'F7', 'DOM_PK_F7'),
(0x42, 'F8', 'DOM_PK_F8'),
(0x43, 'F9', 'DOM_PK_F9'),
(0x44, 'F10', 'DOM_PK_F10'),
(0x45, 'Pause', 'DOM_PK_PAUSE'),
(0x46, 'ScrollLock', 'DOM_PK_SCROLL_LOCK'),
(0x47, 'Numpad7', 'DOM_PK_NUMPAD_7'),
(0x48, 'Numpad8', 'DOM_PK_NUMPAD_8'),
(0x49, 'Numpad9', 'DOM_PK_NUMPAD_9'),
(0x4A, 'NumpadSubtract', 'DOM_PK_NUMPAD_SUBTRACT'),
(0x4B, 'Numpad4', 'DOM_PK_NUMPAD_4'),
(0x4C, 'Numpad5', 'DOM_PK_NUMPAD_5'),
(0x4D, 'Numpad6', 'DOM_PK_NUMPAD_6'),
(0x4E, 'NumpadAdd', 'DOM_PK_NUMPAD_ADD'),
(0x4F, 'Numpad1', 'DOM_PK_NUMPAD_1'),
(0x50, 'Numpad2', 'DOM_PK_NUMPAD_2'),
(0x51, 'Numpad3', 'DOM_PK_NUMPAD_3'),
(0x52, 'Numpad0', 'DOM_PK_NUMPAD_0'),
(0x53, 'NumpadDecimal', 'DOM_PK_NUMPAD_DECIMAL'),
(0x54, 'PrintScreen', 'DOM_PK_PRINT_SCREEN'),
# 0x0055 'Unidentified', ''
(0x56, 'IntlBackslash', 'DOM_PK_INTL_BACKSLASH'),
(0x57, 'F11', 'DOM_PK_F11'),
(0x58, 'F12', 'DOM_PK_F12'),
(0x59, 'NumpadEqual', 'DOM_PK_NUMPAD_EQUAL'),
# 0x005A 'Unidentified', ''
# 0x005B 'Unidentified', ''
# 0x005C 'Unidentified', ''
# 0x005D 'Unidentified', ''
# 0x005E 'Unidentified', ''
# 0x005F 'Unidentified', ''
# 0x0060 'Unidentified', ''
# 0x0061 'Unidentified', ''
# 0x0062 'Unidentified', ''
# 0x0063 'Unidentified', ''
(0x64, 'F13', 'DOM_PK_F13'),
(0x65, 'F14', 'DOM_PK_F14'),
(0x66, 'F15', 'DOM_PK_F15'),
(0x67, 'F16', 'DOM_PK_F16'),
(0x68, 'F17', 'DOM_PK_F17'),
(0x69, 'F18', 'DOM_PK_F18'),
(0x6A, 'F19', 'DOM_PK_F19'),
(0x6B, 'F20', 'DOM_PK_F20'),
(0x6C, 'F21', 'DOM_PK_F21'),
(0x6D, 'F22', 'DOM_PK_F22'),
(0x6E, 'F23', 'DOM_PK_F23'),
# 0x006F 'Unidentified', ''
(0x70, 'KanaMode', 'DOM_PK_KANA_MODE'),
(0x71, 'Lang2', 'DOM_PK_LANG_2'),
(0x72, 'Lang1', 'DOM_PK_LANG_1'),
(0x73, 'IntlRo', 'DOM_PK_INTL_RO'),
# 0x0074 'Unidentified', ''
# 0x0075 'Unidentified', ''
(0x76, 'F24', 'DOM_PK_F24'),
# 0x0077 'Unidentified', ''
# 0x0078 'Unidentified', ''
(0x79, 'Convert', 'DOM_PK_CONVERT'),
# 0x007A 'Unidentified', ''
(0x7B, 'NonConvert', 'DOM_PK_NON_CONVERT'),
# 0x007C 'Unidentified', ''
(0x7D, 'IntlYen', 'DOM_PK_INTL_YEN'),
(0x7E, 'NumpadComma', 'DOM_PK_NUMPAD_COMMA'),
# 0x007F 'Unidentified', ''
(0xE00A, 'Paste', 'DOM_PK_PASTE'),
(0xE010, 'MediaTrackPrevious', 'DOM_PK_MEDIA_TRACK_PREVIOUS'),
(0xE017, 'Cut', 'DOM_PK_CUT'),
(0xE018, 'Copy', 'DOM_PK_COPY'),
(0xE019, 'MediaTrackNext', 'DOM_PK_MEDIA_TRACK_NEXT'),
(0xE01C, 'NumpadEnter', 'DOM_PK_NUMPAD_ENTER'),
(0xE01D, 'ControlRight', 'DOM_PK_CONTROL_RIGHT'),
(0xE020, 'AudioVolumeMute', 'DOM_PK_AUDIO_VOLUME_MUTE'),
(0xE020, 'VolumeMute', 'DOM_PK_AUDIO_VOLUME_MUTE', 'duplicate'),
(0xE021, 'LaunchApp2', 'DOM_PK_LAUNCH_APP_2'),
(0xE022, 'MediaPlayPause', 'DOM_PK_MEDIA_PLAY_PAUSE'),
(0xE024, 'MediaStop', 'DOM_PK_MEDIA_STOP'),
(0xE02C, 'Eject', 'DOM_PK_EJECT'),
(0xE02E, 'AudioVolumeDown', 'DOM_PK_AUDIO_VOLUME_DOWN'),
(0xE02E, 'VolumeDown', 'DOM_PK_AUDIO_VOLUME_DOWN', 'duplicate'),
(0xE030, 'AudioVolumeUp', 'DOM_PK_AUDIO_VOLUME_UP'),
(0xE030, 'VolumeUp', 'DOM_PK_AUDIO_VOLUME_UP', 'duplicate'),
(0xE032, 'BrowserHome', 'DOM_PK_BROWSER_HOME'),
(0xE035, 'NumpadDivide', 'DOM_PK_NUMPAD_DIVIDE'),
# (0xE037, 'PrintScreen', 'DOM_PK_PRINT_SCREEN'),
(0xE038, 'AltRight', 'DOM_PK_ALT_RIGHT'),
(0xE03B, 'Help', 'DOM_PK_HELP'),
(0xE045, 'NumLock', 'DOM_PK_NUM_LOCK'),
# (0xE046, 'Pause', 'DOM_PK_'), # Says Ctrl+Pause
(0xE047, 'Home', 'DOM_PK_HOME'),
(0xE048, 'ArrowUp', 'DOM_PK_ARROW_UP'),
(0xE049, 'PageUp', 'DOM_PK_PAGE_UP'),
(0xE04B, 'ArrowLeft', 'DOM_PK_ARROW_LEFT'),
(0xE04D, 'ArrowRight', 'DOM_PK_ARROW_RIGHT'),
(0xE04F, 'End', 'DOM_PK_END'),
(0xE050, 'ArrowDown', 'DOM_PK_ARROW_DOWN'),
(0xE051, 'PageDown', 'DOM_PK_PAGE_DOWN'),
(0xE052, 'Insert', 'DOM_PK_INSERT'),
(0xE053, 'Delete', 'DOM_PK_DELETE'),
(0xE05B, 'MetaLeft', 'DOM_PK_META_LEFT'),
(0xE05B, 'OSLeft', 'DOM_PK_OS_LEFT', 'duplicate'),
(0xE05C, 'MetaRight', 'DOM_PK_META_RIGHT'),
(0xE05C, 'OSRight', 'DOM_PK_OS_RIGHT', 'duplicate'),
(0xE05D, 'ContextMenu', 'DOM_PK_CONTEXT_MENU'),
(0xE05E, 'Power', 'DOM_PK_POWER'),
(0xE065, 'BrowserSearch', 'DOM_PK_BROWSER_SEARCH'),
(0xE066, 'BrowserFavorites', 'DOM_PK_BROWSER_FAVORITES'),
(0xE067, 'BrowserRefresh', 'DOM_PK_BROWSER_REFRESH'),
(0xE068, 'BrowserStop', 'DOM_PK_BROWSER_STOP'),
(0xE069, 'BrowserForward', 'DOM_PK_BROWSER_FORWARD'),
(0xE06A, 'BrowserBack', 'DOM_PK_BROWSER_BACK'),
(0xE06B, 'LaunchApp1', 'DOM_PK_LAUNCH_APP_1'),
(0xE06C, 'LaunchMail', 'DOM_PK_LAUNCH_MAIL'),
(0xE06D, 'LaunchMediaPlayer', 'DOM_PK_LAUNCH_MEDIA_PLAYER'),
(0xE06D, 'MediaSelect', 'DOM_PK_MEDIA_SELECT', 'duplicate')
# (0xE0F1, 'Lang2', 'DOM_PK_'), Hanja key
# (0xE0F2, 'Lang2', 'DOM_PK_'), Han/Yeong
]
def hash(s, k1, k2):
h = 0
for c in s:
h = int(int(int(h ^ k1) << k2) ^ ord(c)) & 0xFFFFFFFF
return h
def hash_all(k1, k2):
hashes = {}
str_to_hash = {}
for s in input_strings:
h = hash(s[1], k1, k2)
print('String "' + s[1] + '" hashes to %s ' % hex(h), file=sys.stderr)
if h in hashes:
print('Collision! Earlier string ' + hashes[h] + ' also hashed to %s!' % hex(h), file=sys.stderr)
return None
else:
hashes[h] = s[1]
str_to_hash[s[1]] = h
return (hashes, str_to_hash)
# Find an approprite hash function that is collision free within the set of all input strings
# Try hash function format h_i = ((h_(i-1) ^ k_1) << k_2) ^ s_i, where h_i is the hash function
# value at step i, k_1 and k_2 are the constants we are searching, and s_i is the i'th input
# character
perfect_hash_table = None
while perfect_hash_table == None:
# The search space is super-narrow, but since there are so few items to hash, practically
# almost any choice gives a collision free hash.
k1 = int(random.randint(0, 0x7FFFFFFF))
k2 = int(random.uniform(1, 8))
perfect_hash_table = hash_all(k1, k2)
hash_to_str, str_to_hash = perfect_hash_table
print('Found collision-free hash function!', file=sys.stderr)
print('h_i = ((h_(i-1) ^ %s) << %s) ^ s_i' % (hex(k1), hex(k2)), file=sys.stderr)
def pad_to_length(s, length):
return s + max(0, length - len(s)) * ' '
def longest_dom_pk_code_length():
return max(map(len, [x[2] for x in input_strings]))
def longest_key_code_length():
return max(map(len, [x[1] for x in input_strings]))
h_file = open('system/include/emscripten/dom_pk_codes.h', 'w')
c_file = open('system/lib/html5/dom_pk_codes.c', 'w')
# Generate the output file:
h_file.write('''\
/*
* Copyright 2018 The Emscripten Authors. All rights reserved.
* Emscripten is available under two separate licenses, the MIT license and the
* University of Illinois/NCSA Open Source License. Both these licenses can be
* found in the LICENSE file.
*
* This file was automatically generated from script
* tools/create_dom_pk_codes.py. Edit that file to make changes here.
* Run
*
* tools/create_dom_pk_codes.py
*
* in Emscripten root directory to regenerate this file.
*/
#pragma once
#define DOM_PK_CODE_TYPE int
''')
c_file.write('''/* This file was automatically generated from script
tools/create_dom_pk_codes.py. Edit that file to make changes here.
Run
python tools/create_dom_pk_codes.py
in Emscripten root directory to regenerate this file. */
#include <emscripten/dom_pk_codes.h>
''')
for s in input_strings:
h_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')
h_file.write('''
#ifdef __cplusplus
extern "C" {
#endif
/* Maps the EmscriptenKeyboardEvent::code field from emscripten/html5.h to one of the DOM_PK codes above. */
DOM_PK_CODE_TYPE emscripten_compute_dom_pk_code(const char *keyCodeString);
/* Returns the string representation of the given key code ID. Useful for debug printing. */
const char *emscripten_dom_pk_code_to_string(DOM_PK_CODE_TYPE code);
#ifdef __cplusplus
}
#endif
''')
c_file.write('''
DOM_PK_CODE_TYPE emscripten_compute_dom_pk_code(const char *keyCodeString)
{
if (!keyCodeString) return 0;
/* Compute the collision free hash. */
unsigned int hash = 0;
while(*keyCodeString) hash = ((hash ^ 0x%04XU) << %d) ^ (unsigned int)*keyCodeString++;
/* Don't expose the hash values out to the application, but map to fixed IDs. This is useful for
mapping back codes to MDN documentation page at
https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code */
switch(hash)
{
''' % (k1, k2))
for s in input_strings:
c_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')
c_file.write(''' default: return DOM_PK_UNKNOWN;
}
}
const char *emscripten_dom_pk_code_to_string(DOM_PK_CODE_TYPE code)
{
switch(code)
{
''')
for s in input_strings:
if len(s) == 3:
c_file.write(' case %s return "%s";' % (pad_to_length(s[2] + ':', longest_dom_pk_code_length()+1), s[2]) + '\n')
c_file.write(''' default: return "Unknown DOM_PK code";
}
}
''')