summaryrefslogtreecommitdiff
path: root/lib/python/qmk/info.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib/python/qmk/info.py')
-rw-r--r--lib/python/qmk/info.py271
1 files changed, 143 insertions, 128 deletions
diff --git a/lib/python/qmk/info.py b/lib/python/qmk/info.py
index 0763433b3d..7460d84ad3 100644
--- a/lib/python/qmk/info.py
+++ b/lib/python/qmk/info.py
@@ -26,13 +26,6 @@ def _valid_community_layout(layout):
return (Path('layouts/default') / layout).exists()
-def _remove_newlines_from_labels(layouts):
- for layout_name, layout_json in layouts.items():
- for key in layout_json['layout']:
- if '\n' in key['label']:
- key['label'] = key['label'].split('\n')[0]
-
-
def info_json(keyboard):
"""Generate the info.json data for a specific keyboard.
"""
@@ -111,23 +104,13 @@ def info_json(keyboard):
# Check that the reported matrix size is consistent with the actual matrix size
_check_matrix(info_data)
- # Remove newline characters from layout labels
- _remove_newlines_from_labels(layouts)
-
return info_data
def _extract_features(info_data, rules):
"""Find all the features enabled in rules.mk.
"""
- # Special handling for bootmagic which also supports a "lite" mode.
- if rules.get('BOOTMAGIC_ENABLE') == 'lite':
- rules['BOOTMAGIC_LITE_ENABLE'] = 'on'
- del rules['BOOTMAGIC_ENABLE']
- if rules.get('BOOTMAGIC_ENABLE') == 'full':
- rules['BOOTMAGIC_ENABLE'] = 'on'
-
- # Process the rest of the rules as booleans
+ # Process booleans rules
for key, value in rules.items():
if key.endswith('_ENABLE'):
key = '_'.join(key.split('_')[:-1]).lower()
@@ -228,6 +211,66 @@ def _extract_audio(info_data, config_c):
info_data['audio'] = {'pins': audio_pins}
+def _extract_encoders_values(config_c, postfix=''):
+ """Common encoder extraction logic
+ """
+ a_pad = config_c.get(f'ENCODERS_PAD_A{postfix}', '').replace(' ', '')[1:-1]
+ b_pad = config_c.get(f'ENCODERS_PAD_B{postfix}', '').replace(' ', '')[1:-1]
+ resolutions = config_c.get(f'ENCODER_RESOLUTIONS{postfix}', '').replace(' ', '')[1:-1]
+
+ default_resolution = config_c.get('ENCODER_RESOLUTION', None)
+
+ if a_pad and b_pad:
+ a_pad = list(filter(None, a_pad.split(',')))
+ b_pad = list(filter(None, b_pad.split(',')))
+ resolutions = list(filter(None, resolutions.split(',')))
+ if default_resolution:
+ resolutions += [default_resolution] * (len(a_pad) - len(resolutions))
+
+ encoders = []
+ for index in range(len(a_pad)):
+ encoder = {'pin_a': a_pad[index], 'pin_b': b_pad[index]}
+ if index < len(resolutions):
+ encoder['resolution'] = int(resolutions[index])
+ encoders.append(encoder)
+
+ return encoders
+
+
+def _extract_encoders(info_data, config_c):
+ """Populate data about encoder pins
+ """
+ encoders = _extract_encoders_values(config_c)
+ if encoders:
+ if 'encoder' not in info_data:
+ info_data['encoder'] = {}
+
+ if 'rotary' in info_data['encoder']:
+ _log_warning(info_data, 'Encoder config is specified in both config.h and info.json (encoder.rotary) (Value: %s), the config.h value wins.' % info_data['encoder']['rotary'])
+
+ info_data['encoder']['rotary'] = encoders
+
+
+def _extract_split_encoders(info_data, config_c):
+ """Populate data about split encoder pins
+ """
+ encoders = _extract_encoders_values(config_c, '_RIGHT')
+ if encoders:
+ if 'split' not in info_data:
+ info_data['split'] = {}
+
+ if 'encoder' not in info_data['split']:
+ info_data['split']['encoder'] = {}
+
+ if 'right' not in info_data['split']['encoder']:
+ info_data['split']['encoder']['right'] = {}
+
+ if 'rotary' in info_data['split']['encoder']['right']:
+ _log_warning(info_data, 'Encoder config is specified in both config.h and info.json (encoder.rotary) (Value: %s), the config.h value wins.' % info_data['split']['encoder']['right']['rotary'])
+
+ info_data['split']['encoder']['right']['rotary'] = encoders
+
+
def _extract_secure_unlock(info_data, config_c):
"""Populate data about the secure unlock sequence
"""
@@ -324,12 +367,10 @@ def _extract_split_right_pins(info_data, config_c):
# Figure out the right half matrix pins
row_pins = config_c.get('MATRIX_ROW_PINS_RIGHT', '').replace('{', '').replace('}', '').strip()
col_pins = config_c.get('MATRIX_COL_PINS_RIGHT', '').replace('{', '').replace('}', '').strip()
- unused_pin_text = config_c.get('UNUSED_PINS_RIGHT')
- unused_pins = unused_pin_text.replace('{', '').replace('}', '').strip() if isinstance(unused_pin_text, str) else None
direct_pins = config_c.get('DIRECT_PINS_RIGHT', '').replace(' ', '')[1:-1]
- if row_pins and col_pins:
- if info_data.get('split', {}).get('matrix_pins', {}).get('right') in info_data:
+ if row_pins or col_pins or direct_pins:
+ if info_data.get('split', {}).get('matrix_pins', {}).get('right', None):
_log_warning(info_data, 'Right hand matrix data is specified in both info.json and config.h, the config.h values win.')
if 'split' not in info_data:
@@ -341,37 +382,14 @@ def _extract_split_right_pins(info_data, config_c):
if 'right' not in info_data['split']['matrix_pins']:
info_data['split']['matrix_pins']['right'] = {}
- info_data['split']['matrix_pins']['right'] = {
- 'cols': _extract_pins(col_pins),
- 'rows': _extract_pins(row_pins),
- }
+ if col_pins:
+ info_data['split']['matrix_pins']['right']['cols'] = _extract_pins(col_pins)
- if direct_pins:
- if info_data.get('split', {}).get('matrix_pins', {}).get('right', {}):
- _log_warning(info_data, 'Right hand matrix data is specified in both info.json and config.h, the config.h values win.')
+ if row_pins:
+ info_data['split']['matrix_pins']['right']['rows'] = _extract_pins(row_pins)
- if 'split' not in info_data:
- info_data['split'] = {}
-
- if 'matrix_pins' not in info_data['split']:
- info_data['split']['matrix_pins'] = {}
-
- if 'right' not in info_data['split']['matrix_pins']:
- info_data['split']['matrix_pins']['right'] = {}
-
- info_data['split']['matrix_pins']['right']['direct'] = _extract_direct_matrix(direct_pins)
-
- if unused_pins:
- if 'split' not in info_data:
- info_data['split'] = {}
-
- if 'matrix_pins' not in info_data['split']:
- info_data['split']['matrix_pins'] = {}
-
- if 'right' not in info_data['split']['matrix_pins']:
- info_data['split']['matrix_pins']['right'] = {}
-
- info_data['split']['matrix_pins']['right']['unused'] = _extract_pins(unused_pins)
+ if direct_pins:
+ info_data['split']['matrix_pins']['right']['direct'] = _extract_direct_matrix(direct_pins)
def _extract_matrix_info(info_data, config_c):
@@ -379,8 +397,6 @@ def _extract_matrix_info(info_data, config_c):
"""
row_pins = config_c.get('MATRIX_ROW_PINS', '').replace('{', '').replace('}', '').strip()
col_pins = config_c.get('MATRIX_COL_PINS', '').replace('{', '').replace('}', '').strip()
- unused_pin_text = config_c.get('UNUSED_PINS')
- unused_pins = unused_pin_text.replace('{', '').replace('}', '').strip() if isinstance(unused_pin_text, str) else None
direct_pins = config_c.get('DIRECT_PINS', '').replace(' ', '')[1:-1]
info_snippet = {}
@@ -406,12 +422,6 @@ def _extract_matrix_info(info_data, config_c):
info_snippet['direct'] = _extract_direct_matrix(direct_pins)
- if unused_pins:
- if 'matrix_pins' not in info_data:
- info_data['matrix_pins'] = {}
-
- info_snippet['unused'] = _extract_pins(unused_pins)
-
if config_c.get('CUSTOM_MATRIX', 'no') != 'no':
if 'matrix_pins' in info_data and 'custom' in info_data['matrix_pins']:
_log_warning(info_data, 'Custom Matrix is specified in both info.json and config.h, the config.h values win.')
@@ -440,6 +450,47 @@ def _extract_device_version(info_data):
info_data['usb']['device_version'] = f'{major}.{minor}.{revision}'
+def _config_to_json(key_type, config_value):
+ """Convert config value using spec
+ """
+ if key_type.startswith('array'):
+ if '.' in key_type:
+ key_type, array_type = key_type.split('.', 1)
+ else:
+ array_type = None
+
+ config_value = config_value.replace('{', '').replace('}', '').strip()
+
+ if array_type == 'int':
+ return list(map(int, config_value.split(',')))
+ else:
+ return config_value.split(',')
+
+ elif key_type == 'bool':
+ return config_value in true_values
+
+ elif key_type == 'hex':
+ return '0x' + config_value[2:].upper()
+
+ elif key_type == 'list':
+ return config_value.split()
+
+ elif key_type == 'int':
+ return int(config_value)
+
+ elif key_type == 'str':
+ return config_value.strip('"')
+
+ elif key_type == 'bcd_version':
+ major = int(config_value[2:4])
+ minor = int(config_value[4])
+ revision = int(config_value[5])
+
+ return f'{major}.{minor}.{revision}'
+
+ return config_value
+
+
def _extract_config_h(info_data, config_c):
"""Pull some keyboard information from existing config.h files
"""
@@ -452,47 +503,23 @@ def _extract_config_h(info_data, config_c):
key_type = info_dict.get('value_type', 'raw')
try:
+ replace_with = info_dict.get('replace_with')
+ if config_key in config_c and info_dict.get('invalid', False):
+ if replace_with:
+ _log_error(info_data, '%s in config.h is no longer a valid option and should be replaced with %s' % (config_key, replace_with))
+ else:
+ _log_error(info_data, '%s in config.h is no longer a valid option and should be removed' % config_key)
+ elif config_key in config_c and info_dict.get('deprecated', False):
+ if replace_with:
+ _log_warning(info_data, '%s in config.h is deprecated in favor of %s and will be removed at a later date' % (config_key, replace_with))
+ else:
+ _log_warning(info_data, '%s in config.h is deprecated and will be removed at a later date' % config_key)
+
if config_key in config_c and info_dict.get('to_json', True):
if dotty_info.get(info_key) and info_dict.get('warn_duplicate', True):
_log_warning(info_data, '%s in config.h is overwriting %s in info.json' % (config_key, info_key))
- if key_type.startswith('array'):
- if '.' in key_type:
- key_type, array_type = key_type.split('.', 1)
- else:
- array_type = None
-
- config_value = config_c[config_key].replace('{', '').replace('}', '').strip()
-
- if array_type == 'int':
- dotty_info[info_key] = list(map(int, config_value.split(',')))
- else:
- dotty_info[info_key] = config_value.split(',')
-
- elif key_type == 'bool':
- dotty_info[info_key] = config_c[config_key] in true_values
-
- elif key_type == 'hex':
- dotty_info[info_key] = '0x' + config_c[config_key][2:].upper()
-
- elif key_type == 'list':
- dotty_info[info_key] = config_c[config_key].split()
-
- elif key_type == 'int':
- dotty_info[info_key] = int(config_c[config_key])
-
- elif key_type == 'str':
- dotty_info[info_key] = config_c[config_key].strip('"')
-
- elif key_type == 'bcd_version':
- major = int(config_c[config_key][2:4])
- minor = int(config_c[config_key][4])
- revision = int(config_c[config_key][5])
-
- dotty_info[info_key] = f'{major}.{minor}.{revision}'
-
- else:
- dotty_info[info_key] = config_c[config_key]
+ dotty_info[info_key] = _config_to_json(key_type, config_c[config_key])
except Exception as e:
_log_warning(info_data, f'{config_key}->{info_key}: {e}')
@@ -506,6 +533,8 @@ def _extract_config_h(info_data, config_c):
_extract_split_main(info_data, config_c)
_extract_split_transport(info_data, config_c)
_extract_split_right_pins(info_data, config_c)
+ _extract_encoders(info_data, config_c)
+ _extract_split_encoders(info_data, config_c)
_extract_device_version(info_data)
return info_data
@@ -547,40 +576,23 @@ def _extract_rules_mk(info_data, rules):
key_type = info_dict.get('value_type', 'raw')
try:
+ replace_with = info_dict.get('replace_with')
+ if rules_key in rules and info_dict.get('invalid', False):
+ if replace_with:
+ _log_error(info_data, '%s in rules.mk is no longer a valid option and should be replaced with %s' % (rules_key, replace_with))
+ else:
+ _log_error(info_data, '%s in rules.mk is no longer a valid option and should be removed' % rules_key)
+ elif rules_key in rules and info_dict.get('deprecated', False):
+ if replace_with:
+ _log_warning(info_data, '%s in rules.mk is deprecated in favor of %s and will be removed at a later date' % (rules_key, replace_with))
+ else:
+ _log_warning(info_data, '%s in rules.mk is deprecated and will be removed at a later date' % rules_key)
+
if rules_key in rules and info_dict.get('to_json', True):
if dotty_info.get(info_key) and info_dict.get('warn_duplicate', True):
_log_warning(info_data, '%s in rules.mk is overwriting %s in info.json' % (rules_key, info_key))
- if key_type.startswith('array'):
- if '.' in key_type:
- key_type, array_type = key_type.split('.', 1)
- else:
- array_type = None
-
- rules_value = rules[rules_key].replace('{', '').replace('}', '').strip()
-
- if array_type == 'int':
- dotty_info[info_key] = list(map(int, rules_value.split(',')))
- else:
- dotty_info[info_key] = rules_value.split(',')
-
- elif key_type == 'list':
- dotty_info[info_key] = rules[rules_key].split()
-
- elif key_type == 'bool':
- dotty_info[info_key] = rules[rules_key] in true_values
-
- elif key_type == 'hex':
- dotty_info[info_key] = '0x' + rules[rules_key][2:].upper()
-
- elif key_type == 'int':
- dotty_info[info_key] = int(rules[rules_key])
-
- elif key_type == 'str':
- dotty_info[info_key] = rules[rules_key].strip('"')
-
- else:
- dotty_info[info_key] = rules[rules_key]
+ dotty_info[info_key] = _config_to_json(key_type, rules[rules_key])
except Exception as e:
_log_warning(info_data, f'{rules_key}->{info_key}: {e}')
@@ -821,8 +833,11 @@ def merge_info_jsons(keyboard, info_data):
for new_key, existing_key in zip(layout['layout'], info_data['layouts'][layout_name]['layout']):
existing_key.update(new_key)
else:
- layout['c_macro'] = False
- info_data['layouts'][layout_name] = layout
+ if not all('matrix' in key_data.keys() for key_data in layout['layout']):
+ _log_error(info_data, f'Layout "{layout_name}" has no "matrix" definition in either "info.json" or "<keyboard>.h"!')
+ else:
+ layout['c_macro'] = False
+ info_data['layouts'][layout_name] = layout
# Update info_data with the new data
if 'layouts' in new_info_data: