logo

qmk_firmware

custom branch of QMK firmware git clone https://anongit.hacktivis.me/git/qmk_firmware.git

via2json.py (6258B)


  1. """Generate a keymap.c from a configurator export.
  2. """
  3. import json
  4. import re
  5. from milc import cli
  6. import qmk.keyboard
  7. import qmk.path
  8. from qmk.info import info_json
  9. from qmk.json_encoders import KeymapJSONEncoder
  10. from qmk.commands import dump_lines
  11. from qmk.keymap import generate_json
  12. def _find_via_layout_macro(keyboard_data):
  13. """Assume layout macro when only 1 is available
  14. """
  15. layouts = list(keyboard_data['layouts'].keys())
  16. return layouts[0] if len(layouts) == 1 else None
  17. def _convert_macros(via_macros):
  18. via_macros = list(filter(lambda f: bool(f), via_macros))
  19. if len(via_macros) == 0:
  20. return list()
  21. split_regex = re.compile(r'(}\,)|(\,{)')
  22. macro_group_regex = re.compile(r'({.+?})')
  23. macros = list()
  24. for via_macro in via_macros:
  25. # Split VIA macro to its elements
  26. macro = split_regex.split(via_macro)
  27. # Remove junk elements (None, '},' and ',{')
  28. macro = list(filter(lambda f: False if f in (None, '},', ',{') else True, macro))
  29. macro_data = list()
  30. for m in macro:
  31. if '{' in m or '}' in m:
  32. # Split macro groups
  33. macro_groups = macro_group_regex.findall(m)
  34. for macro_group in macro_groups:
  35. # Remove whitespaces and curly braces from around group
  36. macro_group = macro_group.strip(' {}')
  37. macro_action = 'tap'
  38. macro_keycodes = []
  39. if macro_group[0] == '+':
  40. macro_action = 'down'
  41. macro_keycodes.append(macro_group[1:])
  42. elif macro_group[0] == '-':
  43. macro_action = 'up'
  44. macro_keycodes.append(macro_group[1:])
  45. else:
  46. macro_keycodes.extend(macro_group.split(',') if ',' in macro_group else [macro_group])
  47. # Remove the KC prefixes
  48. macro_keycodes = list(map(lambda s: s.replace('KC_', ''), macro_keycodes))
  49. macro_data.append({"action": macro_action, "keycodes": macro_keycodes})
  50. else:
  51. # Found text
  52. macro_data.append(m)
  53. macros.append(macro_data)
  54. return macros
  55. def _fix_macro_keys(keymap_data):
  56. macro_no = re.compile(r'MACRO0?\(([0-9]{1,2})\)')
  57. for i in range(0, len(keymap_data)):
  58. for j in range(0, len(keymap_data[i])):
  59. kc = keymap_data[i][j]
  60. m = macro_no.match(kc)
  61. if m:
  62. keymap_data[i][j] = f'MC_{m.group(1)}'
  63. return keymap_data
  64. def _via_to_keymap(via_backup, keyboard_data, keymap_layout):
  65. # Check if passed LAYOUT is correct
  66. layout_data = keyboard_data['layouts'].get(keymap_layout)
  67. if not layout_data:
  68. cli.log.error(f'LAYOUT macro {keymap_layout} is not a valid one for keyboard {cli.args.keyboard}!')
  69. return None
  70. layout_data = layout_data['layout']
  71. sorting_hat = list()
  72. for index, data in enumerate(layout_data):
  73. sorting_hat.append([index, data['matrix']])
  74. sorting_hat.sort(key=lambda k: (k[1][0], k[1][1]))
  75. pos = 0
  76. for row_num in range(0, keyboard_data['matrix_size']['rows']):
  77. for col_num in range(0, keyboard_data['matrix_size']['cols']):
  78. if pos >= len(sorting_hat) or sorting_hat[pos][1][0] != row_num or sorting_hat[pos][1][1] != col_num:
  79. sorting_hat.insert(pos, [None, [row_num, col_num]])
  80. else:
  81. sorting_hat.append([None, [row_num, col_num]])
  82. pos += 1
  83. keymap_data = list()
  84. for layer in via_backup['layers']:
  85. pos = 0
  86. layer_data = list()
  87. for key in layer:
  88. if sorting_hat[pos][0] is not None:
  89. layer_data.append([sorting_hat[pos][0], key])
  90. pos += 1
  91. layer_data.sort()
  92. layer_data = [kc[1] for kc in layer_data]
  93. keymap_data.append(layer_data)
  94. return keymap_data
  95. @cli.argument('-o', '--output', arg_only=True, type=qmk.path.normpath, help='File to write to')
  96. @cli.argument('-q', '--quiet', arg_only=True, action='store_true', help="Quiet mode, only output error messages")
  97. @cli.argument('filename', type=qmk.path.FileType('r'), arg_only=True, help='VIA Backup JSON file')
  98. @cli.argument('-kb', '--keyboard', type=qmk.keyboard.keyboard_folder, completer=qmk.keyboard.keyboard_completer, arg_only=True, required=True, help='The keyboard\'s name')
  99. @cli.argument('-km', '--keymap', arg_only=True, default='via2json', help='The keymap\'s name')
  100. @cli.argument('-l', '--layout', arg_only=True, help='The keymap\'s layout')
  101. @cli.subcommand('Convert a VIA backup json to keymap.json format.')
  102. def via2json(cli):
  103. """Convert a VIA backup json to keymap.json format.
  104. This command uses the `qmk.keymap` module to generate a keymap.json from a VIA backup json. The generated keymap is written to stdout, or to a file if -o is provided.
  105. """
  106. # Load the VIA backup json
  107. with cli.args.filename.open('r') as fd:
  108. via_backup = json.load(fd)
  109. keyboard_data = info_json(cli.args.keyboard)
  110. # Find appropriate layout macro
  111. keymap_layout = cli.args.layout if cli.args.layout else _find_via_layout_macro(keyboard_data)
  112. if not keymap_layout:
  113. cli.log.error(f"Couldn't find LAYOUT macro for keyboard {cli.args.keyboard}. Please specify it with the '-l' argument.")
  114. return False
  115. # Get keycode array
  116. keymap_data = _via_to_keymap(via_backup, keyboard_data, keymap_layout)
  117. if not keymap_data:
  118. cli.log.error(f'Could not extract valid keycode data from VIA backup matching keyboard {cli.args.keyboard}!')
  119. return False
  120. # Convert macros
  121. macro_data = list()
  122. if via_backup.get('macros'):
  123. macro_data = _convert_macros(via_backup['macros'])
  124. # Replace VIA macro keys with JSON keymap ones
  125. keymap_data = _fix_macro_keys(keymap_data)
  126. # Generate the keymap.json
  127. keymap_json = generate_json(cli.args.keymap, cli.args.keyboard, keymap_layout, keymap_data, macro_data)
  128. keymap_lines = [json.dumps(keymap_json, cls=KeymapJSONEncoder, sort_keys=True)]
  129. dump_lines(cli.args.output, keymap_lines, cli.args.quiet)