Last active
September 18, 2023 10:50
-
-
Save willwade/b82bc984e5ef3d883812f7b2b7ba866c to your computer and use it in GitHub Desktop.
this takes the directory of xkb keyboard maps found in linux at /usr/share/X11/xkb/symbols - and converts them to a relaykeys format e.g. https://github.com/AceCentre/RelayKeys/blob/master/cli_keymaps/de_keymap.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import os | |
import re | |
import json | |
import unicodedata | |
def xkb_to_unicode(key_code): | |
# Add your custom mapping here | |
custom_mapping = { | |
"adiaeresis": "ä", | |
"odiaeresis": "ö", | |
"udiaeresis": "ü", | |
"space": " ", | |
"Tab": "\t", | |
"exclamation_mark": "!", | |
"double_quotes": "\"", | |
"hash": "#", | |
"dollar": "$", | |
"percent": "%", | |
"ampersand": "&", | |
"single_quote": "'", | |
"left_parenthesis": "(", | |
"right_parenthesis": ")", | |
"asterisk": "*", | |
"plus": "+", | |
"comma": ",", | |
"minus": "-", | |
"period": ".", | |
"slash": "/", | |
"colon": ":", | |
"semicolon": ";", | |
"less_than": "<", | |
"equals": "=", | |
"greater_than": ">", | |
"question_mark": "?", | |
"at": "@", | |
"left_square_bracket": "[", | |
"backslash": "\\", | |
"right_square_bracket": "]", | |
"caret": "^", | |
"underscore": "_", | |
"grave_accent": "`", | |
"left_curly_brace": "{", | |
"vertical_bar": "|", | |
"right_curly_brace": "}", | |
"tilde": "~", | |
'Abreve': 'ă', | |
'Acircumflex': 'â', | |
'Adiaeresis': 'ä', | |
'AE': 'æ', | |
'B': 'B', | |
'C': 'C', | |
'Cacute': 'ć', | |
'Ccaron': 'č', | |
'D': 'D', | |
'Down': '↓', | |
'E': 'E', | |
'ENG': 'ŋ', | |
'ETH': 'ð', | |
'Ecaron': 'ě', | |
'Ediaeresis': 'ë', | |
'End': '⌦', | |
'EuroSign': '€', | |
'Home': '⌂', | |
'Iacute': 'í', | |
'Icircumflex': 'î', | |
'Insert': '⎀', | |
'Left': '←', | |
'Lstroke': 'ł', | |
'Nacute': 'ń', | |
'Next': '⎗', | |
'NoSymbol': '⎋', | |
'OE': 'œ', | |
'Oacute': 'ó', | |
'Odiaeresis': 'ö', | |
'Odoubleacute': 'ő', | |
'Oslash': 'ø', | |
'Prior': '⎖', | |
'Racute': 'ŕ', | |
'Redo': '⎘', | |
'Return': '⎆', | |
'Right': '→', | |
'SCHWA': 'ə', | |
'Sacute': 'ś', | |
'Scaron': 'š', | |
'Scedilla': 'ş', | |
'THORN': 'þ', | |
'Tcedilla': 'ţ', | |
} | |
# Check if the string is a Unicode character | |
unicode_match = re.match(r'U([0-9A-Fa-f]+)', key_code) | |
if unicode_match: | |
unicode_code = int(unicode_match.group(1), 16) | |
return chr(unicode_code) | |
return custom_mapping.get(key_code, key_code) | |
def generate_modifier_mapping(num_symbols): | |
base_modifiers = [ | |
[], # Level 1: No modifiers | |
["LSHIFT"], # Level 2: Left Shift | |
["LALT"], # Level 3: Left Alt (or AltGr) | |
["LCTRL"], # Level 4: Left Control | |
["RSHIFT"], # Level 5: Right Shift | |
["RALT"], # Level 6: Right Alt (or AltGr) | |
["RCTRL"], # Level 7: Right Control | |
["LSHIFT", "LALT"], # Level 8: Left Shift + Left Alt | |
["LSHIFT", "LCTRL"], # Level 9: Left Shift + Left Control | |
["LALT", "LCTRL"], # Level 10: Left Alt + Left Control | |
["RSHIFT", "RALT"], # Level 11: Right Shift + Right Alt | |
["RSHIFT", "RCTRL"], # Level 12: Right Shift + Right Control | |
["RALT", "RCTRL"], # Level 13: Right Alt + Right Control | |
["LSHIFT", "RSHIFT"], # Level 14: Left Shift + Right Shift | |
["LALT", "RALT"], # Level 15: Left Alt + Right Alt | |
["LCTRL", "RCTRL"], # Level 16: Left Control + Right Control | |
] | |
# Generate a mapping based on the number of symbols | |
modifier_mapping = {} | |
for i in range(num_symbols): | |
modifier_mapping[i] = base_modifiers[i % len(base_modifiers)] | |
return modifier_mapping | |
def parse_xkb_to_json(xkb_file_path): | |
json_data = {} | |
# Read the XKB file | |
with open(xkb_file_path, 'r') as f: | |
xkb_data = f.read() | |
# Dynamically generate the modifier_mapping based on the number of symbols for the first key | |
first_key_match = re.search(r"key <(.*?)> { \[ (.*?) \] };", xkb_data) | |
if first_key_match: | |
num_symbols = len(first_key_match.group(2).split(", ")) | |
modifier_mapping = generate_modifier_mapping(num_symbols) | |
# Parse each line in the XKB layout data | |
for line in xkb_data.strip().split("\n"): | |
match = re.search(r"key <(.*?)> { \[ (.*?) \] };", line) | |
if match: | |
key_code_list = match.group(2).split(", ") | |
# Remove extra spaces from key codes | |
key_code_list = [key_code.strip() for key_code in key_code_list] | |
# Inside parse_xkb_to_json function | |
for index, key_code in enumerate(key_code_list): | |
modifiers = modifier_mapping.get(index, []) | |
# Use the first key code as the base for the JSON entry | |
base_key_code = key_code_list[0].strip() | |
# Convert XKB symbol names to Unicode characters | |
key_code = xkb_to_unicode(key_code) | |
base_key_code = xkb_to_unicode(base_key_code) | |
json_data[key_code] = [base_key_code, modifiers] | |
return json_data | |
def format_json(json_data): | |
formatted_json = "{\n" | |
# Sort keys so that numbers and letters appear first | |
sorted_keys = sorted(json_data.keys(), key=lambda x: (not x.isalnum(), x)) | |
for key in sorted_keys: | |
value = json_data[key] | |
# Special handling for the double quote character | |
if key == '"': | |
key = '\\"' | |
formatted_line = f'\t"{key}": {json.dumps(value)},\n' | |
formatted_json += formatted_line | |
formatted_json = formatted_json.rstrip(",\n") + "\n}" | |
return formatted_json | |
# Loop through all files in the 'symbols' directory | |
for xkb_file in os.listdir('symbols'): | |
xkb_file_path = os.path.join('symbols', xkb_file) | |
# Skip directories | |
if os.path.isdir(xkb_file_path): | |
continue | |
# Convert XKB layout to JSON format | |
json_data = parse_xkb_to_json(xkb_file_path) | |
# Save JSON data to a file | |
output_file_path = os.path.join('output_json', f"{xkb_file}.json") | |
# Format the JSON string with line breaks and tabs | |
formatted_json = format_json(json_data) | |
with open(output_file_path, 'w') as json_file: | |
json_file.write(formatted_json) | |
print(f"JSON data for {xkb_file} has been saved to {output_file_path}") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment