keymap.py 3.6 KB

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