keymap.py 3.7 KB

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