keymap.py 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. import json
  2. import logging
  3. import os
  4. from traceback import format_exc
  5. from .errors import NoSuchKeyboardError
  6. # The `keymap.c` template to use when a keyboard doesn't have its own
  7. DEFAULT_KEYMAP_C = """#include QMK_KEYBOARD_H
  8. /* THIS FILE WAS GENERATED!
  9. *
  10. * This file was generated by qmk-compile-json. You may or may not want to
  11. * edit it directly.
  12. */
  13. const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
  14. __KEYMAP_GOES_HERE__
  15. };
  16. """
  17. # Helper Functions
  18. def find_dir(keyboard):
  19. """Locate the correct directory for storing a keymap.
  20. """
  21. for directory in ['.', '..', '../..', '../../..', '../../../..', '../../../../..']:
  22. basepath = os.path.normpath(os.path.join('keyboards', keyboard, directory, 'keymaps'))
  23. if os.path.exists(basepath):
  24. return basepath
  25. logging.error('Could not find keymaps directory!')
  26. raise NoSuchKeyboardError('Could not find keymaps directory for: %s' % keyboard)
  27. def template(keyboard, keymap):
  28. """Returns the `keymap.c` template for a keyboard.
  29. If a template exists in `keyboards/<keyboard>/templates/keymap.c` that
  30. text will be used instead of `DEFAULT_KEYMAP_C`.
  31. """
  32. template_name = 'keyboards/%s/templates/keymap.c' % keyboard
  33. if os.path.exists(template_name):
  34. with open(template_name, 'r') as fd:
  35. template_text = fd.read()
  36. return DEFAULT_KEYMAP_C.replace('__KEYMAP_GOES_HERE__', keymap)
  37. def generate(keyboard, layout, layers):
  38. """Generate the keymap source code.
  39. """
  40. layer_txt = []
  41. for layer_num, layer in enumerate(layers):
  42. if layer_num != 0:
  43. layer_txt[-1] = layer_txt[-1] + ','
  44. layer_keys = ', '.join(layer)
  45. layer_txt.append('\t[%s] = %s(%s)' % (layer_num, layout, layer_keys))
  46. keymap = '\n'.join(layer_txt)
  47. keymap_c = template(keyboard, keymap)
  48. return keymap_c
  49. def write(keyboard, keymap, layout, layers):
  50. """Generate the `keymap.c` and write it to disk.
  51. """
  52. keymap_c = generate(keyboard, layout, layers)
  53. keymap_path = find_dir(keyboard)
  54. keymap_dir = os.path.join(keymap_path, keymap)
  55. keymap_file = os.path.join(keymap_dir, 'keymap.c')
  56. if not os.path.exists(keymap_dir):
  57. os.makedirs(keymap_dir)
  58. with open(keymap_file, 'w') as keymap_fd:
  59. keymap_fd.write(keymap_c)
  60. return keymap_file
  61. # Public functions
  62. def compile_firmware(keyboard, keymap, layout, layers):
  63. """Compile a firmware.
  64. """
  65. result = {
  66. 'keyboard': keyboard,
  67. 'layout': layout,
  68. 'keymap': keymap,
  69. 'command': ['make', 'COLOR=false', ':'.join((keyboard, keymap))],
  70. 'returncode': -2,
  71. 'output': '',
  72. 'firmware': None,
  73. 'firmware_filename': '',
  74. }
  75. try:
  76. job = get_current_job()
  77. result['id'] = job.id
  78. checkout_qmk()
  79. # Sanity checks
  80. if not os.path.exists('qmk_firmware/keyboards/' + keyboard):
  81. logging.error('Unknown keyboard: %s', keyboard)
  82. return {'returncode': -1, 'command': '', 'output': 'Unknown keyboard!', 'firmware': None}
  83. for pathname in (
  84. 'qmk_firmware/keyboards/%s/keymaps/%s' % (keyboard, keymap),
  85. 'qmk_firmware/keyboards/%s/../keymaps/%s' % (keyboard, keymap),
  86. ):
  87. if os.path.exists(pathname):
  88. logging.error('Name collision! %s already exists! This should not happen!', pathname)
  89. return {'returncode': -1, 'command': '', 'output': 'Keymap name collision! %s already exists!' % (pathname), 'firmware': None}
  90. # Build the keyboard firmware
  91. write_keymap(result['keyboard'], result['keymap'], layers)
  92. store_firmware_binary(result)
  93. except Exception as e:
  94. result['returncode'] = -3
  95. result['exception'] = e.__class__.__name__
  96. result['stacktrace'] = format_exc()
  97. if not result['output']:
  98. result['output'] = result['stacktrace']
  99. return result