keychron_qmk_firmware/users/spacebarracecar
SpacebarRacecar 3c95de3c69 Adding a readme to my userspace (#4239)
* added userspace readme

* small fix to userspace readme

* another fix to userspace readme

* changed and improved userspace readme

* Update readme.md

* Update readme.md

* changes to readme again

* readme again
2018-10-25 13:08:25 -07:00
..
config.h Keymap: Added personal userspace and keymaps. (#4073) 2018-10-04 15:00:33 -07:00
readme.md Adding a readme to my userspace (#4239) 2018-10-25 13:08:25 -07:00
rules.mk Keymap: Added personal userspace and keymaps. (#4073) 2018-10-04 15:00:33 -07:00
spacebarracecar.c Keymap: Updates to personal userspace and keymaps (#4206) 2018-10-22 13:23:22 -07:00
spacebarracecar.h Keymap: Updates to personal userspace and keymaps (#4206) 2018-10-22 13:23:22 -07:00

SpacebarRacecar Userspace

The main purpose of this userspace is to keep my personal keymaps clean by moving shared code in here and also allowing me to easily change all my keymaps at once. However it also contains code that might be interesting to QMK Users living in Germany. Most of this code will be explained here.

US Layout Keys for German PCs

I'm using the US Ansi layout however I'm living in Germany which means that every PC here has its input language set to German. My goal was to create custom keycodes that emulate the US Keys so that I can plug my keyboards into any German PC and have them working without changing any settings.

How to use

  • To activate the custom keycodes set GERMAN_ENABLE = yes in the keyboards rules.mk file.
  • The keycodes are listed and explained in spacebarracecar.h under enum userspace_custom_keycodes.
  • The keycodes are handled by the process_record_user function which is now located in spacebarracecar.c. To change keyboard specific configuration process_record_keymap is used (see drashna userspace readme for better explanation).
  • There is a predefined _DEADKEY layer in spacebarracecar.h under enum userspace_layers. Shifted CU_QUOT enables the dead key layer, just like KC_QUOT would when using the US International layout. (See enum userspace_custom_keycodes for more explanation).
  • On Windows grave and circonflexe are defined as dead keys when using the standard german layout. Those are automatically escaped when using the custom keycodes. CU_ESCT can be used to enable/disable this behavior.
  • For a complete example see my fc660c or planck keymaps.

How it works

Creating the custom keycodes was not a trivial task because it is necessary that they are able to send different keycodes depending on the physical shift state. Also the shift state of the actual send keycodes has to be completely independent of the physical shift state. At the same time it has to be ensured that shift never gets stuck or disabled accidentaly.

(Shift state: If shift keycode is registered; Physical shift state: If shift key is actually pressed)

Here is an example for a custom US Equal keycode that illustrates the problem:

Physical Shift State Custom keycode Actual send keycodes
Unshifted = Shifted DE_0
Shifted + Unshifted DE_PLUS

Tracking the physical shift state

To track the physical shift state there are two custom left and right shift keycodes CU_LSFT and CU_RSFT. These track the current physical shift state using two booleans lshift and rshift. To make things easier both custom shifts use KC_LSFT. A space cadet shift functionality is also included. The preexisting space cadet shift implementation could not be used because it doesn't reset when custom keycodes are pressed and therefore produces unwated parenthesis.

Custom keycode templates

To ease the creation of custom keycodes there are a few templates defined in spacebarracecar.h.

  • SHIFT_NORM(kc1, kc2) Sends independent keycodes kc1 and kc2 depending on physical shift state without changing shift state.
  • SHIFT_SWITCH(kc1, kc2) Sends independent keycodes kc1 and kc2 depending on physical shift state while flipping shift state.
  • SHIFT_ALL(kc1, kc2) Sends independent keycodes kc1 and kc2 depending on physical shift state while always sending shift.
  • SHIFT_NO(kc1, kc2) Sends independent keycodes kc1 and kc2 depending on physical shift state while never sending shift.
  • SHIFT_ALGR(kc1, kc2) Sends independent keycodes kc1 and kc2 depending on physical shift state while never sending shift and always sending ralt.
  • UML(kc) Sends keycode kc without changing shift state and then escapes the _DEADKEY layer.

Here is SHIFT_NORM as an example:

#define SHIFT_NORM(kc1, kc2) \
if (record->event.pressed) { \
  timer_timeout(); \
  if (lshift || rshift) { \
    register_code(KC_LSFT); \
    unregister_code(kc2); \
    register_code(kc2); \
    add_to_prev(kc2); \
  } else { \
    unregister_code(KC_LSFT); \
    unregister_code(kc1); \
    register_code(kc1); \
  } \
} else { \
  unregister_code(kc1); \
  unregister_code(kc2); \
} \
return false;

It is noticeable that before registering a keycode the same keycode gets unregistered first. The reason for this is that there can now be two physical keys that use the same keycode only with different shift states. When rolling those two keys the keycode has to be unregistered first, otherwise the second key doesn't register. Also noticable is that sometimes the add_to_prev function is called. This will be explained later. The timer_timeout function is necessary to reset the timers of the custom space cadet shift functionality.

It's worth mentioning that SHIFT_ALGR unlike the other templates doesn't allow repetition of characters when held. This is because it would otherwise require extensive checking of physical ralt status similar to shift to prevent ralt from getting stuck.

Ensuring that shift state is always correct when pressing normal keycodes

To ensure that non custom keycodes always get send with the correct shift state the default case in process_record_user includes a physical shift check that sets shift accordingly before registering the keycode. Also timer_timeout is always called to reset the space cadet shift timers to prevent unwanted parenthesis.

default:
    if(record->event.pressed) {
      timer_timeout();

      #ifdef GERMAN_ENABLE
      if (lshift || rshift)
        register_code(KC_LSFT);
      else
        unregister_code(KC_LSFT);
      #endif

    }
    return process_record_keymap(keycode, record);

Ensuring that custom keycodes always produce the correct character

When very specific key combinations that include custom keycodes and/or shift are pressed it can happen that wrong characters are produced due to changing shift states. The previously mentioned add_to_prev function is there to prevent that. It can be used to add a keycode to the prev_kcs array that can hold up to 6 keycodes. On various occasions, but mainly when shift is released unreg_prev is called, which then unregisters all saved keycodes.

For real use this is probably not needed, but it doesn't hurt either.

Adapting the templates for other languages

In theory the templates can be used to create custom keycodes that map any layout to any other layout.

Other stuff

Custom Nav/Esc Key

Since I'm always using a navigation layer with all my keyboards there is a _NAV layer predefined in spacebarracecar.h under enum userspace_layers. In addition to that I wanted to have a keycode that activates the navigation layer when held, but acts as Escape when pressed. In QMK there already exists LT(layer, kc), however I found that there is some amount of lag before the layer is actived. Therefore I created a custom keycode CU_NAV that does the same without lag using a custom timer. Since I already need timers for the custom space cadet shift implementing this was very easy by adding the timer to the timer_timeout function.

Gamemode

The userspace includes the custom keycode CU_GAME that is used to flip a boolean variable called game. That variable is used to enable/disable windows keys and space cadet shift. In my planck keymap it also makes Lower act like Space which is more comfortable when resting on wasd and it could also be used to change various other things when gaming.