From cf58a8733d35bbfea2f54bd0175747ee63d9b2e3 Mon Sep 17 00:00:00 2001 From: Joel Challis Date: Fri, 12 Sep 2025 10:23:23 +0100 Subject: Add DIP Switch map support to keymap.json (#25431) --- lib/python/qmk/keymap.py | 20 ++++++++++++++++++++ lib/python/qmk/tests/test_cli_commands.py | 2 ++ lib/python/qmk/tests/test_qmk_keymap.py | 1 + 3 files changed, 23 insertions(+) (limited to 'lib/python/qmk') diff --git a/lib/python/qmk/keymap.py b/lib/python/qmk/keymap.py index 4cf07f59d6..0ac04f6f73 100644 --- a/lib/python/qmk/keymap.py +++ b/lib/python/qmk/keymap.py @@ -32,6 +32,7 @@ __INCLUDES__ __KEYMAP_GOES_HERE__ __ENCODER_MAP_GOES_HERE__ +__DIP_SWITCH_MAP_GOES_HERE__ __MACRO_OUTPUT_GOES_HERE__ #ifdef OTHER_KEYMAP_C @@ -66,6 +67,19 @@ def _generate_encodermap_table(keymap_json): return lines +def _generate_dipswitchmap_table(keymap_json): + lines = [ + '#if defined(DIP_SWITCH_ENABLE) && defined(DIP_SWITCH_MAP_ENABLE)', + 'const uint16_t PROGMEM dip_switch_map[NUM_DIP_SWITCHES][NUM_DIP_STATES] = {', + ] + for index, switch in enumerate(keymap_json['dip_switches']): + if index != 0: + lines[-1] = lines[-1] + ',' + lines.append(f' DIP_SWITCH_OFF_ON({_strip_any(switch["off"])}, {_strip_any(switch["on"])})') + lines.extend(['};', '#endif // defined(DIP_SWITCH_ENABLE) && defined(DIP_SWITCH_MAP_ENABLE)']) + return lines + + def _generate_macros_function(keymap_json): macro_txt = [ 'bool process_record_user(uint16_t keycode, keyrecord_t *record) {', @@ -286,6 +300,12 @@ def generate_c(keymap_json): encodermap = '\n'.join(encoder_txt) new_keymap = new_keymap.replace('__ENCODER_MAP_GOES_HERE__', encodermap) + dipswitchmap = '' + if 'dip_switches' in keymap_json and keymap_json['dip_switches'] is not None: + dip_txt = _generate_dipswitchmap_table(keymap_json) + dipswitchmap = '\n'.join(dip_txt) + new_keymap = new_keymap.replace('__DIP_SWITCH_MAP_GOES_HERE__', dipswitchmap) + macros = '' if 'macros' in keymap_json and keymap_json['macros'] is not None: macro_txt = _generate_macros_function(keymap_json) diff --git a/lib/python/qmk/tests/test_cli_commands.py b/lib/python/qmk/tests/test_cli_commands.py index dd659fe0f2..2716459989 100644 --- a/lib/python/qmk/tests/test_cli_commands.py +++ b/lib/python/qmk/tests/test_cli_commands.py @@ -159,6 +159,7 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { + #ifdef OTHER_KEYMAP_C # include OTHER_KEYMAP_C #endif // OTHER_KEYMAP_C @@ -196,6 +197,7 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { + #ifdef OTHER_KEYMAP_C # include OTHER_KEYMAP_C #endif // OTHER_KEYMAP_C diff --git a/lib/python/qmk/tests/test_qmk_keymap.py b/lib/python/qmk/tests/test_qmk_keymap.py index 80cc679b00..34360d3b6d 100644 --- a/lib/python/qmk/tests/test_qmk_keymap.py +++ b/lib/python/qmk/tests/test_qmk_keymap.py @@ -27,6 +27,7 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { + #ifdef OTHER_KEYMAP_C # include OTHER_KEYMAP_C #endif // OTHER_KEYMAP_C -- cgit v1.2.3 From d34cade5abe11da93a6f3d204b00278127299f8b Mon Sep 17 00:00:00 2001 From: Joel Challis Date: Fri, 12 Sep 2025 13:21:49 +0100 Subject: Generate default encoder resolution for sparse config (#25247) --- lib/python/qmk/cli/generate/config_h.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'lib/python/qmk') diff --git a/lib/python/qmk/cli/generate/config_h.py b/lib/python/qmk/cli/generate/config_h.py index d6d0299291..1ade452f95 100755 --- a/lib/python/qmk/cli/generate/config_h.py +++ b/lib/python/qmk/cli/generate/config_h.py @@ -125,11 +125,12 @@ def generate_encoder_config(encoder_json, config_h_lines, postfix=''): config_h_lines.append(generate_define(f'ENCODER_A_PINS{postfix}', f'{{ {", ".join(a_pads)} }}')) config_h_lines.append(generate_define(f'ENCODER_B_PINS{postfix}', f'{{ {", ".join(b_pads)} }}')) - if None in resolutions: - cli.log.debug(f"Unable to generate ENCODER_RESOLUTION{postfix} configuration") - elif len(resolutions) == 0: + if len(resolutions) == 0 or all(r is None for r in resolutions): cli.log.debug(f"Skipping ENCODER_RESOLUTION{postfix} configuration") - elif len(set(resolutions)) == 1: + return + + resolutions = [4 if r is None else r for r in resolutions] + if len(set(resolutions)) == 1: config_h_lines.append(generate_define(f'ENCODER_RESOLUTION{postfix}', resolutions[0])) else: config_h_lines.append(generate_define(f'ENCODER_RESOLUTIONS{postfix}', f'{{ {", ".join(map(str,resolutions))} }}')) -- cgit v1.2.3 From 883465d9fb29cd793684ee0ac688ff3517cf8bc0 Mon Sep 17 00:00:00 2001 From: Joel Challis Date: Mon, 22 Sep 2025 00:01:23 +0100 Subject: Add generic handling to cycle LED/RGB Matrix flags (#24649) --- data/constants/keycodes/keycodes_0.0.8.hjson | 0 .../keycodes/keycodes_0.0.8_lighting.hjson | 37 ++++++++++++++++ data/mappings/info_config.hjson | 2 + data/schemas/keyboard.jsonschema | 10 +++++ docs/reference_info_json.md | 6 +++ lib/python/qmk/info.py | 8 ++++ quantum/keycodes.h | 22 +++++++--- quantum/led_matrix/led_matrix.c | 51 ++++++++++++++++++++++ quantum/led_matrix/led_matrix.h | 4 ++ quantum/process_keycode/process_led_matrix.c | 6 +++ quantum/process_keycode/process_rgb_matrix.c | 14 ++++++ quantum/rgb_matrix/rgb_matrix.c | 51 ++++++++++++++++++++++ quantum/rgb_matrix/rgb_matrix.h | 4 ++ 13 files changed, 208 insertions(+), 7 deletions(-) create mode 100644 data/constants/keycodes/keycodes_0.0.8.hjson create mode 100644 data/constants/keycodes/keycodes_0.0.8_lighting.hjson (limited to 'lib/python/qmk') diff --git a/data/constants/keycodes/keycodes_0.0.8.hjson b/data/constants/keycodes/keycodes_0.0.8.hjson new file mode 100644 index 0000000000..e69de29bb2 diff --git a/data/constants/keycodes/keycodes_0.0.8_lighting.hjson b/data/constants/keycodes/keycodes_0.0.8_lighting.hjson new file mode 100644 index 0000000000..fa7227235e --- /dev/null +++ b/data/constants/keycodes/keycodes_0.0.8_lighting.hjson @@ -0,0 +1,37 @@ +{ + "keycodes": { + "0x7819": { + "group": "led_matrix", + "key": "QK_LED_MATRIX_FLAG_NEXT", + "label": "LED Matrix Flag Next", + "aliases": [ + "LM_FLGN" + ] + }, + "0x781A": { + "group": "led_matrix", + "key": "QK_LED_MATRIX_FLAG_PREVIOUS", + "label": "LED Matrix Flag Previous", + "aliases": [ + "LM_FLGP" + ] + }, + + "0x784D": { + "group": "rgb_matrix", + "key": "QK_RGB_MATRIX_FLAG_NEXT", + "label": "RGB Matrix Flag Next", + "aliases": [ + "RM_FLGN" + ] + }, + "0x784E": { + "group": "rgb_matrix", + "key": "QK_RGB_MATRIX_FLAG_PREVIOUS", + "label": "RGB Matrix Flag Previous", + "aliases": [ + "RM_FLGP" + ] + } + } +} diff --git a/data/mappings/info_config.hjson b/data/mappings/info_config.hjson index 4c53aa4339..c4052c64f6 100644 --- a/data/mappings/info_config.hjson +++ b/data/mappings/info_config.hjson @@ -98,6 +98,7 @@ // LED Matrix "LED_MATRIX_CENTER": {"info_key": "led_matrix.center_point", "value_type": "array.int"}, + "LED_MATRIX_FLAG_STEPS": {"info_key": "led_matrix.flag_steps", "value_type": "array.int"}, "LED_MATRIX_KEYRELEASES": {"info_key": "led_matrix.react_on_keyup", "value_type": "flag"}, "LED_MATRIX_LED_FLUSH_LIMIT": {"info_key": "led_matrix.led_flush_limit", "value_type": "int"}, "LED_MATRIX_LED_PROCESS_LIMIT": {"info_key": "led_matrix.led_process_limit", "value_type": "int", "to_json": false}, @@ -147,6 +148,7 @@ // RGB Matrix "RGB_MATRIX_CENTER": {"info_key": "rgb_matrix.center_point", "value_type": "array.int"}, + "RGB_MATRIX_FLAG_STEPS": {"info_key": "rgb_matrix.flag_steps", "value_type": "array.int"}, "RGB_MATRIX_HUE_STEP": {"info_key": "rgb_matrix.hue_steps", "value_type": "int"}, "RGB_MATRIX_KEYRELEASES": {"info_key": "rgb_matrix.react_on_keyup", "value_type": "flag"}, "RGB_MATRIX_LED_FLUSH_LIMIT": {"info_key": "rgb_matrix.led_flush_limit", "value_type": "int"}, diff --git a/data/schemas/keyboard.jsonschema b/data/schemas/keyboard.jsonschema index 93b1c82b1c..93fc4ed8a2 100644 --- a/data/schemas/keyboard.jsonschema +++ b/data/schemas/keyboard.jsonschema @@ -571,6 +571,11 @@ "maxItems": 2, "items": {"$ref": "./definitions.jsonschema#/unsigned_int_8"} }, + "flag_steps": { + "type": "array", + "minItems": 1, + "items": {"$ref": "./definitions.jsonschema#/unsigned_int_8"} + }, "max_brightness": {"$ref": "./definitions.jsonschema#/unsigned_int_8"}, "timeout": {"$ref": "./definitions.jsonschema#/unsigned_int"}, "val_steps": {"$ref": "./definitions.jsonschema#/unsigned_int"}, @@ -656,6 +661,11 @@ "maxItems": 2, "items": {"$ref": "./definitions.jsonschema#/unsigned_int_8"} }, + "flag_steps": { + "type": "array", + "minItems": 1, + "items": {"$ref": "./definitions.jsonschema#/unsigned_int_8"} + }, "max_brightness": {"$ref": "./definitions.jsonschema#/unsigned_int_8"}, "timeout": {"$ref": "./definitions.jsonschema#/unsigned_int"}, "hue_steps": {"$ref": "./definitions.jsonschema#/unsigned_int"}, diff --git a/docs/reference_info_json.md b/docs/reference_info_json.md index 84377ef36c..e7cb8c31e8 100644 --- a/docs/reference_info_json.md +++ b/docs/reference_info_json.md @@ -415,6 +415,9 @@ Configures the [LED Matrix](features/led_matrix) feature. * `center_point` Array: Number * The centroid (geometric center) of the LEDs. Used for certain effects. * Default: `[112, 32]` + * `flag_steps` Array: Number + * A list of flag bitfields that can be cycled through. + * Default: `[255, 5, 0]` * `default` * `animation` String * The default effect. Must be one of `led_matrix.animations` @@ -660,6 +663,9 @@ Configures the [RGB Matrix](features/rgb_matrix) feature. * `center_point` Array: Number * The centroid (geometric center) of the LEDs. Used for certain effects. * Default: `[112, 32]` + * `flag_steps` Array: Number + * A list of flag bitfields that can be cycled through. + * Default: `[255, 5, 2, 0]` * `default` * `animation` String * The default effect. Must be one of `rgb_matrix.animations` diff --git a/lib/python/qmk/info.py b/lib/python/qmk/info.py index f63228b2bc..59a64095e7 100644 --- a/lib/python/qmk/info.py +++ b/lib/python/qmk/info.py @@ -800,6 +800,14 @@ def _extract_led_config(info_data, keyboard): if info_data[feature].get('layout', None) and not info_data[feature].get('led_count', None): info_data[feature]['led_count'] = len(info_data[feature]['layout']) + if info_data[feature].get('layout', None) and not info_data[feature].get('flag_steps', None): + flags = {0xFF, 0} + # if only a single flag is used, assume only all+none flags + unique_flags = set(x.get('flags', 0) for x in info_data[feature]['layout']) + if len(unique_flags) > 1: + flags.update(unique_flags) + info_data[feature]['flag_steps'] = sorted(list(flags), reverse=True) + return info_data diff --git a/quantum/keycodes.h b/quantum/keycodes.h index 6a59aa376d..e5a64d9a71 100644 --- a/quantum/keycodes.h +++ b/quantum/keycodes.h @@ -26,11 +26,11 @@ #pragma once // clang-format off -#define QMK_KEYCODES_VERSION "0.0.7" -#define QMK_KEYCODES_VERSION_BCD 0x00000007 +#define QMK_KEYCODES_VERSION "0.0.8" +#define QMK_KEYCODES_VERSION_BCD 0x00000008 #define QMK_KEYCODES_VERSION_MAJOR 0 #define QMK_KEYCODES_VERSION_MINOR 0 -#define QMK_KEYCODES_VERSION_PATCH 7 +#define QMK_KEYCODES_VERSION_PATCH 8 enum qk_keycode_ranges { // Ranges @@ -663,6 +663,8 @@ enum qk_keycode_defines { QK_LED_MATRIX_BRIGHTNESS_DOWN = 0x7816, QK_LED_MATRIX_SPEED_UP = 0x7817, QK_LED_MATRIX_SPEED_DOWN = 0x7818, + QK_LED_MATRIX_FLAG_NEXT = 0x7819, + QK_LED_MATRIX_FLAG_PREVIOUS = 0x781A, QK_UNDERGLOW_TOGGLE = 0x7820, QK_UNDERGLOW_MODE_NEXT = 0x7821, QK_UNDERGLOW_MODE_PREVIOUS = 0x7822, @@ -697,6 +699,8 @@ enum qk_keycode_defines { QK_RGB_MATRIX_VALUE_DOWN = 0x784A, QK_RGB_MATRIX_SPEED_UP = 0x784B, QK_RGB_MATRIX_SPEED_DOWN = 0x784C, + QK_RGB_MATRIX_FLAG_NEXT = 0x784D, + QK_RGB_MATRIX_FLAG_PREVIOUS = 0x784E, QK_BOOTLOADER = 0x7C00, QK_REBOOT = 0x7C01, QK_DEBUG_TOGGLE = 0x7C02, @@ -1352,6 +1356,8 @@ enum qk_keycode_defines { LM_BRID = QK_LED_MATRIX_BRIGHTNESS_DOWN, LM_SPDU = QK_LED_MATRIX_SPEED_UP, LM_SPDD = QK_LED_MATRIX_SPEED_DOWN, + LM_FLGN = QK_LED_MATRIX_FLAG_NEXT, + LM_FLGP = QK_LED_MATRIX_FLAG_PREVIOUS, UG_TOGG = QK_UNDERGLOW_TOGGLE, UG_NEXT = QK_UNDERGLOW_MODE_NEXT, UG_PREV = QK_UNDERGLOW_MODE_PREVIOUS, @@ -1386,6 +1392,8 @@ enum qk_keycode_defines { RM_VALD = QK_RGB_MATRIX_VALUE_DOWN, RM_SPDU = QK_RGB_MATRIX_SPEED_UP, RM_SPDD = QK_RGB_MATRIX_SPEED_DOWN, + RM_FLGN = QK_RGB_MATRIX_FLAG_NEXT, + RM_FLGP = QK_RGB_MATRIX_FLAG_PREVIOUS, QK_BOOT = QK_BOOTLOADER, QK_RBT = QK_REBOOT, DB_TOGG = QK_DEBUG_TOGGLE, @@ -1511,10 +1519,10 @@ enum qk_keycode_defines { #define IS_MACRO_KEYCODE(code) ((code) >= QK_MACRO_0 && (code) <= QK_MACRO_31) #define IS_CONNECTION_KEYCODE(code) ((code) >= QK_OUTPUT_AUTO && (code) <= QK_BLUETOOTH_PROFILE5) #define IS_BACKLIGHT_KEYCODE(code) ((code) >= QK_BACKLIGHT_ON && (code) <= QK_BACKLIGHT_TOGGLE_BREATHING) -#define IS_LED_MATRIX_KEYCODE(code) ((code) >= QK_LED_MATRIX_ON && (code) <= QK_LED_MATRIX_SPEED_DOWN) +#define IS_LED_MATRIX_KEYCODE(code) ((code) >= QK_LED_MATRIX_ON && (code) <= QK_LED_MATRIX_FLAG_PREVIOUS) #define IS_UNDERGLOW_KEYCODE(code) ((code) >= QK_UNDERGLOW_TOGGLE && (code) <= QK_UNDERGLOW_SPEED_DOWN) #define IS_RGB_KEYCODE(code) ((code) >= RGB_MODE_PLAIN && (code) <= RGB_MODE_TWINKLE) -#define IS_RGB_MATRIX_KEYCODE(code) ((code) >= QK_RGB_MATRIX_ON && (code) <= QK_RGB_MATRIX_SPEED_DOWN) +#define IS_RGB_MATRIX_KEYCODE(code) ((code) >= QK_RGB_MATRIX_ON && (code) <= QK_RGB_MATRIX_FLAG_PREVIOUS) #define IS_QUANTUM_KEYCODE(code) ((code) >= QK_BOOTLOADER && (code) <= QK_LAYER_LOCK) #define IS_KB_KEYCODE(code) ((code) >= QK_KB_0 && (code) <= QK_KB_31) #define IS_USER_KEYCODE(code) ((code) >= QK_USER_0 && (code) <= QK_USER_31) @@ -1537,10 +1545,10 @@ enum qk_keycode_defines { #define MACRO_KEYCODE_RANGE QK_MACRO_0 ... QK_MACRO_31 #define CONNECTION_KEYCODE_RANGE QK_OUTPUT_AUTO ... QK_BLUETOOTH_PROFILE5 #define BACKLIGHT_KEYCODE_RANGE QK_BACKLIGHT_ON ... QK_BACKLIGHT_TOGGLE_BREATHING -#define LED_MATRIX_KEYCODE_RANGE QK_LED_MATRIX_ON ... QK_LED_MATRIX_SPEED_DOWN +#define LED_MATRIX_KEYCODE_RANGE QK_LED_MATRIX_ON ... QK_LED_MATRIX_FLAG_PREVIOUS #define UNDERGLOW_KEYCODE_RANGE QK_UNDERGLOW_TOGGLE ... QK_UNDERGLOW_SPEED_DOWN #define RGB_KEYCODE_RANGE RGB_MODE_PLAIN ... RGB_MODE_TWINKLE -#define RGB_MATRIX_KEYCODE_RANGE QK_RGB_MATRIX_ON ... QK_RGB_MATRIX_SPEED_DOWN +#define RGB_MATRIX_KEYCODE_RANGE QK_RGB_MATRIX_ON ... QK_RGB_MATRIX_FLAG_PREVIOUS #define QUANTUM_KEYCODE_RANGE QK_BOOTLOADER ... QK_LAYER_LOCK #define KB_KEYCODE_RANGE QK_KB_0 ... QK_KB_31 #define USER_KEYCODE_RANGE QK_USER_0 ... QK_USER_31 diff --git a/quantum/led_matrix/led_matrix.c b/quantum/led_matrix/led_matrix.c index 9c8004cc17..b2665597df 100644 --- a/quantum/led_matrix/led_matrix.c +++ b/quantum/led_matrix/led_matrix.c @@ -70,6 +70,13 @@ uint8_t g_led_frame_buffer[MATRIX_ROWS][MATRIX_COLS] = {{0}}; last_hit_t g_last_hit_tracker; #endif // LED_MATRIX_KEYREACTIVE_ENABLED +#ifndef LED_MATRIX_FLAG_STEPS +# define LED_MATRIX_FLAG_STEPS \ + { LED_FLAG_ALL, LED_FLAG_KEYLIGHT | LED_FLAG_MODIFIER, LED_FLAG_NONE } +#endif +static const uint8_t led_matrix_flag_steps[] = LED_MATRIX_FLAG_STEPS; +#define LED_MATRIX_FLAG_STEPS_COUNT ARRAY_SIZE(led_matrix_flag_steps) + // internals static bool suspend_state = false; static uint8_t led_last_enable = UINT8_MAX; @@ -661,6 +668,50 @@ void led_matrix_set_flags_noeeprom(led_flags_t flags) { led_matrix_set_flags_eeprom_helper(flags, false); } +void led_matrix_flags_step_helper(bool write_to_eeprom) { + led_flags_t flags = led_matrix_get_flags(); + + uint8_t next = 0; + for (uint8_t i = 0; i < LED_MATRIX_FLAG_STEPS_COUNT; i++) { + if (led_matrix_flag_steps[i] == flags) { + next = i == LED_MATRIX_FLAG_STEPS_COUNT - 1 ? 0 : i + 1; + break; + } + } + + led_matrix_set_flags_eeprom_helper(led_matrix_flag_steps[next], write_to_eeprom); +} + +void led_matrix_flags_step_noeeprom(void) { + led_matrix_flags_step_helper(false); +} + +void led_matrix_flags_step(void) { + led_matrix_flags_step_helper(true); +} + +void led_matrix_flags_step_reverse_helper(bool write_to_eeprom) { + led_flags_t flags = led_matrix_get_flags(); + + uint8_t next = 0; + for (uint8_t i = 0; i < LED_MATRIX_FLAG_STEPS_COUNT; i++) { + if (led_matrix_flag_steps[i] == flags) { + next = i == 0 ? LED_MATRIX_FLAG_STEPS_COUNT - 1 : i - 1; + break; + } + } + + led_matrix_set_flags_eeprom_helper(led_matrix_flag_steps[next], write_to_eeprom); +} + +void led_matrix_flags_step_reverse_noeeprom(void) { + led_matrix_flags_step_reverse_helper(false); +} + +void led_matrix_flags_step_reverse(void) { + led_matrix_flags_step_reverse_helper(true); +} + // LED Matrix naming #undef LED_MATRIX_EFFECT #ifdef LED_MATRIX_MODE_NAME_ENABLE diff --git a/quantum/led_matrix/led_matrix.h b/quantum/led_matrix/led_matrix.h index 9a49515ab2..f484c700f4 100644 --- a/quantum/led_matrix/led_matrix.h +++ b/quantum/led_matrix/led_matrix.h @@ -183,6 +183,10 @@ void led_matrix_decrease_speed_noeeprom(void); led_flags_t led_matrix_get_flags(void); void led_matrix_set_flags(led_flags_t flags); void led_matrix_set_flags_noeeprom(led_flags_t flags); +void led_matrix_flags_step_noeeprom(void); +void led_matrix_flags_step(void); +void led_matrix_flags_step_reverse_noeeprom(void); +void led_matrix_flags_step_reverse(void); #ifdef LED_MATRIX_MODE_NAME_ENABLE const char *led_matrix_get_mode_name(uint8_t mode); diff --git a/quantum/process_keycode/process_led_matrix.c b/quantum/process_keycode/process_led_matrix.c index 7f95bf1011..3342b33b92 100644 --- a/quantum/process_keycode/process_led_matrix.c +++ b/quantum/process_keycode/process_led_matrix.c @@ -40,6 +40,12 @@ bool process_led_matrix(uint16_t keycode, keyrecord_t *record) { case QK_LED_MATRIX_SPEED_DOWN: led_matrix_decrease_speed(); return false; + case QK_LED_MATRIX_FLAG_NEXT: + led_matrix_flags_step(); + return false; + case QK_LED_MATRIX_FLAG_PREVIOUS: + led_matrix_flags_step_reverse(); + return false; } } diff --git a/quantum/process_keycode/process_rgb_matrix.c b/quantum/process_keycode/process_rgb_matrix.c index fd2aa1a0c7..c18212294d 100644 --- a/quantum/process_keycode/process_rgb_matrix.c +++ b/quantum/process_keycode/process_rgb_matrix.c @@ -94,6 +94,20 @@ bool process_rgb_matrix(uint16_t keycode, keyrecord_t *record) { rgb_matrix_decrease_speed(); } return false; + case QK_RGB_MATRIX_FLAG_NEXT: + if (shifted) { + rgb_matrix_flags_step_reverse(); + } else { + rgb_matrix_flags_step(); + } + return false; + case QK_RGB_MATRIX_FLAG_PREVIOUS: + if (shifted) { + rgb_matrix_flags_step(); + } else { + rgb_matrix_flags_step_reverse(); + } + return false; } } diff --git a/quantum/rgb_matrix/rgb_matrix.c b/quantum/rgb_matrix/rgb_matrix.c index ab0aa17512..19edfb52b0 100644 --- a/quantum/rgb_matrix/rgb_matrix.c +++ b/quantum/rgb_matrix/rgb_matrix.c @@ -72,6 +72,13 @@ uint8_t g_rgb_frame_buffer[MATRIX_ROWS][MATRIX_COLS] = {{0}}; last_hit_t g_last_hit_tracker; #endif // RGB_MATRIX_KEYREACTIVE_ENABLED +#ifndef RGB_MATRIX_FLAG_STEPS +# define RGB_MATRIX_FLAG_STEPS \ + { LED_FLAG_ALL, LED_FLAG_KEYLIGHT | LED_FLAG_MODIFIER, LED_FLAG_UNDERGLOW, LED_FLAG_NONE } +#endif +static const uint8_t rgb_matrix_flag_steps[] = RGB_MATRIX_FLAG_STEPS; +#define RGB_MATRIX_FLAG_STEPS_COUNT ARRAY_SIZE(rgb_matrix_flag_steps) + // internals static bool suspend_state = false; static uint8_t rgb_last_enable = UINT8_MAX; @@ -747,6 +754,50 @@ void rgb_matrix_set_flags_noeeprom(led_flags_t flags) { rgb_matrix_set_flags_eeprom_helper(flags, false); } +void rgb_matrix_flags_step_helper(bool write_to_eeprom) { + led_flags_t flags = rgb_matrix_get_flags(); + + uint8_t next = 0; + for (uint8_t i = 0; i < RGB_MATRIX_FLAG_STEPS_COUNT; i++) { + if (rgb_matrix_flag_steps[i] == flags) { + next = i == RGB_MATRIX_FLAG_STEPS_COUNT - 1 ? 0 : i + 1; + break; + } + } + + rgb_matrix_set_flags_eeprom_helper(rgb_matrix_flag_steps[next], write_to_eeprom); +} + +void rgb_matrix_flags_step_noeeprom(void) { + rgb_matrix_flags_step_helper(false); +} + +void rgb_matrix_flags_step(void) { + rgb_matrix_flags_step_helper(true); +} + +void rgb_matrix_flags_step_reverse_helper(bool write_to_eeprom) { + led_flags_t flags = rgb_matrix_get_flags(); + + uint8_t next = 0; + for (uint8_t i = 0; i < RGB_MATRIX_FLAG_STEPS_COUNT; i++) { + if (rgb_matrix_flag_steps[i] == flags) { + next = i == 0 ? RGB_MATRIX_FLAG_STEPS_COUNT - 1 : i - 1; + break; + } + } + + rgb_matrix_set_flags_eeprom_helper(rgb_matrix_flag_steps[next], write_to_eeprom); +} + +void rgb_matrix_flags_step_reverse_noeeprom(void) { + rgb_matrix_flags_step_reverse_helper(false); +} + +void rgb_matrix_flags_step_reverse(void) { + rgb_matrix_flags_step_reverse_helper(true); +} + //---------------------------------------------------------- // RGB Matrix naming #undef RGB_MATRIX_EFFECT diff --git a/quantum/rgb_matrix/rgb_matrix.h b/quantum/rgb_matrix/rgb_matrix.h index a91dded4a8..f800679b46 100644 --- a/quantum/rgb_matrix/rgb_matrix.h +++ b/quantum/rgb_matrix/rgb_matrix.h @@ -218,6 +218,10 @@ void rgb_matrix_decrease_speed_noeeprom(void); led_flags_t rgb_matrix_get_flags(void); void rgb_matrix_set_flags(led_flags_t flags); void rgb_matrix_set_flags_noeeprom(led_flags_t flags); +void rgb_matrix_flags_step_noeeprom(void); +void rgb_matrix_flags_step(void); +void rgb_matrix_flags_step_reverse_noeeprom(void); +void rgb_matrix_flags_step_reverse(void); void rgb_matrix_update_pwm_buffers(void); #ifdef RGB_MATRIX_MODE_NAME_ENABLE -- cgit v1.2.3 From 6e35013bc2e30af022cdb8c176e869c5c94ee17a Mon Sep 17 00:00:00 2001 From: Joel Challis Date: Sat, 8 Nov 2025 20:48:48 +0000 Subject: Generate `CUSTOM_MATRIX = lite` without `matrix_pins.custom` (#25453) --- keyboards/argyle/keyboard.json | 1 - keyboards/keychron/c2_pro/info.json | 1 - keyboards/keychron/q1v2/info.json | 1 - lib/python/qmk/cli/generate/rules_mk.py | 9 ++++--- lib/python/qmk/info.py | 42 +++++++++++++++++++++------------ 5 files changed, 31 insertions(+), 23 deletions(-) (limited to 'lib/python/qmk') diff --git a/keyboards/argyle/keyboard.json b/keyboards/argyle/keyboard.json index a7bfc335df..d97f067be8 100755 --- a/keyboards/argyle/keyboard.json +++ b/keyboards/argyle/keyboard.json @@ -11,7 +11,6 @@ }, "matrix_pins": { "cols": ["D1", "D4", "D5", "D6", "D7", "B0", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN"], - "custom": true, "custom_lite": true, "rows": ["D0", "C3", "B1", "B2", "B3"] }, diff --git a/keyboards/keychron/c2_pro/info.json b/keyboards/keychron/c2_pro/info.json index 9c0e1ac72e..8a7d0968b1 100644 --- a/keyboards/keychron/c2_pro/info.json +++ b/keyboards/keychron/c2_pro/info.json @@ -26,7 +26,6 @@ "matrix_pins": { "cols": ["A10", "A9", "A8", "B1", "B0", "A7", "A6", "A5", "A4", "A3", "A2", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "C14"], "rows": ["B5", "B4", "B3", "A15", "A14", "A13"], - "custom": true, "custom_lite": true }, "diode_direction": "ROW2COL" diff --git a/keyboards/keychron/q1v2/info.json b/keyboards/keychron/q1v2/info.json index f0342254fa..ba6f908bbb 100644 --- a/keyboards/keychron/q1v2/info.json +++ b/keyboards/keychron/q1v2/info.json @@ -27,7 +27,6 @@ "matrix_pins": { "cols": ["C14", "C15", "A0", "A1", "A2", "A3", "A4", "A5", null, null, null, null, null, null, null, null], "rows": ["B5", "B4", "B3", "A15", "A14", "A13"], - "custom": true, "custom_lite": true }, "diode_direction": "ROW2COL", diff --git a/lib/python/qmk/cli/generate/rules_mk.py b/lib/python/qmk/cli/generate/rules_mk.py index 358a22fd1d..16084bded1 100755 --- a/lib/python/qmk/cli/generate/rules_mk.py +++ b/lib/python/qmk/cli/generate/rules_mk.py @@ -96,11 +96,10 @@ def generate_rules_mk(cli): rules_mk_lines.append(generate_rule('SPLIT_TRANSPORT', 'custom')) # Set CUSTOM_MATRIX, if needed - if kb_info_json.get('matrix_pins', {}).get('custom'): - if kb_info_json.get('matrix_pins', {}).get('custom_lite'): - rules_mk_lines.append(generate_rule('CUSTOM_MATRIX', 'lite')) - else: - rules_mk_lines.append(generate_rule('CUSTOM_MATRIX', 'yes')) + if kb_info_json.get('matrix_pins', {}).get('custom_lite'): + rules_mk_lines.append(generate_rule('CUSTOM_MATRIX', 'lite')) + elif kb_info_json.get('matrix_pins', {}).get('custom'): + rules_mk_lines.append(generate_rule('CUSTOM_MATRIX', 'yes')) if converter: rules_mk_lines.append(generate_rule('CONVERT_TO', converter)) diff --git a/lib/python/qmk/info.py b/lib/python/qmk/info.py index 59a64095e7..e6d51e1239 100644 --- a/lib/python/qmk/info.py +++ b/lib/python/qmk/info.py @@ -302,6 +302,24 @@ def _extract_features(info_data, rules): return info_data +def _extract_matrix_rules(info_data, rules): + """Find all the features enabled in rules.mk. + """ + if rules.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 rules.mk, the rules.mk values win.') + + if 'matrix_pins' not in info_data: + info_data['matrix_pins'] = {} + + if rules['CUSTOM_MATRIX'] == 'lite': + info_data['matrix_pins']['custom_lite'] = True + else: + info_data['matrix_pins']['custom'] = True + + return info_data + + def _pin_name(pin): """Returns the proper representation for a pin. """ @@ -552,7 +570,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() direct_pins = config_c.get('DIRECT_PINS', '').replace(' ', '')[1:-1] - info_snippet = {} if 'MATRIX_ROWS' in config_c and 'MATRIX_COLS' in config_c: if 'matrix_size' in info_data: @@ -567,26 +584,20 @@ def _extract_matrix_info(info_data, config_c): if 'matrix_pins' in info_data and 'cols' in info_data['matrix_pins'] and 'rows' in info_data['matrix_pins']: _log_warning(info_data, 'Matrix pins are specified in both info.json and config.h, the config.h values win.') - info_snippet['cols'] = _extract_pins(col_pins) - info_snippet['rows'] = _extract_pins(row_pins) + if 'matrix_pins' not in info_data: + info_data['matrix_pins'] = {} + + info_data['matrix_pins']['cols'] = _extract_pins(col_pins) + info_data['matrix_pins']['rows'] = _extract_pins(row_pins) if direct_pins: if 'matrix_pins' in info_data and 'direct' in info_data['matrix_pins']: _log_warning(info_data, 'Direct pins are specified in both info.json and config.h, the config.h values win.') - info_snippet['direct'] = _extract_direct_matrix(direct_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.') - - info_snippet['custom'] = True - - if config_c['CUSTOM_MATRIX'] == 'lite': - info_snippet['custom_lite'] = True + if 'matrix_pins' not in info_data: + info_data['matrix_pins'] = {} - if info_snippet: - info_data['matrix_pins'] = info_snippet + info_data['matrix_pins']['direct'] = _extract_direct_matrix(direct_pins) return info_data @@ -755,6 +766,7 @@ def _extract_rules_mk(info_data, rules): # Merge in config values that can't be easily mapped _extract_features(info_data, rules) + _extract_matrix_rules(info_data, rules) return info_data -- cgit v1.2.3 From 8ec3de0f92219ee783425f406ff188597ec5e8c6 Mon Sep 17 00:00:00 2001 From: Joel Challis Date: Tue, 11 Nov 2025 21:59:06 +0000 Subject: Add return code to `qmk userspace-doctor` (#25775) --- lib/python/qmk/cli/userspace/doctor.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'lib/python/qmk') diff --git a/lib/python/qmk/cli/userspace/doctor.py b/lib/python/qmk/cli/userspace/doctor.py index 2b7e29aa7e..7c016e5a2f 100644 --- a/lib/python/qmk/cli/userspace/doctor.py +++ b/lib/python/qmk/cli/userspace/doctor.py @@ -2,10 +2,12 @@ # SPDX-License-Identifier: GPL-2.0-or-later from milc import cli -from qmk.constants import QMK_FIRMWARE +from qmk.constants import QMK_FIRMWARE, HAS_QMK_USERSPACE from qmk.cli.doctor.main import userspace_tests @cli.subcommand('Checks userspace configuration.') def userspace_doctor(cli): userspace_tests(QMK_FIRMWARE) + + return 0 if HAS_QMK_USERSPACE else 1 -- cgit v1.2.3 From 1a7f544e0df833f9709aaa724f9fd8b5c5c274de Mon Sep 17 00:00:00 2001 From: Jack Sangdahl Date: Fri, 21 Nov 2025 23:57:58 -0700 Subject: [CLI] Lint error on missing keyboard readme (#25814) --- lib/python/qmk/cli/lint.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'lib/python/qmk') diff --git a/lib/python/qmk/cli/lint.py b/lib/python/qmk/cli/lint.py index 484ddb5bd9..8a128ce6d2 100644 --- a/lib/python/qmk/cli/lint.py +++ b/lib/python/qmk/cli/lint.py @@ -304,6 +304,10 @@ def keyboard_check(kb): # noqa C901 cli.log.error(f'{kb}: The file "{file}" should not exist!') ok = False + if not _get_readme_files(kb): + cli.log.error(f'{kb}: Is missing a readme.md file!') + ok = False + for file in _get_readme_files(kb): if _is_invalid_readme(file): cli.log.error(f'{kb}: The file "{file}" still contains template tokens!') -- cgit v1.2.3 From 28a11ff6f7a721820cc335287e44e2fefe22ac71 Mon Sep 17 00:00:00 2001 From: Joel Challis Date: Sun, 23 Nov 2025 05:02:32 +0000 Subject: Fix preference of output file for 'qmk generate-autocorrect-data' (#25818) --- lib/python/qmk/cli/generate/autocorrect_data.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'lib/python/qmk') diff --git a/lib/python/qmk/cli/generate/autocorrect_data.py b/lib/python/qmk/cli/generate/autocorrect_data.py index 01a29b46fe..4f322adce2 100644 --- a/lib/python/qmk/cli/generate/autocorrect_data.py +++ b/lib/python/qmk/cli/generate/autocorrect_data.py @@ -250,8 +250,8 @@ def to_hex(b: int) -> str: @cli.argument('filename', type=normpath, help='The autocorrection database file') -@cli.argument('-kb', '--keyboard', type=keyboard_folder, completer=keyboard_completer, help='The keyboard to build a firmware for. Ignored when a configurator export is supplied.') -@cli.argument('-km', '--keymap', completer=keymap_completer, help='The keymap to build a firmware for. Ignored when a configurator export is supplied.') +@cli.argument('-kb', '--keyboard', type=keyboard_folder, completer=keyboard_completer, help='The keyboard to build a firmware for. Ignored when a output file is supplied.') +@cli.argument('-km', '--keymap', completer=keymap_completer, help='The keymap to build a firmware for. Ignored when a output file is supplied.') @cli.argument('-o', '--output', arg_only=True, type=normpath, help='File to write to') @cli.argument('-q', '--quiet', arg_only=True, action='store_true', help="Quiet mode, only output error messages") @cli.subcommand('Generate the autocorrection data file from a dictionary file.') @@ -263,7 +263,7 @@ def generate_autocorrect_data(cli): current_keyboard = cli.args.keyboard or cli.config.user.keyboard or cli.config.generate_autocorrect_data.keyboard current_keymap = cli.args.keymap or cli.config.user.keymap or cli.config.generate_autocorrect_data.keymap - if current_keyboard and current_keymap: + if not cli.args.output and current_keyboard and current_keymap: cli.args.output = locate_keymap(current_keyboard, current_keymap).parent / 'autocorrect_data.h' assert all(0 <= b <= 255 for b in data) -- cgit v1.2.3 From fd65390496cb47b3164c507656798664b8c2fcd1 Mon Sep 17 00:00:00 2001 From: Xelus22 Date: Sun, 23 Nov 2025 22:21:13 +1100 Subject: [core] add BCD versions of QMK Version (#25804) Co-authored-by: Joel Challis --- lib/python/qmk/cli/generate/version_h.py | 4 ++++ lib/python/qmk/util.py | 10 ++++++++++ 2 files changed, 14 insertions(+) (limited to 'lib/python/qmk') diff --git a/lib/python/qmk/cli/generate/version_h.py b/lib/python/qmk/cli/generate/version_h.py index fd87df3617..8156e85559 100644 --- a/lib/python/qmk/cli/generate/version_h.py +++ b/lib/python/qmk/cli/generate/version_h.py @@ -8,6 +8,7 @@ from qmk.path import normpath from qmk.commands import dump_lines from qmk.git import git_get_qmk_hash, git_get_version, git_is_dirty from qmk.constants import GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE +from qmk.util import triplet_to_bcd TIME_FMT = '%Y-%m-%d-%H:%M:%S' @@ -32,12 +33,14 @@ def generate_version_h(cli): git_dirty = False git_version = "NA" git_qmk_hash = "NA" + git_bcd_version = "0x00000000" chibios_version = "NA" chibios_contrib_version = "NA" else: git_dirty = git_is_dirty() git_version = git_get_version() or current_time git_qmk_hash = git_get_qmk_hash() or "Unknown" + git_bcd_version = triplet_to_bcd(git_version) chibios_version = git_get_version("chibios", "os") or current_time chibios_contrib_version = git_get_version("chibios-contrib", "os") or current_time @@ -48,6 +51,7 @@ def generate_version_h(cli): f""" #define QMK_VERSION "{git_version}" #define QMK_BUILDDATE "{current_time}" +#define QMK_VERSION_BCD {git_bcd_version} #define QMK_GIT_HASH "{git_qmk_hash}{'*' if git_dirty else ''}" #define CHIBIOS_VERSION "{chibios_version}" #define CHIBIOS_CONTRIB_VERSION "{chibios_contrib_version}" diff --git a/lib/python/qmk/util.py b/lib/python/qmk/util.py index 8f99410e1d..6da684a577 100644 --- a/lib/python/qmk/util.py +++ b/lib/python/qmk/util.py @@ -3,9 +3,12 @@ import contextlib import multiprocessing import sys +import re from milc import cli +TRIPLET_PATTERN = re.compile(r'^(\d+)\.(\d+)\.(\d+)') + maybe_exit_should_exit = True maybe_exit_reraise = False @@ -96,3 +99,10 @@ def parallel_map(*args, **kwargs): # before the results are returned. Returning a list ensures results are # materialised before any worker pool is shut down. return list(map_fn(*args, **kwargs)) + + +def triplet_to_bcd(ver: str): + m = TRIPLET_PATTERN.match(ver) + if not m: + return '0x00000000' + return f'0x{int(m.group(1)):02d}{int(m.group(2)):02d}{int(m.group(3)):04d}' -- cgit v1.2.3 From 53de903fb89d4138fdc38f98d266db0fec9548b1 Mon Sep 17 00:00:00 2001 From: Joel Challis Date: Sun, 23 Nov 2025 11:21:55 +0000 Subject: Better defaulting of `{RGB,LED}_MATRIX_DEFAULT_FLAGS` (#25785) --- lib/python/qmk/info.py | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) (limited to 'lib/python/qmk') diff --git a/lib/python/qmk/info.py b/lib/python/qmk/info.py index e6d51e1239..a0b8fe72b6 100644 --- a/lib/python/qmk/info.py +++ b/lib/python/qmk/info.py @@ -5,6 +5,7 @@ import os from pathlib import Path import jsonschema from dotty_dict import dotty +from enum import IntFlag from milc import cli @@ -21,6 +22,15 @@ true_values = ['1', 'on', 'yes'] false_values = ['0', 'off', 'no'] +class LedFlags(IntFlag): + ALL = 0xFF + NONE = 0x00 + MODIFIER = 0x01 + UNDERGLOW = 0x02 + KEYLIGHT = 0x04 + INDICATOR = 0x08 + + def _keyboard_in_layout_name(keyboard, layout): """Validate that a layout macro does not contain name of keyboard """ @@ -813,12 +823,23 @@ def _extract_led_config(info_data, keyboard): info_data[feature]['led_count'] = len(info_data[feature]['layout']) if info_data[feature].get('layout', None) and not info_data[feature].get('flag_steps', None): - flags = {0xFF, 0} + flags = {LedFlags.ALL, LedFlags.NONE} + default_flags = {LedFlags.MODIFIER | LedFlags.KEYLIGHT, LedFlags.UNDERGLOW} + # if only a single flag is used, assume only all+none flags - unique_flags = set(x.get('flags', 0) for x in info_data[feature]['layout']) - if len(unique_flags) > 1: - flags.update(unique_flags) - info_data[feature]['flag_steps'] = sorted(list(flags), reverse=True) + kb_flags = set(x.get('flags', LedFlags.NONE) for x in info_data[feature]['layout']) + if len(kb_flags) > 1: + # check if any part of LED flag is with the defaults + unique_flags = set() + for candidate in default_flags: + if any(candidate & flag for flag in kb_flags): + unique_flags.add(candidate) + + # if we still have a single flag, assume only all+none + if len(unique_flags) > 1: + flags.update(unique_flags) + + info_data[feature]['flag_steps'] = sorted([int(flag) for flag in flags], reverse=True) return info_data -- cgit v1.2.3