zmk icon indicating copy to clipboard operation
zmk copied to clipboard

Keyboard Language/Layout Support

Open innovaker opened this issue 5 years ago • 16 comments

This topic was recently raised by xudongz and is increasingly on my mind as I revisit key names/codes.

  • Many potential users run their OS with an alternative keyboard layout.
  • For those unfamiliar with layouts: https://en.wikipedia.org/wiki/Keyboard_layout
  • Some users cannot change this setting on the OS due to restrictive policies.
  • The layout on a user's PC might be different to their smartphone.
  • This can be a point of confusion for mappings.

How should ZMK support or facilitate this use case?

Suggestions so far:

innovaker avatar Sep 12 '20 19:09 innovaker

The way I've approached this problem is distinguishing between "physical keys" (what gets sent via HID) and "virtual keys" (what is getting typed).

Suppose I want to type F. Normally you can just send KC_F if user is using QWERTY. However if user has Colemak, you need to send KC_E to get F. One can define a VirtualF key that looks at whether the device is configured for QWERTY/Colemak/etc and decide what to send.

I think some sort of "virtual key" mechanism is also useful for macros, where you have a virtual key that corresponds to a series of other virtual keys, at the lowest layer corresponding to a physical key that gets sent.

This can also be useful for making non-ASCII characters easy to define. For example you might want a key for é and you can define VirtualETilde that does:

  • Alt-E-E on MacOS
  • AltGr-E on Linux/Windows
  • Alt-K-K on MacOS with Colemak

Similarly you can have VirtualUnicode or that Ctrl-Shift-U-[codepoint] on Linux (which by the way, probably needs to be Ctrl-Shift-I on Linux with Colemak) and adjust the sequence based on the OS (or do nothing if not supported). You can then have VirtualString that calls a series of VirtualUnicode keys.

https://github.com/ergoblue/src-adafruit/blob/master/control/keymap.go is an example implementation of my proposal (in Go).

xudongzheng avatar Sep 12 '20 19:09 xudongzheng

@xudongzheng, as I've worked through #21, I've come to the same conclusion from a different angle: functional keys such as PASTE or MUTE.

  • These functional keys probably also need to be virtual, because Operating Systems don't provide consistent support across the HID specification. So, for the same user intent, the code(s) that must be sent over HID sometimes depends on the host's OS.
  • I also wouldn't be surprised if we discover nuances or platform specific codes the deeper we dig.

For the reasons you outlined, as well as my own, I'm also leaning towards the need for virtual key codes. Moreover, as we venture beyond standard keyboard codes (i.e. consumer controls, mouse buttons, axes, etc.), we may want to consider calling them intents.

However, it wouldn't be trivial to achieve, because an implementation needs to:

  1. Be aware of each host's expected layout.
  2. Provide mapping to the expected host layout. We'd only compile support for the mappings the user requires.
  3. Allow switching between host layouts.
  4. Handle the interplay of modifiers and simultaneous key events (NKRO).
  5. Minimize memory footprints (ROM and RAM). This'll largely depend on our encoding approach.
  6. Ideally associate host layout with the connection (profile). Which is probably only possible over BLE? because USB doesn't seem to have the concept of a unique connection identifier from ZMK's perspective.

I suspect that points (4) and (6) will be sticking points for virtual keys or intents.

Any thoughts?

innovaker avatar Oct 10 '20 14:10 innovaker

A short discussion between @petejohanson and I following my comment above is relevant to this issue in the short-term: https://discordapp.com/channels/719497620560543766/719544230497878116/764501452894765116

Suffice to say, host layout support is a consideration for the longer-term which needs to be explored.

In the short-term we're going to use workarounds for functional keys by leveraging OS-specific layers.

innovaker avatar Oct 10 '20 15:10 innovaker

First it would be nice to implement something like qmk, that doesn't recognizes the host keyboard layout necessarily, just create aliases for the keycodes in the different layout. Just implementing the basic would be sufficient for most people (I think). I just use the abnt2 layout because I need to have access to ç and accented keys, so in my keyboard running qmk I just need to import the abnt2 keycodes and use it instead of the default KC_ keycodes, for most keys, most modifiers and function keys are unchanged.

This and macros are the only things stopping me from running zmk, it would be nice to have something like that implemented.

Thanks for the awesome work!

heyitscassio avatar Nov 09 '21 07:11 heyitscassio

as @toniz4 said about, ignoring the layout on the host. IMO it will be best thing. I was on qmk and thought zmk will also do the same and used my keyboard on other machine just to realize it will understand my keys. This also limit me from using custom layout.

correct me if I am wrong about custom layout

Shahabaz-Bagwan avatar Feb 23 '22 07:02 Shahabaz-Bagwan

this is something that in my opinion keeps zmk from expanding outside us/ansi world. @urob has a solution that an be simply upstreamed and simplified.

alinelena avatar Oct 04 '22 20:10 alinelena

When I saw this post, I felt discouraged about ZMK, but then after quite a bit of reading through the documentation I set up my layout which is Colemak-DH angle mode with a few of Slovenian letters (č, š, ž...).

My host (PC) layout is Slovenian so I needed to change quite a number of characters, but you can copy keymaps for different host layouts from QMK repo (like I did for Slovenian host layout).

My steps were to copy keymap_slovenian.h (other layouts) to my ZMK config folder (keys_si.h). Then I changed symbols to match ZMK naming convention (take a look at keys.h).

With something like keys_si.h you can include it into your .keymap (#include "keys_si.h") and use your symbols really simply: &kp SI_A or &kp SI_ZCAR.

bzgec avatar Nov 07 '22 21:11 bzgec

@bzgec I've used your solution too for the German layout. Maybe we could write a small python script or something around that corner to translate from QMK naming convention to the ZMK one? I know that would only be provisionary, as it's a compile time thing, but it mostly works.

tyalie avatar Nov 13 '22 00:11 tyalie

When I saw this post, I felt discouraged about ZMK, but then after quite a bit of reading through the documentation I set up my layout which is Colemak-DH angle mode with a few of Slovenian letters (č, š, ž...).

My host (PC) layout is Slovenian so I needed to change quite a number of characters, but you can copy keymaps for different host layouts from QMK repo (like I did for Slovenian host layout).

My steps were to copy keymap_slovenian.h (other layouts) to my ZMK config folder (keys_si.h). Then I changed symbols to match ZMK naming convention (take a look at keys.h).

With something like keys_si.h you can include it into your .keymap (#include "keys_si.h") and use your symbols really simply: &kp SI_A or &kp SI_ZCAR.

I didn't know you could do that, thanks for the info! Now I feel comfortable with trying out zmk.

@bzgec I've used your solution too for the German layout. Maybe we could write a small python script or something around that corner to translate from QMK naming convention to the ZMK one? I know that would only be provisionary, as it's a compile time thing, but it mostly works.

I could try to do something like that, probably an awk script would suffice that, if there is not many edge cases. In 2 months or so i will receive the components for my new keyboard, so by then I probably will get something made.

heyitscassio avatar Nov 13 '22 15:11 heyitscassio

@bzgec and @tyalie thank you very much, I solved it now similarly to you for my swiss layout. Exellent!

KyrillGobber avatar Nov 18 '22 23:11 KyrillGobber

When I saw this post, I felt discouraged about ZMK, but then after quite a bit of reading through the documentation I set up my layout which is Colemak-DH angle mode with a few of Slovenian letters (č, š, ž...).

My host (PC) layout is Slovenian so I needed to change quite a number of characters, but you can copy keymaps for different host layouts from QMK repo (like I did for Slovenian host layout).

My steps were to copy keymap_slovenian.h (other layouts) to my ZMK config folder (keys_si.h). Then I changed symbols to match ZMK naming convention (take a look at keys.h).

With something like keys_si.h you can include it into your .keymap (#include "keys_si.h") and use your symbols really simply: &kp SI_A or &kp SI_ZCAR.

This is brilliant, thanks a lot. I followed your steps to make a french zmk keymap, it works perfectly well.

MajykOyster avatar May 07 '23 17:05 MajykOyster

Worth noting the ZMK locale generator project, which generates locale headers suitable for use with ZMK: https://github.com/joelspadin/zmk-locale-generator

If it is confirmed working for various locales by users, the generated headers could be incorporated into ZMK, or linked to in the documentation as an official approach.

caksoylar avatar May 07 '23 19:05 caksoylar

This is brilliant, thanks a lot. I followed your steps to make a french zmk keymap, it works perfectly well.

@MajykOyster I looked at your solution and I was wondering how you figured out how to compose the characters in your french_unicode.dtsi file?

delriodev avatar Nov 02 '23 04:11 delriodev

When I saw this post, I felt discouraged about ZMK, but then after quite a bit of reading through the documentation I set up my layout which is Colemak-DH angle mode with a few of Slovenian letters (č, š, ž...).

My host (PC) layout is Slovenian so I needed to change quite a number of characters, but you can copy keymaps for different host layouts from QMK repo (like I did for Slovenian host layout).

My steps were to copy keymap_slovenian.h (other layouts) to my ZMK config folder (keys_si.h). Then I changed symbols to match ZMK naming convention (take a look at keys.h).

With something like keys_si.h you can include it into your .keymap (#include "keys_si.h") and use your symbols really simply: &kp SI_A or &kp SI_ZCAR.

Thank you very much! I created a Danish keys.h if others are searching for something similar. keys_dk.h.txt

eZtaR1 avatar May 13 '24 12:05 eZtaR1

When I saw this post, I felt discouraged about ZMK, but then after quite a bit of reading through the documentation I set up my layout which is Colemak-DH angle mode with a few of Slovenian letters (č, š, ž...). My host (PC) layout is Slovenian so I needed to change quite a number of characters, but you can copy keymaps for different host layouts from QMK repo (like I did for Slovenian host layout). My steps were to copy keymap_slovenian.h (other layouts) to my ZMK config folder (keys_si.h). Then I changed symbols to match ZMK naming convention (take a look at keys.h). With something like keys_si.h you can include it into your .keymap (#include "keys_si.h") and use your symbols really simply: &kp SI_A or &kp SI_ZCAR.

Thank you very much! I created a Danish keys.h if others are searching for something similar. keys_dk.h.txt

Lol what a timing, just seaching for a DK layout, my {} ends up being ¶≠ with these. do you happen to know what i do wrong?

arbejd avatar May 13 '24 14:05 arbejd

Lol what a timing, just seaching for a DK layout, my {} ends up being ¶≠ with these. do you happen to know what i do wrong?

You didn't make a mistake - I did, and defined the brackets twice and one of these wrong, this should work :)

Nick from keymap editor proposed of doing it this way though, if you like having a GUI for editing. keys_dk.h.txt

eZtaR1 avatar May 14 '24 06:05 eZtaR1

Thanks @bzgec. Adapted your approach to make a UK version here: keys_uk.h

bensums avatar Dec 08 '24 12:12 bensums

Worth noting the ZMK locale generator project, which generates locale headers suitable for use with ZMK: https://github.com/joelspadin/zmk-locale-generator

If it is confirmed working for various locales by users, the generated headers could be incorporated into ZMK, or linked to in the documentation as an official approach.

Might be worth adding Joel's module to the docs and/or editing the OP at the top so it's a bit easier to discover?

urob avatar Dec 08 '24 18:12 urob