This turns right modifer keys into arrow keys when the keys are tapped while still modifiers when the keys are hold. In TMK the dual-role function is dubbed
TAP
.
#include "keymap_common.h"
/* Arrow keys on right modifier keys with TMK dual role feature
* https://github.com/tmk/tmk_core/blob/master/doc/keymap.md#213-modifier-with-tap-keydual-role
* https://en.wikipedia.org/wiki/Modifier_key#Dual-role_keys
const uint8_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
/* 0: qwerty */
[0] = KEYMAP( \
ESC, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, MINS,EQL, NUHS,BSPC, \
TAB, Q, W, E, R, T, Y, U, I, O, P, LBRC,RBRC,BSLS, \
LCTL,A, S, D, F, G, H, J, K, L, SCLN,QUOT,ENT, \
LSFT,NUBS,Z, X, C, V, B, N, M, COMM,DOT, SLSH,FN0, ESC, \
FN4, LGUI,LALT, SPC, APP, FN2, FN1, FN3),
[1] = KEYMAP( \
GRV, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, TRNS,TRNS, \
TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,\
TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS, \
TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,FN5, TRNS, \
TRNS,TRNS,TRNS, TRNS, TRNS,FN7, FN6, FN8),
const uint16_t PROGMEM fn_actions[] = {
[0] = ACTION_MODS_TAP_KEY(MOD_RSFT, KC_UP),
[1] = ACTION_MODS_TAP_KEY(MOD_RGUI, KC_DOWN),
[2] = ACTION_MODS_TAP_KEY(MOD_RALT, KC_LEFT),
[3] = ACTION_MODS_TAP_KEY(MOD_RCTL, KC_RIGHT),
[4] = ACTION_LAYER_MOMENTARY(1),
[5] = ACTION_MODS_TAP_KEY(MOD_RSFT, KC_PGUP),
[6] = ACTION_MODS_TAP_KEY(MOD_RGUI, KC_PGDN),
[7] = ACTION_MODS_TAP_KEY(MOD_RALT, KC_HOME),
[8] = ACTION_MODS_TAP_KEY(MOD_RCTL, KC_END),
Dual-role key: https://en.wikipedia.org/wiki/Modifier_key#Dual-role_keys
EJCT keycode works on OSX. https://github.com/tmk/tmk_keyboard/issues/250
It seems Windows 10 ignores the code and Linux/Xorg recognizes but has no mapping by default.
Not sure what keycode Eject is on genuine Apple keyboard actually. HHKB uses F20 for Eject key(Fn+f) on Mac mode but this is not same as Apple Eject keycode probably.
TO BE IMPROVED
real_mods is intended to retains state of real/physical modifier key state, while
weak_mods retains state of virtual or temprary modifiers which should not affect state real modifier key.
Let's say you hold down physical left shift key and type ACTION_MODS_KEY(LSHIFT, KC_A),
with weak_mods,
(1) hold down left shift: real_mods |= MOD_BIT(LSHIFT)
(2) press ACTION_MODS_KEY(LSHIFT, KC_A): weak_mods |= MOD_BIT(LSHIFT)
(3) release ACTION_MODS_KEY(LSHIFT, KC_A): waek_mods &= ~MOD_BIT(LSHIFT)
real_mods still keeps modifier state.
without weak mods,
(1) hold down left shift: real_mods |= MOD_BIT(LSHIFT)
(2) press ACTION_MODS_KEY(LSHIFT, KC_A): real_mods |= MOD_BIT(LSHIFT)
(3) release ACTION_MODS_KEY(LSHIFT, KC_A): real_mods &= ~MOD_BIT(LSHIFT)
here real_mods lost state for 'physical left shift'.
weak_mods is ORed with real_mods when keyboard report is sent.
https://github.com/tmk/tmk_core/blob/master/common/action_util.c#L57
AltGr is equivalent to Right Alt key, use KC_RALT. Use KC_LALT or KC_RALT for Option key on Mac.
http://en.wikipedia.org/wiki/AltGr_key
See this.
https://github.com/tmk/tmk_keyboard/issues/446#issuecomment-284899925
AL Email Reader(18A) is recognized as 'Photo viewer' on Windows10 weirdly. You can configure this behaviour at Settings>System>Apps for websites>Photo viewer.
https://geekhack.org/index.php?topic=88720.msg2439662#msg2439662
In Functoin Action you can check current modifier state with get_mods which returns 8-bit value, whose bit takes 1 when modifier turns on.
Modifier state:
76543210
|||||||`-- LCTRL
||||||`--- LSHIFT
|||||`---- LALT
||||`----- LGUI
|||`------ RCTRL
||`------- RSHIFT
|`-------- RALT
`--------- RGUI
So you can test modifer state like below:
if (get_mods() & MOD_BIT(KC_RALT)) {
// with RAlt
if (get_mods() & (MOD_BIT(KC_LSHIFT) | MOD_BIT(KC_RSHIFT))) {
// with Shift
} else {
// without Shift
else {
// without RAlt
Use KC_NUHS(KC_NONUS_HASH) for key around Enter and KC_NUBS(KC_NONUS_BSLASH) for one next to Left Shift.
https://github.com/tmk/tmk_keyboard/blob/be80ed2ef3ca669cc8dcc3555c09da0a7defae8c/tmk_core/common/keycode.h#L90-L91
It seems that many of ISO keyobards in the market uses KC_BSLS(KC_BSLASH) instead of KC_NUHS.
Also refer to footnotes 2 and 3 on p59 of 'USB HID Usage Tables' spec.
http://www.usb.org/developers/hidpage/Hut1_12v2.pdf
2 Typical language mappings: US: \| Belg: μ`£ FrCa: <}> Dan:’* Dutch: <> Fren:*μ Ger: #’ Ital: ù§ LatAm: }`] Nor:,*
Span: }Ç Swed: ,* Swiss: $£ UK: #~.
3 Typical language mappings: Belg:<\> FrCa:«°» Dan:<\> Dutch:]|[ Fren:<> Ger:<|> Ital:<> LatAm:<> Nor:<> Span:<>
Swed:<|> Swiss:<\> UK:\| Brazil: \|.
Assume that you are using non-US-QWERTY layout on your OS here.
Computer world is absolutely US-centric and you have to think keymap in US-QWERTY way. Use 'Q'(KC_Q) for 'A' and 'Z'(KC_Z) for 'W' in French-AZERTY keyboard, for example.
This is woe of us, who are minorities belong to rest of the world :)
You can hook API for this purpose. hook_layer_change() is called when layer_state is changed and you can define the function to control your LED.
This is example from FC660C keymap file, it controls its LED when Layer1 state is changed.
https://github.com/tmk/tmk_keyboard/blob/22f6cb273e347214bee1c31182764155a584a942/keyboard/fc660c/unimap_emu.c#L57-L65
#include "hook.h"
void hook_layer_change(uint32_t layer_state)
// lights LED on Insert when layer 1 is enabled
if (layer_state & (1L<<1)) {
PORTB &= ~(1<<5);
} else {
PORTB |= (1<<5);
At this time 'TAPPING_TERM' is only parameter for tap key. It is 200ms by default and you have to relase a key within the term to get 'tap' action and otherwise 'hold' action will come out.
You can configure in your config.h.
https://github.com/tmk/tmk_keyboard/blob/master/keyboard/hhkb/config.h#L43-L44
/* period of tapping(ms) */
#define TAPPING_TERM 300
Also see this entry for Tapping or Tap key.
https://github.com/tmk/tmk_keyboard/wiki/Keymap#4-tapping
Especially when you are using keyboard with physical ISO or JIS layout Mac may not recognize your keyboard type immediately and you have to specify the type manually.
https://support.apple.com/kb/PH25495?locale=en_US&viewlocale=en_US
Try the above first. Go 'System Preferences -> Keyboard -> Input Sources' and replace current one with other source, for example, use 'British - PC' instead of 'British' for UK keyboard or vice versa.
If this doesn't resolve your problem search the web for further infos or it is time to configure in your firmware keymap.
TMK firmware supports all of Japnese specific keys; 無変換(muhenkan), 変換(henkan), ひらがな(hiragana), ろ(ro), ¥(yen) and 全角/半角(zenkaku/hankaku). Also Apple Mac keyboard specific keys are supported; 英数(eisuu) and かな(kana). Note that 全角/半角 key is identical to Grave(`~) in US in USB HID spec and TMK firmware.
TMK USB-USB converter translates 英数(eisuu) and かな(kana) into 無変換 and 変換 respectively.
USB HID Usage
This is not trivial and you have to write C code to implement it.
Let's assume that you cannot change keyboard layout on the computer for some reason but you want to use US keyboard(layout).
For example, when you press '[' on your US keyboard you will get 'ü' on the German computer but you want to get '[' here.
To get '[' char on the computer the keyboard(conveter) have to send scan code of 'RAlt + 8'.
This can be done with ACTION_MODS_KEY(MOD_RALT, KC_8) relatively easily. See line 96 and 230.
Also you want to get '{' char when pressing Shift + '[' on your US keyboard. This is more complex.
The converter should send scan code for 'RAlt+9' but it have to cancel Shift before sending the code and restore Shift state after that.
CANCEL_SHIFT_ADD_RALT function is used for that.
See line 146 and 240.
https://gist.github.com/tmk/70babc152e09f102396dc0a621c8d66a/332f76be59c95c9efc33bcfd9337f7de26ba9501
* German(Linux): https://en.wikipedia.org/wiki/German_keyboard_layout#Linux
* Defult: Shift: RAlt: Sihft-RALt:
* ^1234567890ß' °!"§$%&/()=?` ′¹²³¼½¬{[]}\¸ ″¡⅛£¤⅜⅝⅞™±°¿˛
* qwertzuiopü+ QWERTZUIOPÜ* @ł€¶ŧ←↓→øþ"~ ΩŁ€®Ŧ¥↑ıØÞ°¯
* asdfghjklöä# ASDFGHJKLÖÄ' æſðđŋ ħ̣ĸł˝^’ ÆẞЪŊĦ˙&Ł̣ˇ˘˘
* <yxcvbnm,.- >YXCVBNM;:_ |»«¢„“”µ·…– ›‹©‚‘’º×÷—
* Current impl.:
* Defult: Shift: RAlt:(WIP) Sihft-RALt:(WIP)
* `1234567890-= ~!@#$%^&*()_+ ˛¡²³¤€¼½‘’¥× ¹ £ ÷
* qwertyuiop[]\ QWERTYUIOP{}| @ł€¶ŧ»↓→øþ[]’ ΩŁ€®Ŧ›↑ıØÞ™±`
* asdfghjkl;' ASDFGHJKL:" æſðđŋħ̣ ̣ ĸł˝^ ÆẞЪŊĦ˙&Ł̣ ˇ
* zxcvbnm,./ ZXCVBNM<>? ←«¢„“”µ·…⅞ ¥‹©‚‘’º×÷⅞
* US intl. with dead key(Linux) for reference:
* Defult: Shift: RAlt: Sihft-RALt:
* `1234567890-= ~!@#$%^&*()_+ `¡²³¤€¼½¾‘’¥× ~¹˝¯£¸^̛ ˛˘°̣ ÷
* qwertyuiop[]\ QWERTYUIOP{}| äåé®þüúíóö«»¬ ÄÅÉ®ÞÜÚÍÓÖ“”¦
* asdfghjkl;' ASDFGHJKL:" áßðfghjœø¶' Á§ÐFGHJŒØ°"
* zxcvbnm,./ ZXCVBNM<>? æx©vbñµç˙¿ ÆX¢VBÑµÇˇ̉
German T1: https://en.wikipedia.org/wiki/German_keyboard_layout#General_information
US International: https://en.wikipedia.org/wiki/File:KB_US-International.svg