mirror of
				https://github.com/mfulz/qmk_firmware.git
				synced 2025-10-31 05:12:33 +01:00 
			
		
		
		
	Merge pull request #182 from Vifon/modifier-release-fix
Fix the layer-dependent modifiers handling
This commit is contained in:
		
						commit
						153a6fb0d3
					
				
							
								
								
									
										21
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								README.md
									
									
									
									
									
								
							| @ -98,6 +98,27 @@ We've added shortcuts to make common modifier/tap (mod-tap) mappings more compac | |||||||
| 
 | 
 | ||||||
| `DF(layer)` - sets default layer to *layer*. The default layer is the one at the "bottom" of the layer stack - the ultimate fallback layer. This currently does not persist over power loss. When you plug the keyboard back in, layer 0 will always be the default. It is theoretically possible to work around that, but that's not what `DF` does. | `DF(layer)` - sets default layer to *layer*. The default layer is the one at the "bottom" of the layer stack - the ultimate fallback layer. This currently does not persist over power loss. When you plug the keyboard back in, layer 0 will always be the default. It is theoretically possible to work around that, but that's not what `DF` does. | ||||||
| 
 | 
 | ||||||
|  | ### Prevent stuck modifiers | ||||||
|  | 
 | ||||||
|  | Consider the following scenario: | ||||||
|  | 
 | ||||||
|  | 1. Layer 0 has a key defined as Shift. | ||||||
|  | 2. The same key is defined on layer 1 as the letter A. | ||||||
|  | 3. User presses Shift. | ||||||
|  | 4. User switches to layer 1 for whatever reason. | ||||||
|  | 5. User releases Shift, or rather the letter A. | ||||||
|  | 6. User switches back to layer 0. | ||||||
|  | 
 | ||||||
|  | Shift was actually never released and is still considered pressed. | ||||||
|  | 
 | ||||||
|  | If such situation bothers you add this to your `config.h`: | ||||||
|  | 
 | ||||||
|  |     #define PREVENT_STUCK_MODIFIERS | ||||||
|  | 
 | ||||||
|  | This option uses 5 bytes of memory per every 8 keys on the keyboard | ||||||
|  | rounded up (5 bits per key). For example on Planck (48 keys) it uses | ||||||
|  | (48/8)\*5 = 30 bytes. | ||||||
|  | 
 | ||||||
| ### Remember: These are just aliases | ### Remember: These are just aliases | ||||||
| 
 | 
 | ||||||
| These functions work the same way that their `ACTION_*` functions do - they're just quick aliases. To dig into all of the tmk ACTION_* functions, please see the [TMK documentation](https://github.com/jackhumbert/qmk_firmware/blob/master/tmk_core/doc/keymap.md#2-action). | These functions work the same way that their `ACTION_*` functions do - they're just quick aliases. To dig into all of the tmk ACTION_* functions, please see the [TMK documentation](https://github.com/jackhumbert/qmk_firmware/blob/master/tmk_core/doc/keymap.md#2-action). | ||||||
|  | |||||||
| @ -53,6 +53,22 @@ void action_exec(keyevent_t event) | |||||||
| #endif | #endif | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #if !defined(NO_ACTION_LAYER) && defined(PREVENT_STUCK_MODIFIERS) | ||||||
|  | bool disable_action_cache = false; | ||||||
|  | 
 | ||||||
|  | void process_action_nocache(keyrecord_t *record) | ||||||
|  | { | ||||||
|  |     disable_action_cache = true; | ||||||
|  |     process_action(record); | ||||||
|  |     disable_action_cache = false; | ||||||
|  | } | ||||||
|  | #else | ||||||
|  | void process_action_nocache(keyrecord_t *record) | ||||||
|  | { | ||||||
|  |     process_action(record); | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| __attribute__ ((weak)) | __attribute__ ((weak)) | ||||||
| void process_action_kb(keyrecord_t *record) {} | void process_action_kb(keyrecord_t *record) {} | ||||||
| 
 | 
 | ||||||
| @ -67,7 +83,7 @@ void process_action(keyrecord_t *record) | |||||||
| 
 | 
 | ||||||
|     process_action_kb(record); |     process_action_kb(record); | ||||||
| 
 | 
 | ||||||
|     action_t action = layer_switch_get_action(event.key); |     action_t action = store_or_get_action(event.pressed, event.key); | ||||||
|     dprint("ACTION: "); debug_action(action); |     dprint("ACTION: "); debug_action(action); | ||||||
| #ifndef NO_ACTION_LAYER | #ifndef NO_ACTION_LAYER | ||||||
|     dprint(" layer_state: "); layer_debug(); |     dprint(" layer_state: "); layer_debug(); | ||||||
|  | |||||||
| @ -62,6 +62,10 @@ void action_function(keyrecord_t *record, uint8_t id, uint8_t opt); | |||||||
| void process_action_kb(keyrecord_t *record); | void process_action_kb(keyrecord_t *record); | ||||||
| 
 | 
 | ||||||
| /* Utilities for actions.  */ | /* Utilities for actions.  */ | ||||||
|  | #if !defined(NO_ACTION_LAYER) && defined(PREVENT_STUCK_MODIFIERS) | ||||||
|  | extern bool disable_action_cache; | ||||||
|  | #endif | ||||||
|  | void process_action_nocache(keyrecord_t *record); | ||||||
| void process_action(keyrecord_t *record); | void process_action(keyrecord_t *record); | ||||||
| void register_code(uint8_t code); | void register_code(uint8_t code); | ||||||
| void unregister_code(uint8_t code); | void unregister_code(uint8_t code); | ||||||
|  | |||||||
| @ -110,9 +110,71 @@ void layer_debug(void) | |||||||
| } | } | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  | #if !defined(NO_ACTION_LAYER) && defined(PREVENT_STUCK_MODIFIERS) | ||||||
|  | uint8_t source_layers_cache[MAX_LAYER_BITS][(MATRIX_ROWS * MATRIX_COLS + 7) / 8] = {0}; | ||||||
|  | 
 | ||||||
|  | void update_source_layers_cache(keypos_t key, uint8_t layer) | ||||||
|  | { | ||||||
|  |     const uint8_t key_number = key.col + (key.row * MATRIX_COLS); | ||||||
|  |     const uint8_t storage_row = key_number / 8; | ||||||
|  |     const uint8_t storage_bit = key_number % 8; | ||||||
|  | 
 | ||||||
|  |     for (uint8_t bit_number = 0; bit_number < MAX_LAYER_BITS; bit_number++) { | ||||||
|  |         source_layers_cache[bit_number][storage_row] ^= | ||||||
|  |             (-((layer & (1U << bit_number)) != 0) | ||||||
|  |              ^ source_layers_cache[bit_number][storage_row]) | ||||||
|  |             & (1U << storage_bit); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint8_t read_source_layers_cache(keypos_t key) | ||||||
|  | { | ||||||
|  |     const uint8_t key_number = key.col + (key.row * MATRIX_COLS); | ||||||
|  |     const uint8_t storage_row = key_number / 8; | ||||||
|  |     const uint8_t storage_bit = key_number % 8; | ||||||
|  |     uint8_t layer = 0; | ||||||
|  | 
 | ||||||
|  |     for (uint8_t bit_number = 0; bit_number < MAX_LAYER_BITS; bit_number++) { | ||||||
|  |         layer |= | ||||||
|  |             ((source_layers_cache[bit_number][storage_row] | ||||||
|  |               & (1U << storage_bit)) != 0) | ||||||
|  |             << bit_number; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return layer; | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Make sure the action triggered when the key is released is the same | ||||||
|  |  * one as the one triggered on press. It's important for the mod keys | ||||||
|  |  * when the layer is switched after the down event but before the up | ||||||
|  |  * event as they may get stuck otherwise. | ||||||
|  |  */ | ||||||
|  | action_t store_or_get_action(bool pressed, keypos_t key) | ||||||
|  | { | ||||||
|  | #if !defined(NO_ACTION_LAYER) && defined(PREVENT_STUCK_MODIFIERS) | ||||||
|  |     if (disable_action_cache) { | ||||||
|  |         return layer_switch_get_action(key); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     uint8_t layer; | ||||||
|  | 
 | ||||||
|  |     if (pressed) { | ||||||
|  |         layer = layer_switch_get_layer(key); | ||||||
|  |         update_source_layers_cache(key, layer); | ||||||
|  |     } | ||||||
|  |     else { | ||||||
|  |         layer = read_source_layers_cache(key); | ||||||
|  |     } | ||||||
|  |     return action_for_key(layer, key); | ||||||
|  | #else | ||||||
|  |     return layer_switch_get_action(key); | ||||||
|  | #endif | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| action_t layer_switch_get_action(keypos_t key) | int8_t layer_switch_get_layer(keypos_t key) | ||||||
| { | { | ||||||
|     action_t action; |     action_t action; | ||||||
|     action.code = ACTION_TRANSPARENT; |     action.code = ACTION_TRANSPARENT; | ||||||
| @ -124,15 +186,18 @@ action_t layer_switch_get_action(keypos_t key) | |||||||
|         if (layers & (1UL<<i)) { |         if (layers & (1UL<<i)) { | ||||||
|             action = action_for_key(i, key); |             action = action_for_key(i, key); | ||||||
|             if (action.code != ACTION_TRANSPARENT) { |             if (action.code != ACTION_TRANSPARENT) { | ||||||
|                 return action; |                 return i; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     /* fall back to layer 0 */ |     /* fall back to layer 0 */ | ||||||
|     action = action_for_key(0, key); |     return 0; | ||||||
|     return action; |  | ||||||
| #else | #else | ||||||
|     action = action_for_key(biton32(default_layer_state), key); |     return biton32(default_layer_state); | ||||||
|     return action; |  | ||||||
| #endif | #endif | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | action_t layer_switch_get_action(keypos_t key) | ||||||
|  | { | ||||||
|  |     return action_for_key(layer_switch_get_layer(key), key); | ||||||
|  | } | ||||||
|  | |||||||
| @ -70,6 +70,17 @@ void layer_xor(uint32_t state); | |||||||
| #define layer_debug() | #define layer_debug() | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  | /* pressed actions cache */ | ||||||
|  | #if !defined(NO_ACTION_LAYER) && defined(PREVENT_STUCK_MODIFIERS) | ||||||
|  | /* The number of bits needed to represent the layer number: log2(32). */ | ||||||
|  | #define MAX_LAYER_BITS 5 | ||||||
|  | void update_source_layers_cache(keypos_t key, uint8_t layer); | ||||||
|  | uint8_t read_source_layers_cache(keypos_t key); | ||||||
|  | #endif | ||||||
|  | action_t store_or_get_action(bool pressed, keypos_t key); | ||||||
|  | 
 | ||||||
|  | /* return the topmost non-transparent layer currently associated with key */ | ||||||
|  | int8_t layer_switch_get_layer(keypos_t key); | ||||||
| 
 | 
 | ||||||
| /* return action depending on current layer status */ | /* return action depending on current layer status */ | ||||||
| action_t layer_switch_get_action(keypos_t key); | action_t layer_switch_get_action(keypos_t key); | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Erez Zukerman
						Erez Zukerman