KMonad is an advanced tool that lets you infinitely customize and extend the functionalities of almost any keyboard. For a detailed list of features, see here.
If you want to get started with the latest, stable binary release, please check out the master branch, if you are interested in the latest additions and tweaks, switch on over to develop and compile your own binary.
Additionally, if you need any help or just want to say hi, you can
join our Discord server or
jump into our IRC channel
(#kmonad@irc.libera.chat
), which is also bridged with
matrix (#kmonad:libera.chat
).
KMonad offers advanced customization features such as layers, multi-tap, tap-hold, and much more. These features are usually available at the hardware level on the QMK-firmware enabled keyboards. However, KMonad allows you to enjoy such features in virtually any keyboard by low-level system manipulations.
For a good introduction to KMonad, have a look at this Youtube video.
KMonad lets you map any keyboard button to any keymap. Want to swap the useless Caps Lock key with the Escape key? Want to have your modifiers such as Shift and Control on your home row, without breaking your normal typing flow? Want a modifier that is combination of Alt + Ctrl + Super + Shift? You can do all of those and much more!
A layer is a set of keymaps assigned to your keyboard’s buttons. You can have as many layers on top of your base layer as you want. For instance, you can have your regular QWERTY layout, a Colemak/ Dvorak layout, a numbers and symbols layer, a function keys layer, a layer for mouse navigation and system controls — all in a 60% keyboard. When a particular layer is active, any keypress is interpreted according to the layout defined in that layer. With proper configurations, you can jump to a specific layer or switch to one for the next keypress, or do various other complex manipulations.
One of the distinguishing features of KMonad is the vast capabilities with Multi-Use Buttons. You can have a single button do different things based on whether it is pressed quickly in succession, or pressed once, or held. For example, you can configure the Caps Lock key to act as an Escape button when pressed once and released, a Ctrl modifier when held-down, and a button to jump to a layer when pressed twice quickly in succession. You can make the left and right Shift keys to act like left and right parentheses (like the Space Cadet Shift keys) when tapped once, and regular Shift keys when held down. The possibilities are infinite!
With Command Buttons you can trigger shell commands with a tap of any button.
There are many more exciting features of KMonad that you can find in the configuration tutorial.
For more information on how to install KMonad, please refer to: - installation
For information on how to configure KMonad, please refer you to: - the configuration tutorial - user configurations
Want to add your own keyboard configuration to kmonad-contrib? Just fork the repository, create a new subdirectory using your GitHub username and submit a pull request!
There are startup scripts available for different init systems in startup.
For several commonly asked questions regarding various configuration issues, please see: - the FAQ
The core maintainer is currently chronically ill with debilitating autoimmune symptoms. They come and go, but when they are there they very much get in the way of concentrated work. It is very much his intent to keep working on KMonad until it is very, very good. But please be aware that he might be gone for weeks on end, not out of a lack of interest, but out of a lack of capacity. You are always free to reach out to him by email.
` #| ————————————————————————–
KMonad: Guided tour
Welcome to the KMonad configuration tutorial. This document aims to explain: 1. The configuration syntax 2. The required information 3. All possible configuration options
This document should be a runnable configuration, so with some tweaks
to the defcfg
block (see below) you should be able to try
out all the examples interactively.
————————————————————————– |#
#| ————————————————————————–
Basic syntax: comments and parentheses
KMonad’s configuration language is styled on various lisps, like scheme or Common Lisp. In a lisp, every statement is entered between ‘(’ and ’)’s. If you are more used to Fortan style languages (python, ruby, C, Java, etc.), the change is quite straightforward: the function name moves into the parentheses, and you don’t use commas to separate arguments. I.e.
This: my_function(a, 3, “Alakazam”) Becomes: (my_function a 3 “Alakazam”)
The reason for this is because Lisp-style languages are very easy to parse and write syntax-highlighters for.
We also provide standard Lisp syntax for comments: - block comments between: #| and its reverse - line comments following: ;;
Unlike standard lisp, a single ; does not denote a comment, but instead the keycode for semicolon.
Also, as you might have noticed, whitespace is possible anywhere.
To check for syntax errors while editing, invoke kmonad with the -d option. ————————————————————————– |#
#| ————————————————————————– Necessary: the defcfg
block
There are a few bits of information that are required to be present
in a KMonad configuration file. One of these is the existence of exactly
1 defcfg
statement. This statement is used to customize
various configuration settings. Many of these settings have default
values, but a minimal definition must include at least an ‘input’ field
and an ‘output’ field. These describe how KMonad captures its inputs and
how it emits its outputs.
First, let’s go over the optional, non-OS specific settings. Currently there is only 2:
fallthrough: true
or false
, defaults to
false
KMonad catches input events and tries to match them to various
handlers. If it cannot match an event to any handler (for example, if it
isn’t included in the defsrc
block, or if it is, but the
current keymap does not map any buttons to it), then the event gets
quietly ignored. If fallthrough
is set to
true
, any unhandled events simply get reemitted.
allow-cmd: true
or false
, defaults to
false
If this is set to false
, any action that runs a
shell-command will simply log to stdout
without ever
running (log-level info). Don’t ever enable this on a configuration that
you do not trust, because:
(cmd-button “rm -rf ~/*“)
is a thing. For more information on the `cmd-button’ function, see the section on Command buttons below.
There are also some optional OS specific settings that we support:
cmp-seq': KEY, defaults to
RightAlt’ (Linux X11
specific)
This sets your compose key for Unicode input. For more information, as well as a workaround to also make this work on windows, see the section on Compose-key sequences below.
`cmp-seq-delay’: NUMBER (in milliseconds)
This sets a delay between each pressed key in a compose-key sequence.
Some environments may have troubles recognizing the key sequence if it’s
pressed too rapidly; if you experience any problems in this direction,
you can try setting this value to 5' or
10’ and see if that
helps.
Secondly, let’s go over how to specify the input
and
output
fields of a defcfg
block. This differs
between OS’es, and so do the capabilities of these interfaces.
– Linux ——
In Linux we deal with input by performing an ioctl-grab on a specific device-file. This allows us to hook KMonad on the input of exactly 1 keyboard, and allows you to run multiple instances of KMonad for different keyboards. We make an input using: (device-file “/dev/input/by-id/my-keyboard-kbd”)
NOTE: Any valid path to a device-file will work, but it is recommended to use the ‘by-id’ directory, since these names will not change if you replug the device.
We deal with output by creating a ‘uinput’ device. This requires that the ‘uinput’ kernel module is loaded. The easiest way to ensure this is by calling ‘sudo modprobe uinput’. We create a uinput device using: (uinput-sink “name” “optional post-init command”)
– Windows —-
In Windows we do not get such fine-grained control. We use a low-level keyboard hook to intercept all non-injected keyboard events. There is currently an open issue to improve the C-bindings used to capture windows keyevents, and if you have a better way to approach this issue, help is deeply appreciated. You specify a windows input using: (low-level-hook)
Similarly, the output in Windows lacks the fine-grained control. We
use the SendEvent API to emit key events directly to Windows. Since
these are ‘artificial’ events we won’t end up catching them again by the
low-level-hook
. It is very likely that KMonad does not play
well with other programs that capture keyboard input like AHK. You
specify windows output using: (send-event-sink)
– Mac OS —–
For Mac questions I suggest filing an issue and tagging @thoelze1, he wrote the MacOS API. However, input using: (iokit-name “optional product string”)
By default this should grab all keyboards, however if a product string is provided, KMonad will only capture those devices that match the provided product string. If you would like to provide a product string, you can run `make; ./list-keyboards’ in c_src/mac to list the product strings of all connected keyboards.
You initialize output on MacOS using: (kext)
————————————————————————– |#
(defcfg ;; For Linux input (device-file “/dev/input/by-id/usb-04d9_daskeyboard-event-kbd”) output (uinput-sink “My KMonad output” ;; To understand the importance of the following line, see the section on ;; Compose-key sequences at the near-bottom of this file. “/run/current-system/sw/bin/sleep 1 && /run/current-system/sw/bin/setxkbmap -option compose:ralt”) cmp-seq ralt ;; Set the compose key to `RightAlt’ cmp-seq-delay 5 ;; 5ms delay between each compose-key sequence press
;; For Windows ;; input (low-level-hook) ;; output (send-event-sink)
;; For MacOS ;; input (iokit-name “my-keyboard-product-string”) ;; output (kext)
;; Comment this if you want unhandled events not to be emitted fallthrough true
;; Set this to false to disable any command-execution in KMonad allow-cmd true )
#| ————————————————————————– Necessary: the defsrc
block
It is difficult to explain the defsrc
block without
immediately going into deflayer
blocks as well.
Essentially, KMonad maps input-events to various internal actions, many
of which generate output events. The defsrc
block explains
the layout on which we specify our deflayer
s down the
line.
It is important to realize that the defsrc
block doesn’t
necessarily have to coincide with your actual input keyboard.
You can specify a full 100% defsrc
block, but only use a
40% keyboard. This will mean that every deflayer
you
specify will also have to match your 100% defsrc
, and that
your actual keyboard would be physically unable to trigger about 60% of
your keymap, but it would be perfectly valid syntax.
The dual of this (and more useful) is that it is also perfectly valid
to only specify that part of your keyboard in defsrc
that
you want to remap. If you use a 100% keyboard, but don’t want to remap
the numpad at all you can simply leave the numpad out of your
defsrc
, and it should work just fine. In that particular
case you probably want to set fallthrough
to
true
in your defcfg
block though.
In the future we would like to provide support for multiple, named
defsrc
blocks, so that it becomes easier to specify various
layers for just the numpad, for example, but at the moment any more or
less than 1 defsrc
block will result in an error.
The layouting in the defsrc
block is completely free,
whitespace simply gets ignored. We strive to provide a name for every
keycode that is no longer than 4 characters, so we find that laying out
your keymap in columns of 5 works out quite nicely (although wider
columns will allow for more informative aliases, see below).
Most keycodes should be obvious. If you are unsure, check ‘./src/KMonad/Keyboard/Keycode.hs’. Every Keycode has a name corresponding to its Keycode name, but all lower-case and with the ‘Key’ prefix removed. There are also various aliases for Keycodes starting around line 350. If you are trying to bind a key and there is not a 4-letter alias, please file an issue, or better yet, a pull-request, and it will be added promptly.
Also, you can consult ‘./keymap/template/’ for various input templates to use directly or to look up keycodes by position. Here we use the input-template for ‘us_ansi_60.kbd’
————————————————————————– |#
(defsrc grv 1 2 3 4 5 6 7 8 9 0 - = bspc tab q w e r t y u i o p [
]
caps a s d f g h j k l ; ’ ret lsft z x c v b n m , . / rsft lctl lmet
lalt spc ralt rmet cmp rctl )
#| ————————————————————————– Optional : defalias
statements
KMonad will let you specify some very specific, crazy buttons. These
definitions can get pretty long, though, and would make
deflayer
blocks nearly impossible to read. Therefore we
provide the ability to alias names to these buttons, to keep the actual
deflayer
statements orderly.
A defalias
can contain any number of aliases, and it can
refer backwards or forwards to layers without issue. The only sequencing
that needs to be kept in mind is that a defalias
cannot
refer forward to another defalias
that is not yet
defined.
Here we define a few aliases, but we will define more later. Notice that we try to only use 3 letter names for aliases. If that is not enough to be clear, consider widening all columns to 6 or 7 characters (or be content with a messy config).
————————————————————————– |#
(defalias num (layer-toggle numbers) ;; Bind num to a button that switches to a layer kil C-A-del ;; Bind kil to a button that Ctrl-Alt-deletes )
#| NOTE: The above code could just as easily have been written as: (defalias num (layer-toggle numbers)) (defalias kil C-A-del) |#
#| ————————————————————————– Necessary: at least 1
deflayer
block
As explained in the defsrc
section, a
deflayer
will define a button for each corresponding entry
in the defsrc
definition. A deflayer
statement
consists of the deflayer
keyword, followed by the name used
to identify this layer, followed by N
‘statements-that-evaluate-to-a-button’, where N is exactly how many
entries are defined in the defsrc
statement.
It is also important to mention that the ‘keymap’ in KMonad is modelled as a stack of layers (just like in QMK). When an event is registered we look in the top-most layer for a handler. If we don’t find one we try the next layer, and then the next.
Exactly what ‘evaluates-to-a-button’ will be expanded on in more detail below. There are very many different specialist buttons in KMonad that we will touch upon. However, for now, these 4 are a good place to begin:
Any keycode evaluates to a button that, on press, emits the press of that keycode, and on release, emits the release of that keycode. Just a ‘normal’ button. The exception is ‘', which gets used as an escape character. Use’\’ instead. Other characters that need to be escaped to match the literal character are ‘(’, ‘)’, and ’_’.
An @-prefixed name evaluates to an alias lookup. We named two
buttons in the defalias
block above, we could now refer to
these buttons using @num
and @kil
. This is
also why we only use alias-names no longer than 3 characters in this
tutorial. Also, note that we are already referencing some aliases that
have not yet been defined, this is not an issue.
The ’_’ character evaluates to transparent. I.e. no handler for that key-event in this layer, causing this event to be handed down the layer stack to perhaps be handled by the next layer.
The ‘XX’ character evaluates to blocked. I.e. no action bound to that key-event in this layer, but do actually catch event, preventing any underlying layer from handling it.
Finally, it is important to note that the first
deflayer
statement in a KMonad config will be the layer
that is active when KMonad starts up.
————————————————————————– |#
(deflayer qwerty grv 1 2 3 4 5 6 7 8 9 0 - = bspc tab q w e r t y u i
o p [ ]
caps a s d f g h j k l ; ’ ret lsft z x c v b n m , . / rsft lctl @num lalt spc ralt rmet @sym @tst )
#| ————————————————————————– Optional: as many layers as you please
We had already defined num
as referring to a
(layer-toggle numbers)
. We will get into layer-manipulation
soon, but first, let’s just create a second layer that overlays a numpad
under our right-hand.
To easily specify layers it is highly recommended to create an empty
deflayer
statement as a comment at the top of your config,
so you can simply copy-paste this template. There are also various empty
layer templates available in the ‘./keymap/template’ directory.
————————————————————————– |#
(deflayer numbers _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ XX / 7 8 9 - _ _ _ _ _ _ XX * 4 5 6 + _ _ ( ) . XX 0 1 2 3 _ _ _ _ _ _ _ _ )
#| ————————————————————————– Optional: modded buttons
Let’s start by exploring the various special buttons that are supported by KMonad by looking at ‘modded’ buttons, that is to say, buttons that activate some kind of ‘mod’, then perform some button, and finally release that ‘mod’ again.
We have already seen an example of this style of button, our
kil
button is one such button. Let’s look at it in more
detail: C-A-del
This looks like a simple declarative statement, but it’s helpful to realize that is simply syntactic sugar around 2 function calls. This statement is equivalent to: (around ctl (around alt del))
This highlights a core design principle in KMonad: we try to provide very simple buttons, and then we provide rules and functions for combining them into new buttons. Although note: still very much a work in progress.
So, looking at this statement: (around foo bar)
Here, around
is a function that takes two buttons and
creates a new button. This new button will, on a press, first press foo,
then press bar, and on a release first release bar, and then foo. Once
created, this new button can be passed to anything in KMonad that
expects a button.
We have already seen other examples of modded buttons, (, ), *, and
+. There are no Keycodes for these buttons in KMonad, but they are
buttons. They simply evaluate to (around lsft x)
. All
shifted numbers have their corresponding characters, the same is true
for all capitals, and < > : ~ ” | { } _ + and ?.
To wrap up ‘modded-buttons’, let’s look back at C-A-del. We have 8 variants: C- : (around lctl X) A- : (around lalt X) M- : (around lmet X) S- : (around lsft X)
Then RC-, RA-, RM-, and RS- behave exactly the same, except using the right-modifier.
These can be combined however you please: C-A-M-S-x ;; Perfectly valid C-% ;; Perfectly valid: same as C-S-5 C-RC-RA-A-M-S-RS-m ;; Sure, but why would you?
Also, note that although we provide special syntax for certain modifiers, these buttons are in no way ‘special’ in KMonad. There is no concept of ‘modifier’. (around a (around b c)) ;; Perfectly valid
————————————————————————– |#
(defalias
;; Something useful cpy C-c pst C-v cut C-x
;; Something silly md1 (around a (around b c)) ;; abc md2 (around a (around lsft b)) ;; aB md3 C-A-M-S-l md4 (around % b) ;; BEWARE: %B, not %b, do you see why? )
#| ————————————————————————– Optional: sticky keys
KMonad also support so called “sticky keys”. These are keys that will
behave as if they were pressed after just tapping them. This behaviour
wears off after the next button is pressed, which makes them ideal for
things like a quick control or shift. For example, tapping a sticky and
then pressing abc' will result in
Abc’.
You can create these keys with the `sticky-key’ keyword:
(defalias
slc (sticky-key 500 lctl))
The number after `sticky-key’ is the timeout you want, in milliseconds. If a key is tapped and that time has passed, it won’t act like it’s pressed down when we receive the next keypress.
It is also possible to combine sticky keys. For example, to get a sticky shift+control you can do
(defalias
ssc (around
(sticky-key 500 lsft)
(sticky-key 500 lctl)))
————————————————————————– |#
;; Let’s make both shift keys sticky (defalias sl (sticky-key 300 lsft) sr (sticky-key 300 rsft))
;; Now we define the ‘tst’ button as opening and closing a bunch of
layers at ;; the same time. If you understand why this works, you’re
starting to grok ;; KMonad. ;; ;; Explanation: we define a bunch of
testing-layers with buttons to illustrate ;; the various options in
KMonad. Each of these layers makes sure to have its ;; buttons not
overlap with the buttons from the other layers, and specifies all ;; its
other buttons as transparent. When we use the nested around
statement, ;; whenever we push the button linked to ‘@tst’ (check
qwerty
layer, we bind ;; it to rctl
), any
button we press when holding rctl
will be pressed in the ;;
context of those 4 layers overlayed on the stack. When we release
rctl
, all ;; these layers will be popped again. (defalias
tst (around (layer-toggle macro-test) (around (layer-toggle layer-test)
(around (layer-toggle around-next-test) (around (layer-toggle
command-test) (layer-toggle modded-test))))))
(deflayer modded-test _ _ _ _ _ _ _ _ _ _ _ _ _ _ @md4 _ _ _ _ _ _ _ _ _ _ _ @md1 @md2 @md3 _ _ _ _ _ _ _ _ @cut @cpy @pst _ _ _ _ _ _ _ _ _ _ _ _ _ )
#| ————————————————————————– Optional: tap-macros
Let’s look at a button we haven’t seen yet, tap-macros.
tap-macro
is a function that takes an arbitrary number
of buttons and returns a new button. When this new button is pressed it
rapidly taps all its stored buttons in quick succesion except for its
last button, which it only presses. This last button gets released when
the tap-macro
gets released.
There are two ways to define a tap-macro
, using the
tap-macro
function directly, or through the #() syntactic
sugar. Both evaluate to exactly the same button.
(tap-macro K M o n a d)
#(K M o n a d)
If you are going to use a tap-macro
to perform a
sequence of actions inside some program you probably want to include
short pauses between inputs to give the program time to register all the
key-presses. Therefore we also provide the ‘pause’ function, which
simply pauses processing for a certain amount of milliseconds. Pauses
can be created like this:
(pause 20)
P20
You can also pause between each key stroke by specifying the
:delay' keyword, as well as a time in ms, at the end of a
tap-macro’:
(tap-macro K M o n a d :delay 5)
#(K M o n a d :delay 5)
The above would be equivalent to e.g.
(tap-macro K P5 M P5 o P5 n P5 a P5 d)
The tap-macro-release
is like tap-macro
,
except that it waits to press the last button when the
tap-macro-release
gets released. It might be useful when
combined with a footswitch that sends keybooard scan codes.
(tap-macro-release i K M o n a d esc)
WARNING: DO NOT STORE YOUR PASSWORDS IN PLAIN TEXT OR IN YOUR KEYBOARD
I know it might be tempting to store your password as a macro, but there are 2 huge risks: 1. You accidentally leak your config and expose your password 2. Anyone who knows about the button can get clear-text representation of your password with any text editor, shell, or text-input field.
Support for triggering shell commands directly from KMonad is described in the command buttons section below.
This concludes this public service announcement.
————————————————————————– |#
(defalias mc1 #(K M o n a d) mc2 #(C-c P50 A-tab P50 C-v) ;; Careful, this might do something mc3 #(P200 h P150 4 P100 > < P50 > < P20 0 r z 1 ! 1 ! !) mc4 (tap-macro a (pause 50) @md2 (pause 50) c) mc5 (tap-macro-release esc esc esc) mc6 #(@mc3 spc @mc3 spc @mc3) )
(deflayer macro-test _ @mc1 @mc2 @mc3 @mc4 @mc5 @mc6 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ )
#| ————————————————————————– Optional: layer manipulation
You have already seen the basics of layer-manipulation. The
layer-toggle
button. This button adds a layer to the top of
KMonad’s layer stack when pressed, and removes it again when released.
There are a number of other ways to manipulate the layer stack, some
safer than others. Let’s go through all of them from safest to least
safe:
layer-toggle
works as described before, 2 things to
note: 1. If you are confused or worried about pressing a key, changing
layers, and then releasing a key and this causing issues: don’t be.
KMonad handles presses and releases in very different ways. Presses get
passed directly to the stacked keymap as previously described. When a
KMonad button has its press-action triggered, it then registers a
callback that will catch its own release before we ever touch the
keymap. This guarantees that the button triggered by the press of X
will be the button whose release is triggered by the release of
X (the release of X might trigger other things as well, but that is
besides the point.) 2. If layer-toggle
can only ever add
and then necessarily remove 1 layer from the stack, then it will never
cause a permanent change, and is perfectly safe.
layer-delay
, once pressed, temporarily switches to some
layer for some milliseconds. Just like layer-toggle
this
will never permanently mess-up the layer stack. This button was
initially implemented to provide some ‘leader-key’ style behavior.
Although I think in the future better solutions will be available. For
now this will temporarily add a layer to the top of the stack:
(layer-delay 500 my-layer)
layer-next
, once pressed, primes KMonad to handle the
next press from some arbitrary layer. This aims to fill the same usecase
as layer-delay
: the beginnings of ‘leader-key’ style
behavior. I think this whole button will get deleted soon, because the
more general around-next
now exists (see below) and this is
nothing more than: (around-next (layer-toggle layer-name)) Until then
though, use layer-next
like this: (layer-next
layer-name)
layer-switch
: change the base-layer of KMonad. As
described at the top of this document, the first deflayer
statement is the layer that is active when KMonad starts. Since
layer-toggle
can only ever add on and remove from the top
of that, it can never change the base-layer. The following button will
unregister the bottom-most layer of the keymap, and replace it with
another layer. (layer-switch my-layer)
This is where things start getting potentially dangerous (i.e. get
KMonad into an unusuable state until a restart has occured). It is
perfectly possible to switch into a layer that you can never get out of.
Or worse, you could theoretically have a layer full of only
XX
s and switch into that, rendering your keyboard unuseable
until you somehow manage to kill KMonad (without using your
keyboard).
However, when handled well, layer-switch
is very useful,
letting you switch between ‘modes’ for your keyboard. I have a tiny
keyboard with a weird keymap, but I switch into a simple ‘qwerty’ keymap
shifted 1 button to the right for gaming. Just make sure that any ‘mode’
you switch into has a button that allows you to switch back out of the
‘mode’ (or content yourself restarting KMonad somehow).
layer-add
and layer-rem
. This is where you
can very quickly cause yourself a big headache. Originally I didn’t
expose these operations, but someone wanted to use them, and I am not
one to deny someone else a chainsaw. As the names might give away:
(layer-add name) ;; Add a layer to the top of the stack (layer-rem name)
;; Remove a layer by name (noop if no such layer)
To use layer-add
and layer-rem
well, you
should take a moment to think about how to create a layout that will
prevent you from getting into situations where you enter a
key-configuration you cannot get out of again. These two operations
together, however, are very useful for activating a permanent overlay
for a while. This technique is illustrated in the tap-hold overlay a bit
further down.
————————————————————————– |#
(defalias
yah (layer-toggle asking-for-trouble) ;; Completely safe nah (layer-add asking-for-trouble) ;; Completely unsafe
ld1 (layer-delay 500 numbers) ;; One way to get a leader-key ld2 (layer-next numbers) ;; Another way to get a leader key
;; NOTE, this is safe because both qwerty
and
colemak
contain the @tst
;; button which will
get us to the layer-test
layer, which itself contains ;;
both @qwe
and @col
. qwe (layer-switch qwerty)
;; Set qwerty as the base layer col (layer-switch colemak) ;; Set
colemak as the base layer ) (deflayer layer-test @qwe _ _ _ _ _ _ _ _ _ _ @add _ @nah @col _ _ _ _ _ _ _ _ _ _ _ _ @yah _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ @ld1 @ld2 _ _ _ _ _ _ _ )
;; Exactly like qwerty, but with the letters switched around
(deflayer colemak grv 1 2 3 4 5 6 7 8 9 0 - = bspc tab q w f p g j l u y
; [ ]
@xcp a r s t d h n e i o
’ ret @sl z x c v b k m ,
. / @sr lctl @num lalt spc ralt rmet @sym @tst )
(defalias lol #(: - D))
;; Contrived example (deflayer asking-for-trouble @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol )
;; One way to safely use layer-add and layer-rem: the button bound to
layer-add ;; is the same button bound to layer-rem in the layer that
add
adds to the ;; stack. I.e., it becomes impossible to
add or remove multiple copies of a ;; layer. (defalias add (layer-add
multi-overlay) ;; multi-overlay is defined in the next rem (layer-rem
multi-overlay) ;; section below this )
#| ————————————————————————– Optional: Multi-use buttons
Perhaps one of the most useful features of KMonad, where a lot of work has gone into, but also an area with many buttons that are ever so slightly different. The naming and structuring of these buttons might change sometime soon, but for now, this is what there is.
For the next section being able to talk about examples is going to be
handy, so consider the following scenario and mini-language that will be
the same between scenarios. - We have some button foo
that
will be different between scenarios - foo
is bound to ‘Esc’
on the input keyboard - the letters a s d f are bound to themselves - Px
signifies the press of button x on the keyboard - Rx signifies the
release of said button - Tx signifies the sequential and near
instantaneous press and release of x - 100 signifies 100ms pass
So for example: Tesc Ta: tap of ‘Esc’ (triggering foo
),
tap of ‘a’ triggering a
Pesc 100 Ta Tb Resc: press of
‘Esc’, 100ms pause, tap of ‘a’, tap of ‘b’, release of ‘Esc’
The tap-next
button takes 2 buttons, one for tapping,
one for holding, and combines them into a single button. When pressed,
if the next event is its own release, we tap the ‘tapping’ button. In
all other cases we first press the ‘holding’ button then we handle the
event. Then when the tap-next
gets released, we release the
‘holding’ button.
So, using our mini-language, we set foo to: (tap-next x lsft) Then: Tesc -> x Tesc Ta -> xa Pesc Ta Resc -> A Pesc Ta Tr Resc -> AR
The tap-hold
button is very similar to
tap-next
(a theme, trust me). The difference lies in how
the decision is made whether to tap or hold. A tap-hold
waits for a particular timeout, if the tap-hold
is released
anywhere before that moment we execute a tap immediately. If the timeout
occurs and the tap-hold
is still held, we switch to holding
mode.
The additional feature of a tap-hold
is that it pauses
event-processing until it makes its decision and then rolls back
processing when the decision has been made.
So, again with the mini-language, we set foo to: (tap-hold 200 x lsft) ;; Like tap-next, but with a 200ms timeout Then: Tesc -> x Tesc Ta -> xa Pesc 300 a -> A (the moment you press a) Pesc a 300 -> A (after 200 ms) Pesc a 100 Resc -> xa (both happening immediately on Resc)
The tap-hold-next
button is a combination of the
previous 2. Essentially, think of it as a tap-next
button,
but it also switches to held after a period of time. This is useful,
because if you have a (tap-next ret ctl) for example, and you press it
thinking you want to press C-v, but then you change your mind, you now
cannot release the button without triggering a ‘ret’, that you then have
to backspace. With the tap-hold-next
button, you simply
outwait the delay, and you’re good. I see no benefit of
tap-next
over tap-hold-next
with a decent
timeout value.
You can use the :timeout-button
keyword to specify a
button other than the hold button which should be held when the timeout
expires. For example, we can construct a button which types one x when
tapped, multiple x’s when held, and yet still acts as shift when another
button is pressed before the timeout expires. So, using the minilanguage
and foo as: (tap-hold-next 200 x lsft :timeout-button x) Then: Tesc
-> Tx Pesc 100 a -> A (the moment you press a) Pesc 5000 Resc
-> xxxxxxx (some number of auto-repeated x’s)
Note that KMonad does not itself auto-repeat the key. In this last example, KMonad emits 200 Px 4800 Rx, and the operating system’s auto-repeat feature, if any, emits multiple x’s because it sees that the x key is held for 4800 ms.
The tap-next-release
is like tap-next
,
except it decides whether to tap or hold based on the next release of a
key that was not pressed before us. This also performs rollback
like tap-hold
. So, using the minilanguage and foo as:
(tap-next-release x lsft) Then: Tesc Ta -> xa Pa Pesc Ra Resc ->
ax (because ‘a’ was already pressed when we started, so foo decides it
is tapping) Pesc Ta Resc -> A (because a was pressed and
released after we started, so foo decides it is holding)
These increasingly stranger buttons are, I think, coming from the
stubborn drive of some of my more eccentric (and I mean that in the most
positive way) users to make typing with modifiers on the home-row more
comfortable. Especially layouts that encourage a lot of rolling motions
are nicer to use with the release
style buttons.
The tap-hold-next-release
(notice a trend?) is just like
tap-next-release
, but it comes with an additional timeout
that, just like tap-hold-next
will jump into holding-mode
after a timeout.
I honestly think that tap-hold-next-release
, although it
seems the most complicated, probably is the most comfortable to use. But
I’ve put all of them in a testing layer down below, so give them a go
and see what is nice.
————————————————————————– |#
(defalias xtn (tap-next x lsft) ;; Shift that does ‘x’ on tap xth (tap-hold 400 x lsft) ;; Long delay for easier testing thn (tap-hold-next 400 x lsft) tnr (tap-next-release x lsft) tnh (tap-hold-next-release 2000 x lsft)
;; Used it the colemak layer xcp (tap-hold-next 400 esc ctl) )
;; Some of the buttons used here are defined in the next section (deflayer multi-overlay @mt _ _ _ _ _ _ _ _ _ _ _ @rem _ _ _ _ _ _ _ _ _ _ _ _ @thn _ _ _ _ _ _ _ _ _ _ @xtn _ _ _ _ _ _ _ _ _ @xth @tnr _ _ _ _ _ _ @tnh )
#| ————————————————————————– Optional: Multi-tap
Besides the tap-hold style buttons there is another multi-use button
(with. only 1 variant, at the moment). The multi-tap
.
A multi-tap
codes for different buttons depending on how
often it is tapped. It is defined by a series of delays and buttons,
followed by a last button without delay. As long as you tap the
multi-tap
within the delay specified, it will jump to the
next button. Once the delay is exceeded the selected button is pressed.
If the last button in the list is reached, it is immediately pressed.
When another key is pressed down while we’re tapping, `multi-tap’ also
immediately exits and taps the current button.
Note that you can actually hold the button, so in the below example, going: tap-tap-hold (wait 300ms) will get you a pressed c, until you release again.
————————————————————————– |#
(defalias mt (multi-tap 300 a 300 b 300 c 300 d e))
#| ————————————————————————– Optional: Around-next
The around-next
function creates a button that primes
KMonad to perform the next button-press inside some context. This could
be the context of ‘having Shift pressed’ or ‘being inside some layer’
or, less usefully, ‘having d pressed’. It is a more general and powerful
version of layer-next
.
There is also an around-next-timeout
button that does
the same thing as around-next
, except that if some other
button press is not detected within some timeout, some other button is
tapped. This can be used to create a leader-key that simply times out
(by passing a non-button), or a key that can still function as a normal
key, but also as a leader key when used slowly.
I think expansion of this button-style is probably the future of leader-key, hydra-style functionality support in KMonad.
————————————————————————– |#
(defalias ns (around-next sft) ;; Shift the next press nnm (around-next @num) ;; Perform next press in numbers layer ntm (around-next-timeout 500 sft XX)
)
(deflayer around-next-test _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ @ns _ _ _ _ _ _ _ _ _ _ @nnm _ _ _ _ _ _ _ _ _ @ntm _ _ _ _ _ _ )
#| ————————————————————————– Optional: Compose-key sequences
Compose-key sequences are series of button-presses that your operating system will interpret as the insertion of a special character, like accented characters, or various special-languages. In that sense, they are just syntactic sugar for keyboard macros.
To get this to work on Linux you will need to set your compose-key
with a tool like
setxkbmap', as well as tell kmonad that information. See the
defcfg’
block at the top of this file for a working example. Note that you need
to wait ever so slightly for the keyboard to register with linux before
the command gets executed, that’s why the sleep 1
. Also,
note that all the `/run/current-system’ stuff is because the author uses
NixOS. Just find a shell-command that will:
1. Sleep a moment
2. Set the compose-key to your desired key
Please be aware that what setxkbmap' calls the
menu’ key
is not actually the `menu’ key! If you want to use the often
suggested
setxkbmap -option compose:menu
you will have to set your compose key within kmonad to
compose' and not
menu’.
After this, this should work out of the box under Linux. Windows does not recognize the same compose-key sequences, but WinCompose will make most of the sequences line up with KMonad: http://wincompose.info/ This has not in any way been tested on Mac.
In addition to hard-coded symbols, we also provide ‘uncompleted’
macros. Since a compose-key sequence is literally just a series of
keystrokes, we can omit the last one, and enter the sequence for ‘add an
umlaut’ and let the user then press some letter to add this umlaut to.
These are created using the +"
syntax.
————————————————————————– |#
(defalias sym (layer-toggle symbols)
)
(deflayer symbols _ _ _ _ _ _ _ _ _ _ _ _ _ ä é © _ _ _ _ _ _ _ _ _ +’ +~ +` +^ _ _ _ _ _ _ _ +” +, _ _ _ _ _ _ _ _ _ _ _ _ _ _ _)
#| ————————————————————————– Optional: Command buttons
Currently we also provide the ability to launch arbitrary shell-commands from inside kmonad. These commands are simply handed off to the command-shell without any further checking or waiting.
NOTE: currently only tested on Linux, but should work on any platform, as long as the command is valid for that platform.
The `cmd-button’ function takes two arguments, the second one of which is optional. These represent the commands to be executed on pressing and releasing the button respectively.
BEWARE: never run anyone’s configuration without looking at it. You wouldn’t want to push:
(cmd-button "rm -rf ~/*") ;; Delete all this user's data
————————————————————————– |#
(defalias dat (cmd-button “date >> /tmp/kmonad_example.txt”) ;;
Append date to tmpfile pth (cmd-button “echo $PATH >
/tmp/kmonad_path.txt”) ;; Write out PATH ;;
dat' on press and
pth’ on release bth (cmd-button “date
>> /tmp/kmonad_example.txt” “echo $PATH >
/tmp/kmonad_path.txt”) )
(deflayer command-test _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ @dat @pth _ _ _ _ _ _ _ ) `