* changes to keymaps * changes to userspace * changes to userspace * removed reference to fc660c keymap which no longer exists from userspace readme * removed preonic keymap
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_userfunction which is now located in spacebarracecar.c. To change keyboard specific configurationprocess_record_keymapis used (see drashna userspace readme for better explanation). - There is a predefined
_DEADKEYlayer in spacebarracecar.h underenum userspace_layers. Shifted CU_QUOT enables the dead key layer, just like KC_QUOT would when using the US International layout. (Seeenum userspace_custom_keycodesfor 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_ESCTcan be used to enable/disable this behavior. - For a complete example see my planck keymap.
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_DEADKEYlayer.
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.