keymap.py 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. """Functions that help you work with QMK keymaps.
  2. """
  3. import os
  4. from pathlib import Path
  5. import qmk.path
  6. import qmk.makefile
  7. # The `keymap.c` template to use when a keyboard doesn't have its own
  8. DEFAULT_KEYMAP_C = """#include QMK_KEYBOARD_H
  9. /* THIS FILE WAS GENERATED!
  10. *
  11. * This file was generated by qmk json2c. You may or may not want to
  12. * edit it directly.
  13. */
  14. const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
  15. __KEYMAP_GOES_HERE__
  16. };
  17. """
  18. def template(keyboard):
  19. """Returns the `keymap.c` template for a keyboard.
  20. If a template exists in `keyboards/<keyboard>/templates/keymap.c` that
  21. text will be used instead of `DEFAULT_KEYMAP_C`.
  22. Args:
  23. keyboard
  24. The keyboard to return a template for.
  25. """
  26. template_file = Path('keyboards/%s/templates/keymap.c' % keyboard)
  27. if template_file.exists():
  28. return template_file.read_text()
  29. return DEFAULT_KEYMAP_C
  30. def _strip_any(keycode):
  31. """Remove ANY() from a keycode.
  32. """
  33. if keycode.startswith('ANY(') and keycode.endswith(')'):
  34. keycode = keycode[4:-1]
  35. return keycode
  36. def generate(keyboard, layout, layers):
  37. """Returns a keymap.c for the specified keyboard, layout, and layers.
  38. Args:
  39. keyboard
  40. The name of the keyboard
  41. layout
  42. The LAYOUT macro this keymap uses.
  43. layers
  44. An array of arrays describing the keymap. Each item in the inner array should be a string that is a valid QMK keycode.
  45. """
  46. layer_txt = []
  47. for layer_num, layer in enumerate(layers):
  48. if layer_num != 0:
  49. layer_txt[-1] = layer_txt[-1] + ','
  50. layer = map(_strip_any, layer)
  51. layer_keys = ', '.join(layer)
  52. layer_txt.append('\t[%s] = %s(%s)' % (layer_num, layout, layer_keys))
  53. keymap = '\n'.join(layer_txt)
  54. keymap_c = template(keyboard)
  55. return keymap_c.replace('__KEYMAP_GOES_HERE__', keymap)
  56. def write(keyboard, keymap, layout, layers):
  57. """Generate the `keymap.c` and write it to disk.
  58. Returns the filename written to.
  59. Args:
  60. keyboard
  61. The name of the keyboard
  62. keymap
  63. The name of the keymap
  64. layout
  65. The LAYOUT macro this keymap uses.
  66. layers
  67. An array of arrays describing the keymap. Each item in the inner array should be a string that is a valid QMK keycode.
  68. """
  69. keymap_c = generate(keyboard, layout, layers)
  70. keymap_file = qmk.path.keymap(keyboard) / keymap / 'keymap.c'
  71. keymap_file.parent.mkdir(parents=True, exist_ok=True)
  72. keymap_file.write_text(keymap_c)
  73. return keymap_file
  74. def list_keymaps(keyboard_name):
  75. """ List the available keymaps for a keyboard.
  76. Args:
  77. keyboard_name: the keyboards full name with vendor and revision if necessary, example: clueboard/66/rev3
  78. Returns:
  79. a set with the names of the available keymaps
  80. """
  81. # parse all the rules.mk files for the keyboard
  82. rules_mk = qmk.makefile.get_rules_mk(keyboard_name)
  83. names = set()
  84. if rules_mk:
  85. # qmk_firmware/keyboards
  86. keyboards_dir = Path.cwd() / "keyboards"
  87. # path to the keyboard's directory
  88. kb_path = keyboards_dir / keyboard_name
  89. # walk up the directory tree until keyboards_dir
  90. # and collect all directories' name with keymap.c file in it
  91. while kb_path != keyboards_dir:
  92. keymaps_dir = kb_path / "keymaps"
  93. if keymaps_dir.exists():
  94. names = names.union([keymap for keymap in os.listdir(str(keymaps_dir)) if (keymaps_dir / keymap / "keymap.c").is_file()])
  95. kb_path = kb_path.parent
  96. # if community layouts are supported, get them
  97. if "LAYOUTS" in rules_mk:
  98. for layout in rules_mk["LAYOUTS"].split():
  99. cl_path = Path.cwd() / "layouts" / "community" / layout
  100. if cl_path.exists():
  101. names = names.union([keymap for keymap in os.listdir(str(cl_path)) if (cl_path / keymap / "keymap.c").is_file()])
  102. return sorted(names)