lighting_map.py 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. from milc import cli
  2. from qmk.path import normpath
  3. from qmk.commands import dump_lines
  4. from qmk.lighting import load_lighting_spec
  5. from qmk.constants import GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE
  6. PREFIX_MAP = {
  7. 'rgblight': {
  8. 'ifdef': 'RGBLIGHT_EFFECT',
  9. 'def': 'RGBLIGHT_MODE',
  10. },
  11. 'rgb_matrix': {
  12. 'ifdef': 'ENABLE_RGB_MATRIX',
  13. 'def': 'RGB_MATRIX',
  14. },
  15. 'led_matrix': {
  16. 'ifdef': 'ENABLE_LED_MATRIX',
  17. 'def': 'LED_MATRIX',
  18. },
  19. }
  20. def _always_enabled(id):
  21. """Assumption that first effect is always enabled
  22. """
  23. return id == '0x00'
  24. def _wrap_ifdef(line, define):
  25. return f'''
  26. #ifdef {define}
  27. {line}
  28. #endif'''
  29. def _append_lighting_map(lines, feature, spec):
  30. """Translate effect to 'constant id'->'firmware id' lookup table
  31. """
  32. groups = spec.get('groups', {})
  33. ifdef_prefix = PREFIX_MAP[feature]['ifdef']
  34. def_prefix = PREFIX_MAP[feature]['def']
  35. lines.append(f'static const uint8_t {feature}_effect_map[][2] PROGMEM = {{')
  36. for id, obj in spec.get('effects', {}).items():
  37. define = obj['define']
  38. offset = f' + {obj["offset"]}' if obj['offset'] else ''
  39. line = f'{{ {id}, {def_prefix}_{define}{offset}}},'
  40. if not _always_enabled(id):
  41. line = _wrap_ifdef(line, f'{ifdef_prefix}_{define}')
  42. group = groups.get(obj.get('group', None), {}).get('define', None)
  43. if group:
  44. line = _wrap_ifdef(line, group)
  45. lines.append(line)
  46. lines.append('};')
  47. # add helper funcs
  48. lines.append(
  49. f'''
  50. uint8_t {feature}_effect_to_id(uint8_t val) {{
  51. for(uint8_t i = 0; i < ARRAY_SIZE({feature}_effect_map); i++) {{
  52. if (pgm_read_byte(&{feature}_effect_map[i][1]) == val)
  53. return pgm_read_byte(&{feature}_effect_map[i][0]);
  54. }}
  55. return 0xFF;
  56. }}
  57. uint8_t {feature}_id_to_effect(uint8_t val) {{
  58. for(uint8_t i = 0; i < ARRAY_SIZE({feature}_effect_map); i++) {{
  59. if (pgm_read_byte(&{feature}_effect_map[i][0]) == val)
  60. return pgm_read_byte(&{feature}_effect_map[i][1]);
  61. }}
  62. return 0xFF;
  63. }}'''
  64. )
  65. def _append_lighting_bit_field(lines, feature, spec):
  66. """Translate effect to bit of bit-field
  67. """
  68. groups = spec.get('groups', {})
  69. ifdef_prefix = PREFIX_MAP[feature]['ifdef']
  70. lines.append(f'enum {{ ENABLED_{feature.upper()}_EFFECTS = 0')
  71. for id, obj in spec.get('effects', {}).items():
  72. define = obj['define']
  73. line = f' | (1ULL << {id})'
  74. if not _always_enabled(id):
  75. line = _wrap_ifdef(line, f'{ifdef_prefix}_{define}')
  76. group = groups.get(obj.get('group', None), {}).get('define', None)
  77. if group:
  78. line = _wrap_ifdef(line, group)
  79. lines.append(line)
  80. lines.append('};')
  81. def _append_lighting_mapping(lines, feature):
  82. """Generate lookup table and bit-field of effect
  83. """
  84. spec = load_lighting_spec(feature)
  85. _append_lighting_bit_field(lines, feature, spec)
  86. _append_lighting_map(lines, feature, spec)
  87. @cli.argument('-o', '--output', arg_only=True, type=normpath, help='File to write to')
  88. @cli.argument('-q', '--quiet', arg_only=True, action='store_true', help='Quiet mode, only output error messages')
  89. @cli.argument('-f', '--feature', required=True, help='Feature to generate map', choices=PREFIX_MAP.keys())
  90. @cli.subcommand('Generates effect header.')
  91. def generate_lighting_map(cli):
  92. # Preamble
  93. lines = [GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE, '#pragma once', '// clang-format off']
  94. _append_lighting_mapping(lines, cli.args.feature)
  95. dump_lines(cli.args.output, lines, cli.args.quiet)