Skip to content

Repository files navigation

ZMK Layout Shift Module

[ English / 日本語 ]

This module provides a mechanism to dynamically shift keyboard layouts at runtime, primarily intended to solve discrepancies when an OS is configured for a non-US layout (e.g., JIS).

Specifically, this module provides a behavior &kpls that maps keycodes according to the current layout shift state. You can override the &kp behavior with &kpls, which lets it work without modifying your existing keymap, while preserving Keymap Editor compatibility. If you prefer not to override &kp, you can also use &kpls in your keymap instead.

Multiple layouts can be enabled simultaneously, each with independent on/off state.

Behaviors

This module defines the following behaviors:

  • &kpls: A layout-aware version of &kp; maps keycodes according to active layout shift maps. For example, &kpls EQUAL normally outputs =, but outputs _ (which is = in JIS layout) when US -> JIS layout is enabled.
  • &tog_ls: Toggles layout shift maps on/off
  • &tog_ls_on: Turns on layout shift maps
  • &tog_ls_off: Turns off layout shift maps

These toggle behaviors require the layout-maps property to specify which layout(s) to control (see Usage). Multiple layout maps can be specified.

List of Pre-Defined Layouts

  • US -> JIS: Converts US keyboard keycodes for use on an OS configured with the JIS layout
  • Dvorak: Dvorak keyboard layout
  • Swap Ctrl and Cmd: Swap Ctrl / Cmd for Windows / Mac

You can also define your own custom layout maps. See Adding New Layouts for details.

Usage

1. Add the Module to your west.yml

manifest:
  remotes:
    - name: zmkfirmware
      url-base: https://github.com/zmkfirmware
    - name: kot149
      url-base: https://github.com/kot149
  projects:
    - name: zmk
      remote: zmkfirmware
      revision: main
      import: app/west.yml
    - name: zmk-layout-shift
      remote: kot149
      revision: v2
  self:
    path: config

2. Configure Toggle Behaviors with Target Layout(s)

Configure the toggle behaviors to point to the desired layout map(s). Layout maps are automatically enabled when referenced. If layout-maps is omitted, the toggle controls all available layout maps at once:

&tog_ls { layout-maps = <&layout_shift_map_us_to_jis>; };
&tog_ls_on { layout-maps = <&layout_shift_map_us_to_jis>; };
&tog_ls_off { layout-maps = <&layout_shift_map_us_to_jis>; };

Available layout map nodes:

Node Label Layout
layout_shift_map_us_to_jis US -> JIS
layout_shift_map_dvorak Dvorak
layout_shift_map_swap_ctrl_cmd Swap Ctrl / Cmd

You can also define your own custom layout maps. See Adding New Layouts for details.

You can control multiple layouts with a single toggle by specifying multiple phandles:

&tog_ls { layout-maps = <&layout_shift_map_us_to_jis &layout_shift_map_swap_ctrl_cmd>; };
&tog_ls_on { layout-maps = <&layout_shift_map_us_to_jis &layout_shift_map_swap_ctrl_cmd>; };
&tog_ls_off { layout-maps = <&layout_shift_map_us_to_jis &layout_shift_map_swap_ctrl_cmd>; };

Note: When multiple layout maps are active simultaneously, maps are applied sequentially. By default, they are applied in devicetree declaration order (layout_shift_maps.dtsi). The output of one map becomes the input to the next, so if map 1 maps A -> B and map 2 maps B -> C, the final output is C.

To control the application order explicitly, set the priority property on each layout map node — smaller values are applied first.

&layout_shift_map_us_to_jis { priority = <1>; };
&layout_shift_map_swap_ctrl_cmd { priority = <2>; }; // applied after JIS

3. Include layout_shift_kp_override.dtsi to Override &kp Behavior

#include layout_shift_kp_override.dtsi to override &kp with custom implementation which is layout-aware. This allows to use layout shift without modifying your existing keymap, while preserving Keymap Editor compatibility.

#include <layout_shift_kp_override.dtsi>

Important

You need to add this include **below** the #include <behaviors.dtsi> or other includes to make it work. However, Keymap Editor automatically reorders the includes. To avoid this, you can copy-paste the definition of &kp from layout_shift_kp_override.dtsi directly to your keymap file.

4. Use Toggle Behaviors to Control Layout Shift State

Use &tog_ls, &tog_ls_on, &tog_ls_off in your keymap to control layout shift state. Then &kp will output the keycode according to the active layout shift maps.

#include <layout_shift_kp_override.dtsi>

&tog_ls { layout-maps = <&layout_shift_map_us_to_jis>; };
&tog_ls_on { layout-maps = <&layout_shift_map_us_to_jis>; };
&tog_ls_off { layout-maps = <&layout_shift_map_us_to_jis>; };

/ {
    keymap {
        compatible = "zmk,keymap";

        default_layer {
            bindings = <
                &kp EQUAL    // Will output = normally, but _ (which is = on JIS layout OS) when the US -> JIS layout shift is active
                &tog_ls      // Toggle US -> JIS keycode conversion on/off
                &tog_ls_on   // Turn on US -> JIS layout shift
                &tog_ls_off  // Turn off US -> JIS layout shift
            >;
        };
    };
};

Note

If you prefer not to override &kp, #include layout_shift.dtsi instead of layout_shift_kp_override.dtsi and use &kpls in your keymap instead:

#include <layout_shift.dtsi>

&tog_ls { layout-maps = <&layout_shift_map_us_to_jis>; };
&tog_ls_on { layout-maps = <&layout_shift_map_us_to_jis>; };
&tog_ls_off { layout-maps = <&layout_shift_map_us_to_jis>; };

/ {
    keymap {
        compatible = "zmk,keymap";

        default_layer {
            bindings = <
                &kpls EQUAL  // Will output = normally, but _ (which is = on JIS layout) when JIS layout is active
                &tog_ls      // Toggle US -> JIS layout shift on/off
                &tog_ls_on   // Turn on US -> JIS layout shift
                &tog_ls_off  // Turn off US -> JIS layout shift
            >;
        };
    };
};

Per-Layout Toggle Behaviors (Optional)

If you need separate toggle keys for different layouts (e.g., one key for JIS, another for Swap Ctrl/Cmd), you can define per-layout toggle behaviors in your keymap:

#include <layout_shift_kp_override.dtsi>

/ {
    behaviors {
        tog_ls_us_to_jis: toggle_layout_shift_us_to_jis {
            compatible = "zmk,behavior-layout-shift-toggle";
            #binding-cells = <0>;
            toggle-mode = "flip";
            layout-maps = <&layout_shift_map_us_to_jis>;
        };

        tog_ls_swap: toggle_layout_shift_swap {
            compatible = "zmk,behavior-layout-shift-toggle";
            #binding-cells = <0>;
            toggle-mode = "flip";
            layout-maps = <&layout_shift_map_swap_ctrl_cmd>;
        };
    };

    keymap {
        compatible = "zmk,keymap";

        default_layer {
            bindings = <
                &tog_ls_us_to_jis  // Toggle US -> JIS conversion
                &tog_ls_swap       // Toggle Swap Ctrl/Cmd layout
            >;
        };
    };
};

You can also define on / off variants by setting toggle-mode to "on" or "off".

Adding New Layouts

You can define custom layout maps in your own keymap or .dtsi files.

Define a Layout Map Node

Add a layout map node with compatible = "zmk,layout-shift-map":

/ {
    layout_shift_map_colemak: layout_shift_map_colemak {
        compatible = "zmk,layout-shift-map";
        mappings = <
            E  F  LAYOUT_SHIFT_OPTIONAL_ALL
            R  P  LAYOUT_SHIFT_OPTIONAL_ALL
            T  G  LAYOUT_SHIFT_OPTIONAL_ALL
            // ... add more mappings as needed
        >;
    };
};

Then reference it from a toggle behavior:

&tog_ls { layout-maps = <&layout_shift_map_colemak>; };

Or define a dedicated toggle behavior for it (see Per-Layout Toggle Behaviors).

Each mapping consists of three values: from_keycode, to_keycode, optional_modifiers.

Optional Modifier Control Options:

  • LAYOUT_SHIFT_OPTIONAL_NONE (0): All modifiers required (exact match)
  • LAYOUT_SHIFT_OPTIONAL_SHIFT: Shift keys are optional during matching
  • LAYOUT_SHIFT_OPTIONAL_CTRL: Ctrl keys are optional during matching
  • LAYOUT_SHIFT_OPTIONAL_ALT: Alt keys are optional during matching
  • LAYOUT_SHIFT_OPTIONAL_GUI: GUI (Windows/Cmd) keys are optional during matching
  • LAYOUT_SHIFT_OPTIONAL_ALL (0xFF): All modifiers optional during matching
  • Custom combinations: LAYOUT_SHIFT_OPTIONAL_CTRL | LAYOUT_SHIFT_OPTIONAL_ALT (Ctrl/Alt optional, Shift/GUI required)

References:

About

ZMK module to dynamically shift keyboard layouts at runtime

Topics

Resources

Stars

17 stars

Watchers

1 watching

Forks

Used by

Contributors

Languages