info.py 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. """Keyboard information script.
  2. Compile an info.json for a particular keyboard and pretty-print it.
  3. """
  4. import sys
  5. import json
  6. from milc import cli
  7. from qmk.json_encoders import InfoJSONEncoder
  8. from qmk.constants import COL_LETTERS, ROW_LETTERS
  9. from qmk.decorators import automagic_keyboard, automagic_keymap
  10. from qmk.keyboard import keyboard_completer, keyboard_folder, render_layouts, render_layout, rules_mk
  11. from qmk.keymap import locate_keymap
  12. from qmk.info import info_json
  13. from qmk.path import is_keyboard
  14. UNICODE_SUPPORT = sys.stdout.encoding.lower().startswith('utf')
  15. def show_keymap(kb_info_json, keymap_data, title_caps=True):
  16. """Render the keymap in ascii art.
  17. """
  18. layout_name = keymap_data['layout']
  19. layout_name = kb_info_json.get('layout_aliases', {}).get(layout_name, layout_name) # Resolve alias names
  20. for layer_num, layer in enumerate(keymap_data['layers']):
  21. if title_caps:
  22. cli.echo('{fg_cyan}Keymap %s Layer %s{fg_reset}:', cli.config.info.keymap, layer_num)
  23. else:
  24. cli.echo('{fg_cyan}keymap.%s.layer.%s{fg_reset}:', cli.config.info.keymap, layer_num)
  25. print(render_layout(kb_info_json['layouts'][layout_name]['layout'], cli.config.info.ascii, layer))
  26. def show_layouts(kb_info_json, title_caps=True):
  27. """Render the layouts with info.json labels.
  28. """
  29. for layout_name, layout_art in render_layouts(kb_info_json, cli.config.info.ascii).items():
  30. title = f'Layout {layout_name.title()}' if title_caps else f'layouts.{layout_name}'
  31. cli.echo('{fg_cyan}%s{fg_reset}:', title)
  32. print(layout_art) # Avoid passing dirty data to cli.echo()
  33. def show_matrix(kb_info_json, title_caps=True):
  34. """Render the layout with matrix labels in ascii art.
  35. """
  36. for layout_name, layout in kb_info_json['layouts'].items():
  37. # Build our label list
  38. labels = []
  39. for key in layout['layout']:
  40. if 'matrix' in key:
  41. row = ROW_LETTERS[key['matrix'][0]]
  42. col = COL_LETTERS[key['matrix'][1]]
  43. labels.append(row + col)
  44. else:
  45. labels.append('')
  46. # Print the header
  47. if title_caps:
  48. cli.echo('{fg_blue}Matrix for "%s"{fg_reset}:', layout_name)
  49. else:
  50. cli.echo('{fg_blue}matrix_%s{fg_reset}:', layout_name)
  51. print(render_layout(kb_info_json['layouts'][layout_name]['layout'], cli.config.info.ascii, labels))
  52. def print_friendly_output(kb_info_json):
  53. """Print the info.json in a friendly text format.
  54. """
  55. cli.echo('{fg_blue}Keyboard Name{fg_reset}: %s', kb_info_json.get('keyboard_name', 'Unknown'))
  56. cli.echo('{fg_blue}Manufacturer{fg_reset}: %s', kb_info_json.get('manufacturer', 'Unknown'))
  57. if 'url' in kb_info_json:
  58. cli.echo('{fg_blue}Website{fg_reset}: %s', kb_info_json.get('url', ''))
  59. if kb_info_json.get('maintainer', 'qmk') == 'qmk':
  60. cli.echo('{fg_blue}Maintainer{fg_reset}: QMK Community')
  61. else:
  62. cli.echo('{fg_blue}Maintainer{fg_reset}: %s', kb_info_json['maintainer'])
  63. cli.echo('{fg_blue}Keyboard Folder{fg_reset}: %s', kb_info_json.get('keyboard_folder', 'Unknown'))
  64. cli.echo('{fg_blue}Layouts{fg_reset}: %s', ', '.join(sorted(kb_info_json['layouts'].keys())))
  65. cli.echo('{fg_blue}Processor{fg_reset}: %s', kb_info_json.get('processor', 'Unknown'))
  66. cli.echo('{fg_blue}Bootloader{fg_reset}: %s', kb_info_json.get('bootloader', 'Unknown'))
  67. if 'layout_aliases' in kb_info_json:
  68. aliases = [f'{key}={value}' for key, value in kb_info_json['layout_aliases'].items()]
  69. cli.echo('{fg_blue}Layout aliases:{fg_reset} %s' % (', '.join(aliases),))
  70. def print_text_output(kb_info_json):
  71. """Print the info.json in a plain text format.
  72. """
  73. for key in sorted(kb_info_json):
  74. if key == 'layouts':
  75. cli.echo('{fg_blue}layouts{fg_reset}: %s', ', '.join(sorted(kb_info_json['layouts'].keys())))
  76. else:
  77. cli.echo('{fg_blue}%s{fg_reset}: %s', key, kb_info_json[key])
  78. if cli.config.info.layouts:
  79. show_layouts(kb_info_json, False)
  80. if cli.config.info.matrix:
  81. show_matrix(kb_info_json, False)
  82. if cli.config_source.info.keymap and cli.config_source.info.keymap != 'config_file':
  83. show_keymap(kb_info_json, False)
  84. def print_dotted_output(kb_info_json, prefix=''):
  85. """Print the info.json in a plain text format with dot-joined keys.
  86. """
  87. for key in sorted(kb_info_json):
  88. new_prefix = f'{prefix}.{key}' if prefix else key
  89. if key in ['parse_errors', 'parse_warnings']:
  90. continue
  91. elif key == 'layouts' and prefix == '':
  92. cli.echo('{fg_blue}layouts{fg_reset}: %s', ', '.join(sorted(kb_info_json['layouts'].keys())))
  93. elif isinstance(kb_info_json[key], dict):
  94. print_dotted_output(kb_info_json[key], new_prefix)
  95. elif isinstance(kb_info_json[key], list):
  96. cli.echo('{fg_blue}%s{fg_reset}: %s', new_prefix, ', '.join(map(str, sorted(kb_info_json[key]))))
  97. else:
  98. cli.echo('{fg_blue}%s{fg_reset}: %s', new_prefix, kb_info_json[key])
  99. def print_parsed_rules_mk(keyboard_name):
  100. rules = rules_mk(keyboard_name)
  101. for k in sorted(rules.keys()):
  102. print('%s = %s' % (k, rules[k]))
  103. return
  104. @cli.argument('-kb', '--keyboard', type=keyboard_folder, completer=keyboard_completer, help='Keyboard to show info for.')
  105. @cli.argument('-km', '--keymap', help='Show the layers for a JSON keymap too.')
  106. @cli.argument('-l', '--layouts', action='store_true', help='Render the layouts.')
  107. @cli.argument('-m', '--matrix', action='store_true', help='Render the layouts with matrix information.')
  108. @cli.argument('-f', '--format', default='friendly', arg_only=True, help='Format to display the data in (friendly, text, json) (Default: friendly).')
  109. @cli.argument('--ascii', action='store_true', default=not UNICODE_SUPPORT, help='Render layout box drawings in ASCII only.')
  110. @cli.argument('-r', '--rules-mk', action='store_true', help='Render the parsed values of the keyboard\'s rules.mk file.')
  111. @cli.subcommand('Keyboard information.')
  112. @automagic_keyboard
  113. @automagic_keymap
  114. def info(cli):
  115. """Compile an info.json for a particular keyboard and pretty-print it.
  116. """
  117. # Determine our keyboard(s)
  118. if not cli.config.info.keyboard:
  119. cli.log.error('Missing parameter: --keyboard')
  120. cli.subcommands['info'].print_help()
  121. return False
  122. if not is_keyboard(cli.config.info.keyboard):
  123. cli.log.error('Invalid keyboard: "%s"', cli.config.info.keyboard)
  124. return False
  125. if bool(cli.args.rules_mk):
  126. print_parsed_rules_mk(cli.config.info.keyboard)
  127. return False
  128. # Pull in keymap overrides if necessary
  129. keymap_path = locate_keymap(cli.config.info.keyboard, cli.config.info.keymap) if cli.config.info.keymap else None
  130. keymap_data = json.load(keymap_path.open(encoding='utf-8')) if keymap_path and keymap_path.suffix == '.json' else None
  131. # Build the info.json file
  132. overrides = keymap_data.get('keyboard_overrides') if keymap_data else None
  133. kb_info_json = info_json(cli.config.info.keyboard, overrides=overrides)
  134. # Output in the requested format
  135. if cli.args.format == 'json':
  136. print(json.dumps(kb_info_json, cls=InfoJSONEncoder))
  137. return True
  138. elif cli.args.format == 'text':
  139. print_dotted_output(kb_info_json)
  140. title_caps = False
  141. elif cli.args.format == 'friendly':
  142. print_friendly_output(kb_info_json)
  143. title_caps = True
  144. else:
  145. cli.log.error('Unknown format: %s', cli.args.format)
  146. return False
  147. if cli.config.info.layouts:
  148. show_layouts(kb_info_json, title_caps)
  149. if cli.config.info.matrix:
  150. show_matrix(kb_info_json, title_caps)
  151. if keymap_data:
  152. show_keymap(kb_info_json, keymap_data, title_caps)