Qmk_firmware: Programmatically controlling the modifers

Created on 17 Jul 2017  路  4Comments  路  Source: qmk/qmk_firmware

Currently we have the following different modifiers

  • real_mods or just mods - These are intended to represent the physical modifiers on the keyboard, and not modified manually
  • weak_mods - Can be modified manually to combine with the other modifiers. These are (usually) automatically reset on the next keypress. The exception to this is when the keycode is handled by process_record_quantum, process_record_kb or process_record_user. That's probably not the intention, but that's unfortunately the way it currently works.
  • macro_mods - Used internally by the macro system.

Therefore, if a user want to manually control the modifiers, weak_mods is the only available option today. But that has problems, as mentioned it's reset on the next keypress, which means that you can't really for example toggle the shift key on until something happens. This unfortunate behaviour was introduced in #188. I don't think clearing the mods was the right way to fix the issue, but that's what we have and if we wanted to apply another fix now, it would most likely break some keymaps, so we are stuck with the current behaviour. And even if this is changed, we are still dealing with the fact that the firmware itself modifies the weak modifiers, so user keymaps would have to be aware of that, and possibly also rely on implementation details, which could change.

Therefore, I think we might need a new set of modifiers user_modifiers, which are completely controlled by the keymap. Perhaps the only place they are reset would be during a keyboard suspend, but even that is debatable, maybe the keymap should handle the suspend event and reset them themselves if needed.

Note that I currently don't have a real use case for this, I just thought about it while looking at the code, so I don't think this is an urgent issues. Nevertheless, it's still worth opening the discussion about this.

discussion enhancement

Most helpful comment

I think a situation that can use the programmable control modifier:
That's what I found this issue.

In normal case:
the KC_1 is pressed, 1 will be sent.
But Shift + KC_1 is pressed, ! will be sent.

That's defined somewhere, when we defined in keymap.c:
[BASE] = KEYMAP(
... KC_1, KC_2, ...)

But I want to reverse the case, since a better layout for default is ! , (shift ! is 1)

[BASE] = KEYMAP(
... KC_EXLM, KC_AT, ...)

so in the process_record_user( keycode, record) {
if (record->evend.pressed) {
case KC_1: // never occur , because even press shift-KC_EXLM give KC_EXLM
// return true; // will send '1' instead
break;
case KC_EXLM:
return true; // will send '!' instead
break;
}
}

if a modifier function exists:

so in the new process_record_user( keycode, record) {
if (record->evend.pressed) {
case KC_EXLM:
toggle_the_shift_modifier
return true; // will send '1' if SHIFT-KC_EXLM, and '!' if KC_EXLM
break;
}
}

I hopefully wait for these function to do the reversing.
Or any other way to do the same functionality?
Thanks for attention.

All 4 comments

Some addtions

  • The weak mods are also cleared when the layer changes.
  • register_code16 also has the same limitations, since it uses weak mods internally, unless you send a direct modifier like KC_LSFT. If you look at the code there qk_register_mods, and qk_register_weak_mods are the exactly same function, so one of them should be removed. Note that calling it with KC_LSFT doesn't actually register any mods that way.

The above made me think, we are actually already programmatically modifying the real_mods, so what we actually have

  • real_mods stays the same until the physical key is released or you programmatically change it.
  • weak_mods applied until the next keypress (with some exceptions, most likely not intentionally)

I think it's safe to say, that currently both can be used by keymaps, but by doing so you have to be aware of the fact that the same states are manipulated by the firmware, so you have to rely on implementation details. Also rather than manipulating them directly, you should prefer register_code16, with register_code16(KC_LSFT) it applies a real modifier, and with register_code16(LSFT(KC_NO)) it applies a weak modifier.

I think a situation that can use the programmable control modifier:
That's what I found this issue.

In normal case:
the KC_1 is pressed, 1 will be sent.
But Shift + KC_1 is pressed, ! will be sent.

That's defined somewhere, when we defined in keymap.c:
[BASE] = KEYMAP(
... KC_1, KC_2, ...)

But I want to reverse the case, since a better layout for default is ! , (shift ! is 1)

[BASE] = KEYMAP(
... KC_EXLM, KC_AT, ...)

so in the process_record_user( keycode, record) {
if (record->evend.pressed) {
case KC_1: // never occur , because even press shift-KC_EXLM give KC_EXLM
// return true; // will send '1' instead
break;
case KC_EXLM:
return true; // will send '!' instead
break;
}
}

if a modifier function exists:

so in the new process_record_user( keycode, record) {
if (record->evend.pressed) {
case KC_EXLM:
toggle_the_shift_modifier
return true; // will send '1' if SHIFT-KC_EXLM, and '!' if KC_EXLM
break;
}
}

I hopefully wait for these function to do the reversing.
Or any other way to do the same functionality?
Thanks for attention.

@perlawk I have _a_ solution for this. I use the Workman layout personally and the "programmer" version uses the exact methodology as you have described. Pressing the 1 key without any modifiers prints the "!" by default, and pressing "shift+1" will print "1". Basically reversing the number row to the shifted alternatives by default.

I have a PR #3682 for my solution. I hope you find it useful.

_(NOTE: this solution isn't perfect as it removes all other modifiers when SHIFTing a number)_

3682 was merged - if there are any other complications around implementing this, feel free to reopen this.

Was this page helpful?
0 / 5 - 0 ratings