Skip to content

Instantly share code, notes, and snippets.

@brunomlopes
Created April 7, 2021 08:50
Show Gist options
  • Save brunomlopes/7592e34c6037651d1563cda7a6c11994 to your computer and use it in GitHub Desktop.
Save brunomlopes/7592e34c6037651d1563cda7a6c11994 to your computer and use it in GitHub Desktop.
A quick parser for qmk keymap.c files, and via layouts
# %%
import json,math,itertools
input_file_path = r"C:\Users\bruno\OneDrive\Documents\QMK Via Layouts\sofle bl 2.4.json"
with open(input_file_path) as input:
exported_via_configuration = json.load(input)
layers = exported_via_configuration["layers"]
macros = exported_via_configuration["macros"]
# %%
# %%
def from_ix(ix):
def _(layout):
return layout[ix]
return _
def split_spacer(n):
def _(layout):
return " "*n
return _
def e(layout): return ""
sofle_layout_from_via = (
(from_ix(0) , from_ix(1) , from_ix(2) , from_ix(3) , from_ix(4) , from_ix(5) , e , split_spacer(2), e , from_ix(35) , from_ix(34) , from_ix(33) , from_ix(32) , from_ix(31) , from_ix(30)),
(from_ix(6) , from_ix(7) , from_ix(8) , from_ix(9) , from_ix(10) , from_ix(11) , e , split_spacer(2), e , from_ix(41) , from_ix(40) , from_ix(39) , from_ix(38) , from_ix(37) , from_ix(36)),
(from_ix(12) , from_ix(13) , from_ix(14) , from_ix(15) , from_ix(16) , from_ix(17) , e , split_spacer(2), e , from_ix(47) , from_ix(46) , from_ix(45) , from_ix(44) , from_ix(43) , from_ix(42)),
(from_ix(18) , from_ix(19) , from_ix(20) , from_ix(21) , from_ix(22) , from_ix(23) , from_ix(29) , split_spacer(2), from_ix(59) , from_ix(53) , from_ix(52) , from_ix(51) , from_ix(50) , from_ix(49) , from_ix(48)),
(e , e , from_ix(24) , from_ix(25) , from_ix(26) , from_ix(27) , from_ix(28) , split_spacer(2), from_ix(58) , from_ix(57) , from_ix(56) , from_ix(55) , from_ix(54) , e , e, ),
)
sofle_layout_from_keymap = (
(from_ix(0) , from_ix(1) , from_ix(2) , from_ix(3) , from_ix(4) , from_ix(5) , e , split_spacer(2), e , from_ix(6) , from_ix(7) , from_ix(8) , from_ix(9) , from_ix(10) , from_ix(11)),
(from_ix(12) , from_ix(13) , from_ix(14) , from_ix(15) , from_ix(16) , from_ix(17) , e , split_spacer(2), e , from_ix(18) , from_ix(19) , from_ix(20) , from_ix(21) , from_ix(22) , from_ix(23)),
(from_ix(24) , from_ix(25) , from_ix(26) , from_ix(27) , from_ix(28) , from_ix(29) , e , split_spacer(2), e , from_ix(30) , from_ix(31) , from_ix(32) , from_ix(33) , from_ix(34) , from_ix(35)),
(from_ix(36) , from_ix(37) , from_ix(38) , from_ix(39) , from_ix(40) , from_ix(41) , from_ix(42) , split_spacer(2), from_ix(43) , from_ix(44) , from_ix(45) , from_ix(46) , from_ix(47) , from_ix(48) , from_ix(49)),
(e , e , from_ix(50) , from_ix(51) , from_ix(52) , from_ix(53) , from_ix(54) , split_spacer(2), from_ix(55) , from_ix(56) , from_ix(57) , from_ix(58) , from_ix(59) , e , e, ),
)
sofle_layer_names = (
"_BASE",
"_COLEMAK",
"_LOWER",
"_NAV",
"_SYMBOL",
"_NUMPAD"
)
layer_names = sofle_layer_names
board_layout = sofle_layout_from_keymap
import re
def replace_with_layer_symbol(key_label):
layer_call_regexps = (
re.compile("LT\((?P<layer>\d+),[\w_]+\)"),
re.compile("TT\((?P<layer>\d+)\)"),
re.compile("MO\((?P<layer>\d+)\)"),
re.compile("TG\((?P<layer>\d+)\)"),
re.compile("OSL\((?P<layer>\d+)\)"),
)
for r in layer_call_regexps:
match = r.match(key_label)
if match:
layer = match.groupdict()["layer"]
return key_label.replace(layer, layer_names[int(layer)])
return key_label
def common_replacements(key_label):
replacements = {
"KC_TRNS":"_______",
"SPC_FN3":"LT(3,KC_SPC)",
"SPC_FN2":"LT(2,KC_SPC)",
}
if key_label in replacements.keys():
return replacements[key_label]
return key_label
def run_replacements(key_label):
return replace_with_layer_symbol(common_replacements(key_label))
# %%
def print_keymaps(layers, board_layout):
print("enum layers {")
for layer_name in layer_names:
print(f"\t{layer_name},")
print("};\n\n")
min_padding = 1
matrix_shape = (len(board_layout), max(len(l) for l in board_layout))
print("const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {")
for i,layer in enumerate(layers[:]):
matrix = [[None]*matrix_shape[1] for line in range(matrix_shape[0])]
for line_no,line_content in enumerate(board_layout):
for col_no,col in enumerate(line_content):
key_label = col(layer).strip()
key_label = run_replacements(key_label)
matrix[line_no][col_no] = key_label
print(f"[{layer_names[i]}] = LAYOUT(")
for line_no, line in enumerate(matrix):
print(" ", end="")
for col_no,key in enumerate(line):
longest_key_definition_length = max(len((k or "")) for k in (l[col_no] for l in matrix)) + min_padding
right_padding = " " * ( longest_key_definition_length - len((key or "")) )
left_padding = " "
suffix = ","
# this key is just padding for the layout to appear ok, do not emit a ,
if not key or key.strip() == "":
suffix = " "
# last key which is not padding does not emit a ,
if line_no == len(matrix)-1 and col_no == len([c for i,c in enumerate(line) if i<col_no or c.strip() != ""])-1:
suffix = ""
print(f"{left_padding}{key or ''}{right_padding}{suffix}",end="")
print()
print("),")
print("};")
# %%
# get layers from keymap.c
keymap_c_file_path = r"D:\documents\dev\qmk_firmware\keyboards\sofle\keymaps\bml-via\keymap.c"
with open(keymap_c_file_path) as input:
keymap_c_content = input.readlines()
def read_layouts_from_keymap(content, layout_def_start_marker = "LAYOUT("):
content_reader = iter(content)
try:
while True:
line = next(content_reader)
while layout_def_start_marker not in line:
line = next(content_reader)
line = next(content_reader)
lines = []
while line.strip() != "),":
lines.append(line)
line = next(content_reader)
yield [label.strip() for label in re.split("[,](?!\w+\))","".join(lines))]
except StopIteration:
pass
layers = list(read_layouts_from_keymap(keymap_c_content))
board_layout = sofle_layout_from_keymap
print_keymaps(layers, sofle_layout_from_keymap)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment