Smart Keymap Features

Smart Keys

Below are the smart key features implemented for smart keymap:

Automation Key

Automation keys implement macro key behaviour.

For common use cases, it’s simpler to use higher-level wrappers around the key, such as K.string_macro (documented below), or otherwise use Nickel’s expressive language to build abstractions above it.

For examples of this key in other smart keyboard firmware, see e.g.:

automation key

Given a keymap.ncl:

let K = import "keys.ncl" in

let MY_MACRO = {
    automation_instructions.on_press = [
        { Press = { key_code = { Keyboard = 0x04 } } },
        { Release = { key_code = { Keyboard = 0x04 } } },
        { Press = { key_code = { Keyboard = 0x05 } } },
        { Release = { key_code = { Keyboard = 0x05 } } },
        { Press = { key_code = { Keyboard = 0x06 } } },
        { Release = { key_code = { Keyboard = 0x06 } } },
    ],
} in
{
  keys = [
      MY_MACRO,
  ],
}

When the keymap registers the following input

[
  tap_keymap_index 0,
]

Then the output should be equivalent to output from

[
  tap K.A,
  tap K.B,
  tap K.C,
]

on_press, while_pressed, on_release

The automation key’s key instructions support on_press, while_pressed, on_release instructions:

Given a keymap.ncl:

let K = import "keys.ncl" in
let { string_to_instructions, .. } = import "smart_keys/automation/lib.ncl" in

let MY_MACRO = {
    automation_instructions = {
        on_press = "ab" |> string_to_instructions,
        while_pressed = [
              { Tap = { key_code = { Keyboard = 0x06 } } },
              { Wait = 1000 },
        ],
        on_release = "de" |> string_to_instructions,
    },
} in
{
  keys = [
      MY_MACRO,
  ],
}

Automation Key (String Macro)

Automation keys implement macro key behaviour.

For examples of this key in other smart keyboard firmware, see e.g.:

string macro key

Given a keymap.ncl:

let K = import "keys.ncl" in

let MY_MACRO = K.string_macro "hello world" in
{
  keys = [
      MY_MACRO,
  ],
}

When the keymap registers the following input

let K = import "keys.ncl" in
let MY_MACRO = K.string_macro "hello world" in
[
  tap MY_MACRO,
]

Then the output should be equivalent to output from

[
  tap K.H,
  tap K.E,
  tap K.L,
  tap K.L,
  tap K.O,
  tap K.Space,
  tap K.W,
  tap K.O,
  tap K.R,
  tap K.L,
  tap K.D,
]

Callback Keys

Callback keys invoke keymap callbacks, allowing the implementing firmware to execute arbitrary behaviour when the key is pressed.

Callbacks for resetting the keyboard and entering the bootloader have been defined.

reset callback

Firmware should register the callback with void keymap_register_callback(uint8_t callback_id, void (*callback_fn)(void)); using KEYMAP_CALLBACK_RESET (for libsmart_keymap), or keymap::Keymap::set_callback (when using the smart_keymap crate).

Given a keymap.ncl:

let K = import "keys.ncl" in
{ keys = [ K.reset ] }

reset to bootloader callback

Firmware should register the callback with void keymap_register_callback(uint8_t callback_id, void (*callback_fn)(void)); using KEYMAP_CALLBACK_BOOTLOADER (for libsmart_keymap), or keymap::Keymap::set_callback (when using the smart_keymap crate).

Given a keymap.ncl:

let K = import "keys.ncl" in
{ keys = [ K.reset_to_bootloader ] }

arbitrary keymap callback

Arbitrary callbacks to the keyboard firmware are indicated as a pair of numbers 0-255.

Firmware should register the callback with void keymap_register_custom_callback(uint8_t custom_0, uint8_t custom_1, void (*callback_fn)(void)) (for libsmart_keymap), or keymap::Keymap::set_callback (when using the smart_keymap crate).

Given a keymap.ncl:

let K = import "keys.ncl" in
let my_callback = K.callback 0 255 in
{ keys = [ my_callback ] }

Caps Word Key

The “Caps Word” key can be thought of as “Caps Lock, for a single word”.

Where Caps Lock shifts all keys until it is disabled, Caps Word shifts while alphabetical keys (and underscore) are typed.

A motivating use case is typing out CONSTANTS_LIKE_THIS, automatically leaving the caps word mode when space is hit.

For examples of this key in other smart keyboard firmware, see e.g.:

Demonstrative Keymap

Given a keymap.ncl:

let K = import "keys.ncl" in
{
  keys = [
    K.caps_word.toggle,
    K.A,
    K.B,
    K.Space,
  ]
}

caps word key activates when tapped and deactivates after space key pressed

When the keymap registers the following input

[
  tap K.caps_word.toggle,
  tap K.A,
  tap K.Space,
  tap K.A,
]

Then the output should be equivalent to output from

[
  press (K.LeftShift),
  tap K.A,
  release (K.LeftShift),
  tap K.Space,
  tap K.A,
]

Consumer Keys (Media keys)

HID Consumer usage codes.

Media keys (play/pause, volume up, volume down, etc.) have been defined.

media keys

Given a keymap.ncl:

let K = import "keys.ncl" in
{
  keys = [
    K.PlayPause,
    K.ScanNext,
    K.ScanPrevious,
    K.Stop,
    K.VolumeDown,
    K.VolumeUp,
  ],
}

Custom Keys

Custom keys are reported as arbitrary codes 0-255 in the keymap output.

Their functionality is arbitrary, defined by the keyboard firmware implementation.

The pressed custom codes are available through the KeymapHidReport.custom (for libsmart_keymap), or keymap::KeymapOutput::pressed_custom_codes (for the smart_keymap crate).

custom key

Given a keymap.ncl:

let K = import "keys.ncl" in
let my_custom_key = K.custom 255 in
{ keys = [ my_custom_key ] }

Keyboard Key

HID Keyboard usage codes.

keyboard key

Given a keymap.ncl:

let K = import "keys.ncl" in
{ keys = [ K.A ] }

When the keymap registers the following input

[
  press K.A,
]

Then the HID keyboard report should equal

{ key_codes = [K.A] }

modified keyboard key

In keymap.ncl, modifiers can be merged with keys to form a modified key.

Given a keymap.ncl:

let K = import "keys.ncl" in
{ keys = [ K.A & K.LeftCtrl ] }

When the keymap registers the following input

[
  press (K.A & K.LeftCtrl),
]

Then the HID keyboard report should equal

{ modifiers = { left_ctrl = true }, key_codes = [K.A] }

Layered Key

“Layered Keys” are a lower-level key which implements layering functionality.

See Layers for a friendlier way to declare layers in a keymap.ncl file.

Demonstrative Keymap

Given a keymap.ncl:

let K = import "keys.ncl" in
{
  keys = [
    K.layer_mod.hold 1,
    K.A & { layered = [ K.B ] },
  ],
}

layered key acts as the base key when no layer is active

If no layers are active, the key will be the key on the base layer.

When the keymap registers the following input

[
  press (K.A & { layered = [ K.B ] }),
]

Then the HID keyboard report should equal

{ key_codes = [K.A] }

layered key acts as the key on that layer when its layer modifier held

When the keymap registers the following input

[
  press (K.layer_mod.hold 1),
  press (K.A & { layered = [ K.B ] }),
]

Then the HID keyboard report should equal

{ key_codes = [K.B] }

Layer Modifier: Set Default Layer

The K.layer_mod.set_default key allows setting the Default layer.

For examples of this feature in other smart keyboard firmware, see e.g.:

Demonstrative Keymap

Given a keymap.ncl:

let K = import "keys.ncl" in
{
  layers = [
    [
      K.layer_mod.set_default 0,
      K.layer_mod.set_default 1,
      K.A,
    ],
    [
      K.TTTT,
      K.TTTT,
      K.B,
    ],
  ],
}

tapping the set default layer modifier key changes the default layer

When the keymap registers the following input

[
  tap (K.layer_mod.set_default 1),
  press_keymap_index 2
]

Then the HID keyboard report should equal

{ key_codes = [K.B] }

Layer Modifier: Hold

The K.layer_mod.hold key activates a layer when it is held.

For examples of this feature in other smart keyboard firmware, see e.g.:

Demonstrative Keymap

Given a keymap.ncl:

let K = import "keys.ncl" in
{
  layers = [
    [
      K.layer_mod.hold 1,
      K.A,
    ],
    [
      K.TTTT,
      K.B,
    ],
  ],
}

layers acts as the base when no hold modifier key is held

If no layers are active, the key will be the key on the base layer.

When the keymap registers the following input

[
  press (K.A),
]

Then the HID keyboard report should equal

{ key_codes = [K.A] }

layers acts as active layer when a hold modifier key is held

When the keymap registers the following input

[
  press (K.layer_mod.hold 1),
  press (K.B),
]

Then the HID keyboard report should equal

{ key_codes = [K.B] }

Layer Modifier: Set Active Layers To

The K.layer_mod.set_active_layers_to key allows setting the active layers.

For examples of this feature in other smart keyboard firmware, see e.g.:

Demonstrative Keymap

Given a keymap.ncl:

let K = import "keys.ncl" in
{
  layers = [
    [
      K.layer_mod.set_active_layers_to [1],
      K.A,
    ],
    [
      K.layer_mod.set_active_layers_to [0],
      K.B,
    ],
  ],
}

pressing the set active layers modifier key changes the active layers

When the keymap registers the following input

[
  press (K.layer_mod.set_active_layers_to [1]),
  press_keymap_index 1,
]

Then the output should be equivalent to output from

[
  press (K.B),
]

Layer Modifier: Sticky

The K.layer_mod.sticky key activates a layer in a similar manner to sticky key modifiers:

For examples of this feature in other smart keyboard firmware, see e.g.:

Demonstrative Keymap

Given a keymap.ncl:

let K = import "keys.ncl" in
{
    layers = [
        [K.layer_mod.sticky 1, K.A, K.B],
        [K.TTTT, K.X, K.Y],
    ],
}

tapping sticky layer modifier activates the later for the next pressed key

When the keymap registers the following input

[
  tap (K.layer_mod.sticky 1),
  tap_keymap_index 1,
  tap_keymap_index 2,
]

Then the output should be equivalent to output from

[
  tap (K.X),
  tap (K.B),
]

tapping sticky layer modifier activates the layer only for the next pressed key

When the keymap registers the following input

[
  tap (K.layer_mod.sticky 1),
  press_keymap_index 1,
  press_keymap_index 2,
]

Then the output should be equivalent to output from

[
  press (K.X),
  press (K.B),
]

pressing sticky layer modifier activates the layer

When the keymap registers the following input

[
  press (K.layer_mod.sticky 1),
  tap_keymap_index 1,
  tap_keymap_index 2,
]

Then the output should be equivalent to output from

[
  tap (K.X),
  tap (K.Y),
]

Layer Modifier: Toggle

The K.layer_mod.toggle key toggles whether a layer is active when it is pressed.

For examples of this feature in other smart keyboard firmware, see e.g.:

Demonstrative Keymap

Given a keymap.ncl:

let K = import "keys.ncl" in
{
  layers = [
    [
      K.layer_mod.toggle 1,
      K.A,
    ],
    [
      K.TTTT,
      K.B,
    ],
  ],
}

pressing the toggle layer modifier key activates the layer

When the keymap registers the following input

[
  press (K.layer_mod.toggle 1),
  press (K.B),
]

Then the HID keyboard report should equal

{ key_codes = [K.B] }

pressing toggle layer modifier key a second time deactivates the layer

When the keymap registers the following input

[
  tap (K.layer_mod.toggle 1),
  press (K.layer_mod.toggle 1),
  press (K.A),
]

Then the HID keyboard report should equal

{ key_codes = [K.A] }

Mouse Keys

HID Mouse usage codes.

The pressed mouse codes are available through the KeymapHidReport.mouse (for libsmart_keymap), or keymap::KeymapOutput::pressed_mouse_output (for the smart_keymap crate).

mouse keys

Given a keymap.ncl:

let K = import "keys.ncl" in
{
  keys = [
    K.MouseButton1,
    K.MouseButton2,
    K.MouseButton3,
    K.MouseLeft,
    K.MouseDown,
    K.MouseUp,
    K.MouseRight,
    K.MouseWheelDown,
    K.MouseWheelUp,
  ],
}

Sticky Modifiers Key

The “Sticky Modifiers” key is keymap implementation of the “sticky key” accessibility feature that many desktop environments have.

If the sticky modifier key is tapped (without interruption), it modifies the next key press.

If the sticky modifier key is interrupted by another key press, then it behaves as a regular modifier key.

For examples of this key in other smart keyboard firmware, see e.g.:

Demonstrative Keymap

Given a keymap.ncl:

let K = import "keys.ncl" in
{
  keys = [
    (K.sticky K.LeftShift),
    (K.sticky K.LeftCtrl),
    K.A,
    K.B,
  ]
}

tapping sticky modifier key modifies next key press

When the keymap registers the following input

[
  tap (K.sticky K.LeftShift),
  press K.A,
]

Then the output should be equivalent to output from

[
  press (K.LeftShift),
  press K.A,
]

tapped sticky modifier keys stack

When the keymap registers the following input

[
  tap (K.sticky K.LeftShift),
  tap (K.sticky K.LeftCtrl),
  press K.A,
]

Then the output should be equivalent to output from

[
  press K.LeftShift,
  press K.LeftCtrl,
  press K.A,
]

sticky modifier releases when modified key releases

When the keymap registers the following input

[
  tap (K.sticky K.LeftShift),
  tap K.A,
]

Then the output should be equivalent to output from

[
  press (K.LeftShift),
  tap K.A,
  release (K.LeftShift),
]

sticky modifier key acts as regular key when interrupted by tap

When the keymap registers the following input

[
  press (K.sticky K.LeftShift),
  tap K.A,
  release (K.sticky K.LeftShift),
  tap K.B,
]

Then the output should be equivalent to output from

[
  press (K.LeftShift),
  tap K.A,
  release (K.LeftShift),
  tap K.B,
]

Sticky Modifiers Key (configure release: OnNextKeyPress)

The config.sticky.release can be set to "OnNextKeyPress" so that the sticky key releases when the next key is pressed after the modified key.

This helps with rolling key presses.

Demonstrative Keymap

Given a keymap.ncl:

let K = import "keys.ncl" in
{
  config.sticky.release = "OnNextKeyPress",
  keys = [
    (K.sticky K.LeftShift),
    (K.sticky K.LeftCtrl),
    K.A,
    K.B,
  ]
}

the sticky modifier releases when the next key is pressed

When the keymap registers the following input

[
  tap (K.sticky K.LeftShift),
  press K.A,
  press K.B,
]

Then the output should be equivalent to output from

[
  press (K.LeftShift),
  press K.A,
  release (K.LeftShift),
  press K.B,
]

TapDance Key

The TapDance key can behave differently depending on how many times it is tapped.

For examples of this key in other smart keyboard firmware, see e.g.:

Demonstrative Keymap

Let’s use a keymap with a tap-hold key, and a keyboard key.

Given a keymap.ncl:

let K = import "keys.ncl" in
{
  keys = [
    K.A & { tap_dances = [K.B, K.C] },
  ]
}

tap dance key acts as its first definition when tapped once

When the keymap registers the following input

[
  press (K.A & { tap_dances = [K.B, K.C] }),
  release (K.A & { tap_dances = [K.B, K.C] }),
]

And the keymap ticks 250 times

Then the output should be equivalent to output from

[
  press K.A,
  release K.A,
]

tap dance key acts as its first definition when pressed once and held

When the keymap registers the following input

[
  press (K.A & { tap_dances = [K.B, K.C] })
]

And the keymap ticks 250 times

Then the output should be equivalent to output from

[
  press K.A,
]

tap dance key acts as its third definition when tapped three times

When the keymap registers the following input

[
  tap (K.A & { tap_dances = [K.B, K.C] }),
  tap (K.A & { tap_dances = [K.B, K.C] }),
  tap (K.A & { tap_dances = [K.B, K.C] }),
]

Then the output should be equivalent to output from

[
  tap K.C,
]

TapHold Key

The TapHold key can behave differently depending on whether the key has been tapped or held.

e.g.

For examples of this key in other smart keyboard firmware, see e.g.:

Demonstrative Keymap

Let’s use a keymap with a tap-hold key, and a keyboard key.

Given a keymap.ncl:

let K = import "keys.ncl" in
{
  keys = [
    K.A & K.hold K.LeftCtrl,
    K.B
  ]
}

tap hold key acts as ‘tap’ when tapped

When the keymap registers the following input

[
  tap (K.A & K.hold K.LeftCtrl),
]

Then the HID keyboard report should equal

{ key_codes = [K.A] }

tap hold key acts as ‘hold’ when held

When the keymap registers the following input

[
  press (K.A & K.hold K.LeftCtrl),
]

And the keymap ticks 500 times

Then the HID keyboard report should equal

{ modifiers = { left_ctrl = true } }

TapHold Key (configure interrupt response: ignore)

The tap hold key’s response to interruptions can be configured.

“Interrupts ignored” can be configured by setting config.tap_hold.interrupt_response to "Ignore" in keymap.ncl.

“Ignore interrupts” just means the key only acts as hold by holding the key for longer than the tap-hold timeout duration.

Demonstrative Keymap

Let’s demonstrate tap-hold “ignore interrupt” behaviour using a keymap with a tap-hold key, and a keyboard key:

Given a keymap.ncl:

let K = import "keys.ncl" in
{
  config.tap_hold.interrupt_response = "Ignore",
  keys = [
    K.A & K.hold K.LeftCtrl,
    K.B
  ]
}

rolling key presses (press TH(A), press B, release TH(A), release B)

Rolling the tap-hold key with another key (i.e. interrupting the tap-hold key with another key press), releasing the tap-hold key with interrupt_response = "Ignore" resolves the key as tap.

When the keymap registers the following input

[
  press (K.A & K.hold K.LeftCtrl),
  press (K.B),
  release (K.A & K.hold K.LeftCtrl),
  release (K.B),
]

Then the output should be equivalent to output from

[
  press (K.A),
  press (K.B),
  release (K.A),
  release (K.B),
]

interrupting tap (press TH(A), press B, release B, release TH(A))

After interrupting the tap-hold key with another key tap (press & release), releasing the tap-hold key with interrupt_response = "Ignore" resolves the key as tap.

When the keymap registers the following input

[
  press (K.A & K.hold K.LeftCtrl),
  tap (K.B),
  release (K.A & K.hold K.LeftCtrl),
]

Then the output should be equivalent to output from

[
  press (K.A),
  tap (K.B),
  release (K.A),
]

TapHold Key (configure interrupt response: hold on key press)

The tap hold key’s response to interruptions can be configured.

“Resolves as ‘Hold’ when interrupted by key press” can be configured by setting config.tap_hold.interrupt_response to "HoldOnKeyPress" in keymap.ncl.

Demonstrative Keymap

Let’s demonstrate tap-hold “hold on interrupting key press” behaviour using a keymap with a tap-hold key, and a keyboard key:

Given a keymap.ncl:

let K = import "keys.ncl" in
{
  config.tap_hold.interrupt_response = "HoldOnKeyPress",
  keys = [
    K.A & K.hold K.LeftCtrl,
    K.B
  ]
}

rolling key presses (press TH(A), press B, release TH(A), release B)

Rolling the tap-hold key with another key (i.e. interrupting the tap-hold key with another key press), for a tap-hold key configured with interrupt_response = "HoldOnKeyPress" resolves the tap-hold key as ‘hold’.

When the keymap registers the following input

[
  press (K.A & K.hold K.LeftCtrl),
  press (K.B),
  release (K.A & K.hold K.LeftCtrl),
  release (K.B),
]

Then the output should be equivalent to output from

[
  press (K.LeftCtrl),
  press (K.B),
  release (K.LeftCtrl),
  release (K.B),
]

interrupting tap (press TH(A), press B, release B, release TH(A))

Interrupting the tap-hold key with another key tap (press & release), for a tap-hold key configured with interrupt_response = "HoldOnKeyPress" resolves the key as “hold”.

When the keymap registers the following input

[
  press (K.A & K.hold K.LeftCtrl),
  tap (K.B),
]

Then the output should be equivalent to output from

[
  press (K.LeftCtrl),
  tap (K.B),
]

TapHold Key (configure interrupt response: hold on tap)

The tap hold key’s response to interruptions can be configured.

“Resolves as ‘Hold’ when interrupted by key tap” can be configured by setting config.tap_hold.interrupt_response to "HoldOnKeyTap" in keymap.ncl.

Demonstrative Keymap

Let’s demonstrate tap-hold “hold on interrupting key tap” behaviour using a keymap with a tap-hold key, and a keyboard key:

Given a keymap.ncl:

let K = import "keys.ncl" in
{
  config.tap_hold.interrupt_response = "HoldOnKeyTap",
  keys = [
    K.A & K.hold K.LeftCtrl,
    K.B
  ]
}

rolling key presses (press TH(A), press B, release TH(A), release B)

Rolling the tap-hold key with another key (i.e. interrupting the tap-hold key with another key press), for a tap-hold key configured with interrupt_response = "HoldOnKeyTap" resolves the tap-hold key as ‘tap’.

When the keymap registers the following input

[
  press (K.A & K.hold K.LeftCtrl),
  press (K.B),
  release (K.A & K.hold K.LeftCtrl),
  release (K.B),
]

Then the output should be equivalent to output from

[
  press (K.A),
  press (K.B),
  release (K.A),
  release (K.B),
]

interrupting tap (press TH(A), press B, release B, release TH(A))

Interrupting the tap-hold key with another key tap (press & release), for a tap-hold key configured with interrupt_response = "HoldOnKeyTap" resolves the key as “hold”.

When the keymap registers the following input

[
  press (K.A & K.hold K.LeftCtrl),
  tap (K.B),
]

Then the output should be equivalent to output from

[
  press (K.LeftCtrl),
  tap (K.B),
]

TapHold Key (configure required_idle_time)

The required_idle_time config for tap hold keys means that tap hold keys act as ‘tap’ if they are pressed before the required idle time has passed since the previous keymap input event (press/release).

This helps prevent accidental ‘resolved-as-hold’ tap-hold key presses when typing quickly.

For examples of this key in other smart keyboard firmware, see e.g.:

Demonstrative Keymap

Let’s demonstrate tap-hold “required_idle_time” behaviour using a keymap with a keyboard key, and a tap-hold key:

Given a keymap.ncl:

let K = import "keys.ncl" in
{
  config.tap_hold.required_idle_time = 100,
  keys = [
    K.A,
    K.B & K.hold K.LeftCtrl,
  ]
}

tap hold key resolves as tap when pressed before required idle time

Pressing a tap-hold key immediately resolves it as ‘tap’ when it’s pressed after another key, within the required idle time.

When the keymap registers the following input

[
  tap (K.A),
  wait 50,
  press (K.B & K.hold K.LeftCtrl),
]

Then the output should be equivalent to output from

[
  tap (K.A),
  press (K.B),
]

tap hold key resolves as tap when tapped after required idle time

Whereas, the tap-hold key behaves as usual if the keymap is idle for the required time.

When the keymap registers the following input

[
  tap (K.A),
  wait 110,
  tap (K.B & K.hold K.LeftCtrl),
]

Then the output should be equivalent to output from

[
  tap (K.A),
  tap (K.B),
]

tap hold key resolves as tap when tapped after required idle time

When the keymap registers the following input

[
  tap (K.A),
  wait 110,
  press (K.B & K.hold K.LeftCtrl),
  wait 250,
]

Then the output should be equivalent to output from

[
  tap (K.A),
  press (K.LeftCtrl),
]

TapHold Key (configure resolve-as-hold timeout)

The “timeout” before a tap-hold is considered as held can be configured by the field config.tap_hold.timeout in keymap.ncl.

timeout configured to a low value

Given a keymap.ncl:

let K = import "keys.ncl" in
{
  config.tap_hold.timeout = 50,
  keys = [ K.A & K.hold K.LeftCtrl ],
}

When the keymap registers the following input

[
  press (K.A & K.hold K.LeftCtrl),
  wait 60,
]

Then the HID keyboard report should equal

{ modifiers = { left_ctrl = true } }

timeout configured to a high value

e.g. by setting the timeout to a very high value, the key still won’t resolve as “held” until that the timeout has been reached.

Given a keymap.ncl:

let K = import "keys.ncl" in
{
  config.tap_hold.timeout = 30000,
  keys = [ K.A & K.hold K.LeftCtrl ],
}

When the keymap registers the following input

[
  press (K.A & K.hold K.LeftCtrl),
]

And the keymap ticks 10000 times

Then the HID keyboard report should equal

{ modifiers = {}, key_codes = [] }

keymap.ncl

The Nickel code in ncl/ has some functions which help when writing keymap.ncl files.

Chords

“Chords” (also sometimes called “combos”) allow pressing multiple keys together at the same time to behave as another key.

For examples of this feature in other smart keyboard firmware, see e.g.:

Demonstrative Keymap

Given a keymap.ncl:

let K = import "keys.ncl" in
{
  chords = [
      { indices = [0, 1], key = K.C, },
  ],
  keys = [
      K.A, K.B,
  ],
}

chorded key behaves as usual when tapped individually

When the keymap registers the following input

[
  press K.A,
]

Then the output should be equivalent to output from

[
  press (K.A),
]

chorded keys tapped together behaves as chord

When the keymap registers the following input

[
  press K.A,
  press K.B,
]

Then the output should be equivalent to output from

[
  press (K.C),
]

chorded keys pressed together with delay behaves as separate keys

When the keymap registers the following input

[
  press K.A,
  wait 250,
  press K.B,
]

Then the output should be equivalent to output from

[
  press (K.A),
  press (K.B),
]

Chords (configure required_idle_time)

The required_idle_time config for chords means that chords can only be activated after the required idle time has passed since the previous keymap input event (press/release).

This helps prevent accidental chord activation when typing quickly.

For examples of this key in other smart keyboard firmware, see e.g.:

Demonstrative Keymap

Let’s demonstrate tap-hold “required_idle_time” behaviour using a keymap with a keyboard key, and a tap-hold key:

Given a keymap.ncl:

let K = import "keys.ncl" in
{
  config.chorded.required_idle_time = 100,
  chords = [
    { indices = [0, 1], key = K.C, },
  ],
  keys = [
    K.A, K.B, K.D,
  ],
}

chord doesn’t resolve when pressed before required idle time

When the keymap registers the following input

[
  tap (K.D),
  wait 50,
  press_keymap_index 0,
  press_keymap_index 1,
]

Then the output should be equivalent to output from

[
  tap (K.D),
  press (K.A),
  press (K.B),
]

chord resolves pressed after required idle time

When the keymap registers the following input

[
  tap (K.D),
  wait 150,
  press_keymap_index 0,
  press_keymap_index 1,
]

Then the output should be equivalent to output from

[
  tap (K.D),
  press (K.C),
]

Layer String

Layers in keymap.ncl can be defined using strings.

keymap.ncl with layer string

keymap.ncl supports defining each layer in the keymap with a string.

This is simpler than the equivalent nickel array using keys such as K.A, K.B, etc.

Each whitespace-delimited substring is then used as a field to lookup the key from keys.ncl.

For example:

{
  layers = [
    m%"
      Q W E R T Y
    "%,
  ],
}

keymap.ncl with layer string, with custom key definitions

Often you’ll want to be able to use custom keys, even in layer strings.

This can be achieved by providing a custom_keys function, which is a keys extension. i.e. returns a record of keys, extending a given K keys record.

For example:

{
  custom_keys = fun K =>
    {
      MY_Q = K.Q & K.LeftShift
    },
  layers = [
    m%"
      MY_Q W E R T Y
    "%,
  ],
}

Layers

Layers are a basic part of smart keyboard firmware.

Layers are like the Fn key on laptop keyboards, where holding the Fn key allows alternate functionality for other keys on the keyboard.

For examples of this key in other smart keyboard firmware, see e.g.:

Demonstrative Keymap

Layers can be used by setting using the layers field of a keymap.ncl.

Here, a keymap.ncl file with 2 keys, and 2 layers (base layer + 1 layer).

Given a keymap.ncl:

let K = import "keys.ncl" in
{
  layers = [
    [
      K.layer_mod.hold 1,
      K.A,
    ],
    [
      K.TTTT,
      K.B,
    ],
  ],
}

layers acts as the base when no layer is active

If no layers are active, the key will be the key on the base layer.

When the keymap registers the following input

[
  press (K.A),
]

Then the HID keyboard report should equal

{ key_codes = [K.A] }

layers acts as active layer when its layer modifier is held

When the keymap registers the following input

[
  press (K.layer_mod.hold 1),
  press (K.B),
]

Then the HID keyboard report should equal

{ key_codes = [K.B] }

keys.ncl Fields

keys.ncl provides the values for key definitions used in keymap.ncl.

See the documentation for keymap.ncl for examples of using keys.ncl.

The fields available in keys.ncl:

abbreviations

aliases

automation

callback

caps_word

consumer

custom

keyboard

layered

literals

mouse

shifted

sticky

tap_hold