keymap.py 4.1 KB

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