390 lines
9.3 KiB
GDScript
390 lines
9.3 KiB
GDScript
class_name ParserQMK extends AbstractParser
|
|
|
|
const KEYCODE_MAP: Dictionary[String, Key] = {
|
|
"ENT": KEY_ENTER,
|
|
"ESC": KEY_ESCAPE,
|
|
"BSPC": KEY_BACKSPACE,
|
|
"SPC": KEY_SPACE,
|
|
"MINS": KEY_MINUS,
|
|
"EQL": KEY_EQUAL,
|
|
"LEFT_BRACKET": KEY_BRACKETLEFT,
|
|
"LBRC": KEY_BRACKETLEFT,
|
|
"RIGHT_BRACKET": KEY_BRACKETRIGHT,
|
|
"RBRC": KEY_BRACKETRIGHT,
|
|
"BSLS": KEY_BACKSLASH,
|
|
"NONUS_HASH": KEY_BACKSLASH,
|
|
"NUHS": KEY_BACKSLASH,
|
|
"SCLN": KEY_SEMICOLON,
|
|
"QUOTE": KEY_APOSTROPHE,
|
|
"QUOT": KEY_APOSTROPHE,
|
|
"GRAVE": KEY_QUOTELEFT,
|
|
"GRV": KEY_QUOTELEFT,
|
|
"COMM": KEY_COMMA,
|
|
"DOT": KEY_PERIOD,
|
|
"SLSH": KEY_SLASH,
|
|
"NONUS_BACKSLASH": KEY_SECTION,
|
|
"NUBS": KEY_SECTION,
|
|
"CAPS_LOCK": KEY_CAPSLOCK,
|
|
"CAPS": KEY_CAPSLOCK,
|
|
"SCROLL_LOCK": KEY_SCROLLLOCK,
|
|
"SCRL": KEY_SCROLLLOCK,
|
|
"BRMD": KEY_SCROLLLOCK,
|
|
"NUM_LOCK": KEY_NUMLOCK,
|
|
"NUM": KEY_NUMLOCK,
|
|
"PRINT_SCREEN": KEY_PRINT,
|
|
"PSCR": KEY_PRINT,
|
|
"PAUS": KEY_PAUSE,
|
|
"BRK": KEY_PAUSE,
|
|
"BRMU": KEY_PAUSE,
|
|
"INS": KEY_INSERT,
|
|
"PAGE_UP": KEY_PAGEUP,
|
|
"PGUP": KEY_PAGEUP,
|
|
"DEL": KEY_DELETE,
|
|
"PAGE_DOWN": KEY_PAGEDOWN,
|
|
"PGDN": KEY_PAGEDOWN,
|
|
"RGHT": KEY_RIGHT,
|
|
"APPLICATION": KEY_MENU,
|
|
"APP": KEY_MENU,
|
|
"SYSTEM_REQUEST": KEY_SYSREQ,
|
|
"SYRQ": KEY_SYSREQ,
|
|
"CLR": KEY_CLEAR,
|
|
"RETURN": KEY_ENTER,
|
|
"RETN": KEY_ENTER,
|
|
"AUDIO_MUTE": KEY_VOLUMEMUTE,
|
|
"MUTE": KEY_VOLUMEMUTE,
|
|
"AUDIO_VOL_UP": KEY_VOLUMEUP,
|
|
"VOLU": KEY_VOLUMEUP,
|
|
"AUDIO_VOL_DOWN": KEY_VOLUMEDOWN,
|
|
"VOLD": KEY_VOLUMEDOWN,
|
|
"MEDIA_NEXT_TRACK": KEY_MEDIANEXT,
|
|
"MNXT": KEY_MEDIANEXT,
|
|
"MEDIA_PREV_TRACK": KEY_MEDIAPREVIOUS,
|
|
"MPRV": KEY_MEDIAPREVIOUS,
|
|
"MEDIA_STOP": KEY_MEDIASTOP,
|
|
"MSTP": KEY_MEDIASTOP,
|
|
"MEDIA_PLAY_PAUSE": KEY_MEDIAPLAY,
|
|
"MPLY": KEY_MEDIAPLAY,
|
|
"MAIL": KEY_LAUNCHMAIL,
|
|
"WWW_SEARCH": KEY_SEARCH,
|
|
"WSCH": KEY_SEARCH,
|
|
"WWW_HOME": KEY_SEARCH,
|
|
"WHOM": KEY_SEARCH,
|
|
"WWW_BACK": KEY_BACK,
|
|
"WBAK": KEY_BACK,
|
|
"WWW_FORWARD": KEY_FORWARD,
|
|
"WFWD": KEY_FORWARD,
|
|
"WWW_STOP": KEY_STOP,
|
|
"WSTP": KEY_STOP,
|
|
"WWW_REFRESH": KEY_REFRESH,
|
|
"WREF": KEY_REFRESH,
|
|
"WWW_FAVORITES": KEY_FAVORITES,
|
|
"WFAV": KEY_FAVORITES,
|
|
"MEDIA_FAST_FORWARD": KEY_MEDIANEXT,
|
|
"MFFD": KEY_MEDIANEXT,
|
|
"MEDIA_REWIND": KEY_MEDIAPREVIOUS,
|
|
"MRWD": KEY_MEDIAPREVIOUS,
|
|
"INTERNATIONAL_1": KEY_BACKSLASH,
|
|
"INT1": KEY_BACKSLASH,
|
|
"INTERNATIONAL_3": KEY_YEN,
|
|
"INT3": KEY_YEN,
|
|
"KP_SLASH": KEY_KP_DIVIDE,
|
|
"PSLS": KEY_KP_DIVIDE,
|
|
"KP_ASTERISK": KEY_KP_MULTIPLY,
|
|
"PAST": KEY_KP_MULTIPLY,
|
|
"KP_MINUS": KEY_KP_SUBTRACT,
|
|
"PMNS": KEY_KP_SUBTRACT,
|
|
"KP_PLUS": KEY_KP_ADD,
|
|
"PPLS": KEY_KP_ADD,
|
|
"KP_ENTER": KEY_KP_ENTER,
|
|
"PENT": KEY_KP_ENTER,
|
|
"KP_1": KEY_KP_1,
|
|
"P1": KEY_KP_1,
|
|
"KP_2": KEY_KP_2,
|
|
"P2": KEY_KP_2,
|
|
"KP_3": KEY_KP_3,
|
|
"P3": KEY_KP_3,
|
|
"KP_4": KEY_KP_4,
|
|
"P4": KEY_KP_4,
|
|
"KP_5": KEY_KP_5,
|
|
"P5": KEY_KP_5,
|
|
"KP_6": KEY_KP_6,
|
|
"P6": KEY_KP_6,
|
|
"KP_7": KEY_KP_7,
|
|
"P7": KEY_KP_7,
|
|
"KP_8": KEY_KP_8,
|
|
"P8": KEY_KP_8,
|
|
"KP_9": KEY_KP_9,
|
|
"P9": KEY_KP_9,
|
|
"KP_0": KEY_KP_0,
|
|
"P0": KEY_KP_0,
|
|
"KP_DOT": KEY_KP_PERIOD,
|
|
"PDOT": KEY_KP_PERIOD,
|
|
"KP_COMMA": KEY_KP_PERIOD,
|
|
"PCMM": KEY_KP_PERIOD,
|
|
}
|
|
|
|
const KEYCODE_MODIFIER_LEFT_MAP: Dictionary[String, Key] = {
|
|
"LEFT_CTRL": KEY_CTRL,
|
|
"LCTL": KEY_CTRL,
|
|
"LEFT_SHIFT": KEY_SHIFT,
|
|
"LSFT": KEY_SHIFT,
|
|
"LEFT_ALT": KEY_ALT,
|
|
"LALT": KEY_ALT,
|
|
"LOPT": KEY_ALT,
|
|
"LEFT_GUI": KEY_META,
|
|
"LGUI": KEY_META,
|
|
"LCMD": KEY_META,
|
|
"LWIN": KEY_META,
|
|
}
|
|
|
|
const KEYCODE_MODIFIER_RIGHT_MAP: Dictionary[String, Key] = {
|
|
"RIGHT_CTRL": KEY_CTRL,
|
|
"RCTL": KEY_CTRL,
|
|
"RIGHT_SHIFT": KEY_SHIFT,
|
|
"RSFT": KEY_SHIFT,
|
|
"RIGHT_ALT": KEY_ALT,
|
|
"RALT": KEY_ALT,
|
|
"ROPT": KEY_ALT,
|
|
"RIGHT_GUI": KEY_META,
|
|
"RGUI": KEY_META,
|
|
"RCMD": KEY_META,
|
|
"RWIN": KEY_META,
|
|
}
|
|
|
|
const KEYBOARD_NAME := "keyboard_name"
|
|
const LAYOUTS := "layouts"
|
|
const LAYOUT := "layout"
|
|
|
|
const W := "w"
|
|
const H := "h"
|
|
const X := "x"
|
|
const Y := "y"
|
|
|
|
const R := "r"
|
|
const RX := "rx"
|
|
const RY := "ry"
|
|
|
|
const KEYCODE := "keycode"
|
|
|
|
var _name: String
|
|
var _rows: Array[Array]
|
|
var _file_name: String
|
|
var _has_errors: bool
|
|
|
|
|
|
func _init(data: Dictionary, file_name: String) -> void:
|
|
_file_name = file_name
|
|
if data.has(KEYBOARD_NAME) and data[KEYBOARD_NAME] is String:
|
|
_name = data[KEYBOARD_NAME]
|
|
|
|
if (
|
|
not data.has(LAYOUTS)
|
|
or data[LAYOUTS] is not Dictionary
|
|
or (data[LAYOUTS] as Dictionary).size() == 0
|
|
):
|
|
push_error("%s: '%s' is missing" % [_file_name, LAYOUTS])
|
|
_has_errors = true
|
|
return
|
|
|
|
var layout_name := (data[LAYOUTS] as Dictionary).keys()[0] as String
|
|
|
|
if (
|
|
data[LAYOUTS][layout_name] is not Dictionary
|
|
or not (data[LAYOUTS][layout_name] as Dictionary).has(LAYOUT)
|
|
or data[LAYOUTS][layout_name][LAYOUT] is not Array
|
|
):
|
|
push_error(
|
|
"%s: '%s.%s.%s' is missing" % [_file_name, LAYOUTS, layout_name, LAYOUT]
|
|
)
|
|
_has_errors = true
|
|
return
|
|
|
|
var data_keys := data[LAYOUTS][layout_name][LAYOUT] as Array
|
|
var err := _get_keymap_keys(data_keys, file_name)
|
|
if err:
|
|
_has_errors = true
|
|
return
|
|
_rows = _deserialize_keys(data_keys)
|
|
|
|
|
|
func get_name() -> String:
|
|
return _name
|
|
|
|
|
|
func get_rows() -> Array[Array]:
|
|
return _rows
|
|
|
|
|
|
func has_errors() -> bool:
|
|
return _has_errors
|
|
|
|
|
|
func _sort_data_keys(a: Variant, b: Variant) -> bool:
|
|
if a is not Dictionary or b is not Dictionary:
|
|
return false
|
|
var a_dict := a as Dictionary
|
|
var b_dict := b as Dictionary
|
|
if not a_dict.has(Y) or not b_dict.has(Y):
|
|
return false
|
|
var a_y := a_dict[Y] as float
|
|
var b_y := b_dict[Y] as float
|
|
if a_y == b_y and a_dict.has(X) and a_dict.has(X):
|
|
return (a_dict[X] as float) < (b_dict[X] as float)
|
|
return a_y < b_y
|
|
|
|
|
|
func _deserialize_keys(data_keys: Array) -> Array[Array]:
|
|
data_keys.sort_custom(_sort_data_keys)
|
|
var layout_rows: Array[Array] = []
|
|
var layout_row: Array[Dictionary] = []
|
|
var prev_pos_x: float = 0
|
|
var prev_pos_y: float = 0
|
|
|
|
for i in range(data_keys.size()):
|
|
if data_keys[i] is not Dictionary:
|
|
continue
|
|
var data_key := data_keys[i] as Dictionary
|
|
if not data_key.has(X) or not data_key.has(Y):
|
|
continue
|
|
|
|
if prev_pos_y != data_key[Y]:
|
|
prev_pos_x = 0
|
|
prev_pos_y += 1
|
|
if layout_row:
|
|
layout_rows.append(layout_row)
|
|
layout_row = []
|
|
|
|
var keycode := (
|
|
_get_keycode_from_keymap_key(data_key[KEYCODE] as String)
|
|
if data_key.has(KEYCODE)
|
|
else KEY_NONE
|
|
)
|
|
var key_dict := {KeyProps.KEY: keycode}
|
|
(
|
|
key_dict
|
|
. merge(
|
|
_deserialize_key(
|
|
data_key,
|
|
data_key[X] as float - prev_pos_x,
|
|
data_key[Y] as float - prev_pos_y,
|
|
)
|
|
)
|
|
)
|
|
|
|
var location := (
|
|
_get_key_location(data_key[KEYCODE] as String)
|
|
if data_key.has(KEYCODE)
|
|
else KEY_LOCATION_UNSPECIFIED
|
|
)
|
|
if location != KEY_LOCATION_UNSPECIFIED:
|
|
key_dict[KeyProps.LOC] = location
|
|
|
|
layout_row.append(key_dict)
|
|
|
|
var width: float = data_key[W] if data_key.has(W) else 1.0
|
|
|
|
prev_pos_x = data_key[X] + width
|
|
prev_pos_y = data_key[Y]
|
|
|
|
if layout_row:
|
|
layout_rows.append(layout_row)
|
|
return layout_rows
|
|
|
|
|
|
func _deserialize_key(data_key: Dictionary, pos_x: float, pos_y: float) -> Dictionary:
|
|
var key_dict: Dictionary = {}
|
|
|
|
if data_key.has(W):
|
|
key_dict[KeyProps.W] = data_key[W]
|
|
if data_key.has(H):
|
|
key_dict[KeyProps.H] = data_key[H]
|
|
if pos_x != 0:
|
|
key_dict[KeyProps.X] = pos_x
|
|
if pos_y != 0:
|
|
key_dict[KeyProps.Y] = pos_y
|
|
|
|
if data_key.has(R):
|
|
key_dict[KeyProps.R] = data_key[R]
|
|
if data_key.has(RX):
|
|
key_dict[KeyProps.PX] = data_key[RX]
|
|
if data_key.has(RY):
|
|
key_dict[KeyProps.PY] = data_key[RY]
|
|
|
|
return key_dict
|
|
|
|
|
|
func _get_keymap_keys(data_keys: Array, json_file_name: String) -> Error:
|
|
var c_file_name := json_file_name.substr(0, json_file_name.rfind(".") + 1) + "c"
|
|
|
|
var file := FileAccess.open(
|
|
LayoutConfig.CUSTOM_LAYOUTS_PATH.path_join(c_file_name), FileAccess.READ
|
|
)
|
|
if not file:
|
|
var file_err := FileAccess.get_open_error()
|
|
push_error(
|
|
"%s: error opening file '%s': %s" % [_file_name, c_file_name, error_string(file_err)]
|
|
)
|
|
return FAILED
|
|
|
|
var content := file.get_as_text()
|
|
var layout_regex := RegEx.new()
|
|
layout_regex.compile(
|
|
r".*=\s(?<name>LAYOUT.*)\s*\((?<keys>[^()]*(?:\([^()]*\)[^()]*)*)\)"
|
|
)
|
|
var keys_regex := RegEx.new()
|
|
keys_regex.compile(r"\w+(?:\(.*?\))?")
|
|
|
|
var keymap_keys: Array[String] = []
|
|
|
|
var layout_match := layout_regex.search(content)
|
|
if not layout_match:
|
|
push_error(
|
|
"%s: no layout keymap definitions found in '%s'" % [_file_name, c_file_name]
|
|
)
|
|
return FAILED
|
|
|
|
for key_matches in keys_regex.search_all(layout_match.get_string("keys")):
|
|
keymap_keys.append(key_matches.get_string())
|
|
|
|
for i in range(mini(data_keys.size(), keymap_keys.size())):
|
|
data_keys[i][KEYCODE] = keymap_keys[i]
|
|
|
|
return OK
|
|
|
|
|
|
func _get_keymap_key_unprefixed(keymap_key_prefixed: String) -> String:
|
|
return keymap_key_prefixed.substr(3)
|
|
|
|
|
|
func _get_keycode_from_keymap_key(keymap_key_prefixed: String) -> Key:
|
|
var keymap_key := _get_keymap_key_unprefixed(keymap_key_prefixed)
|
|
var keycode := KEY_NONE
|
|
|
|
keycode = OS.find_keycode_from_string(keymap_key)
|
|
|
|
if keycode == KEY_NONE and KEYCODE_MAP.has(keymap_key):
|
|
keycode = KEYCODE_MAP[keymap_key]
|
|
|
|
if keycode == KEY_NONE and KEYCODE_MODIFIER_LEFT_MAP.has(keymap_key):
|
|
keycode = KEYCODE_MODIFIER_LEFT_MAP[keymap_key]
|
|
|
|
if keycode == KEY_NONE and KEYCODE_MODIFIER_RIGHT_MAP.has(keymap_key):
|
|
keycode = KEYCODE_MODIFIER_RIGHT_MAP[keymap_key]
|
|
|
|
if keycode == KEY_NONE:
|
|
push_warning(
|
|
"%s: could not recognize key label '%s'" % [_file_name, keymap_key_prefixed]
|
|
)
|
|
_has_errors = true
|
|
|
|
return keycode
|
|
|
|
|
|
func _get_key_location(keymap_key_prefixed: String) -> KeyLocation:
|
|
var keymap_key := _get_keymap_key_unprefixed(keymap_key_prefixed)
|
|
if KEYCODE_MODIFIER_LEFT_MAP.has(keymap_key):
|
|
return KEY_LOCATION_LEFT
|
|
if KEYCODE_MODIFIER_RIGHT_MAP.has(keymap_key):
|
|
return KEY_LOCATION_RIGHT
|
|
return KEY_LOCATION_UNSPECIFIED
|