Keyboard mapping by using kanata

Last modified:

Introduction

Keyboard remapping can increase your productivity. Kanata is a tool which works on all platforms. It isn't too hard to setup, and is quite powerful.

JumpApp

JumpApp is a popular Linux util to run or raise an application. When triggered, it launches an application if it's not already running; if it is, it cycles through its windows. It is much faster than using Alt-Tab.

This can be configured in a specific keyboard layer using Kanata. The configuration that I have listed here is made for windows, but it can easily work on Linux if the Linux JumpApp is used instead.

Kanata configuration

Kanata comes in various editions, but for my purposes, I require the one with command support. I require something similar to JumpApp which is a command. Kanata configurations should be saved as .kbd files.

Download Kanata
(defcfg danger-enable-cmd yes ;; Allow commands for JumpApp
  process-unmapped-keys yes ;; I do not see issues when enabling this
  log-layer-changes no ;; I run this hidden, logs have no use for me
)

;; Variables
(defvar
  user-profile %UserProfile%
  ahk-executables-path (concat user-profile "\Documents\AutoHotKey\functions")
  run-or-raise-path (concat ahk-executables-path \run-or-raise.exe)
  toggle-maximize-path (concat ahk-executables-path \toggle-maximize.exe)
)

;; Defsrc contains the original unmapped keyboard layout. Often this is qwerty. Although at home I use dvorak. It is possible to only include the keys that you want to remap.
(defsrc
  esc  f1   f2   f3   f4   f5   f6   f7   f8   f9   f10  f11  f12 
  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 rctl
)

;; The first layer defined is the layer that will be active by default when kanata starts up. I like dvorak, but it is not mandatory.
(deflayer dvorak
  esc  f1   f2   f3   f4   f5   f6   f7   f8   f9   f10  f11  f12
  @os1 8    7    3    4    5    6    2    1    9    0    [    ]    bspc
  tab  '    ,    .    p    y    f    g    c    r    l    /    =    \
  caps a    o    e    u    i    d    h    t    n    s    -    ret
  @lsft ;    q    j    k    x    b    m    w    v    z    @rsft
  @lctl lmet @lalt           spc            @ralt rmet @rctl
)
;; Notice the keys with "@" those are aliases.

;; Since other people often have to use my work laptop I have a way to quickly set the keyboard to a qwerty layer.
(deflayer qwerty
  esc  f1   f2   f3   f4   f5   f6   f7   f8   f9   f10  f11  @dvk
  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 rctl
)

 ;; This is my layer for shortcuts. The jump prefix is given for the JumpApp functionality.
(deflayer shortcuts
  esc  @maximize   f2   f3   f4   f5   f6   f7   f8   f9   f10  f11  @qwr
  @os1 1    2    3    4    5    6    7    8    9    0    [    ]    bspc
  tab  '    ,    .    p  y  @jump-winscp   grv   c    lrld    l    /    =    \
  caps a    @jump-outlook   @jump-emacs    u    i    d    h    @jump-terminal   n    s    -    ret
  @lsft ;    q    j    k    x  @jump-brave  m    w    v    z    @rsft
  @lctl lmet @lalt           spc            @ralt rmet @rctl
)
;; lrld is a default shortcut to reload the configuration 

;; Aliases
(defalias dvk (layer-switch dvorak)
  qwr (layer-switch qwerty)
  ;; os1 switches to my shortcut layer until the next key is pressed, or until it times out
  os1 (one-shot 1500 (layer-while-held shortcuts))
  ;; Sticky keys - You do no longer have to hold both keys, but get a little bit of time to press both, which is better for your fingers.
  lsft (one-shot 1500 lsft)
  rsft (one-shot 1500 rsft)
  lctl (one-shot 1500 lctl)
  rctl (one-shot 1500 rctl)
  lalt (one-shot 1500 lalt)
  ralt (one-shot 1500 ralt)
  ;; Windows menu does not open well, if these are sticky
  ;; lmet (one-shot 1500 lmet)
  ;; rmet (one-shot 1500 rmet)
)

;; Commands
(defalias
  maximize (cmd cmd.exe /c $toggle-maximize-path)
  jump-brave (cmd cmd.exe /c $run-or-raise-path "brave.exe" "%ProgramFiles%\BraveSoftware\Brave-Browser\Application\brave.exe")
  jump-emacs (cmd cmd.exe /c $run-or-raise-path "emacs.exe" "%ProgramFiles%\Emacs\emacs-29.2\bin\runemacs.exe -c -n -a ''")
  jump-outlook (cmd cmd.exe /c $run-or-raise-path "OUTLOOK.EXE" "%ProgramFiles(x86)%\Microsoft Office\root\Office16\OUTLOOK.EXE")
  jump-terminal (cmd cmd.exe /c $run-or-raise-path "WindowsTerminal.exe" "wt.exe")
  jump-winscp (cmd cmd.exe /c $run-or-raise-path "WinSCP.exe" "%LocalAppData%\Programs\WinSCP\WinSCP.exe")
)

JumpApp in AutoHotKey

The above code won't be useful without my JumpApp executables. I compiled them with AutoHotKey. On Linux JumpApp can be used instead.

JumpApp has two arguments. The first one is the window class to cycle through, and the second one is the path to launch the application. AutoHotKey comes with "Window Spy", a tool which is able to show you the window class of an application.

These are the two scripts I wrote and compiled:

; JumpApp AHK script
#Requires AutoHotkey v2.0

; Environment variables (for run or raise) - has to be at top of the file
global ProgramFilesx64 := EnvGet("ProgramFiles")
global ProgramFilesx86 := EnvGet("ProgramFiles(x86)")
global LocalAppData := EnvGet("LocalAppData")

If (A_Args.Length == 2)
{
   RunOrRaise(A_Args[1], A_Args[2])
}

ReplaceEnvironmentVariables(input) {
   output := StrReplace(input, "%ProgramFiles%", ProgramFilesx64)
   output := StrReplace(input, "%ProgramFiles(x86)%", ProgramFilesx64)
   output := StrReplace(input, "%LocalAppData%", LocalAppData)
   return output
}

RunOrRaise(classInput, executablePathInput)
{
   class := ReplaceEnvironmentVariables(classInput)
   executablePath := ReplaceEnvironmentVariables(executablePathInput)
   windowClass := "ahk_exe " . class
   If WinExist(windowClass)
   {
      If WinActive(windowClass) 
      {
         CurrentActive := WinGetClass("A")
		 Instances := WinGetCount("ahk_class " CurrentActive)
		 If (Instances > 1)
		 {
			WinMoveBottom("A")
			WinActivate(windowClass)
		 }
         WinActivate("ahk_class " CurrentActive)
      }
      Else 
      {
         WinActivate(windowClass)
      }
   }
   Else {
      Run executablePath
   }
}
; Maximize toggle AHK
#Requires AutoHotkey v2.0

WinState := WinGetMinMax("A")
If (WinState = 1)
{
  WinRestore("A")
}
Else
{
  WinMaximize("A")
}

Automatically start kanata

Kanata will be set to run automatically at the next startup of your system after running this PowerShell script as an administrator. However, you can also test it locally by executing the command found in the Value field within PowerShell.

The script assumes that you have a folder named "Kanata" located under your documents. The folder should contain the executable and the configuration file.

# $env:Temp gave me a shortened incorrect path, so I use the dotnet variant
$KanataDirectoryPath = "$env:UserProfile\Documents\Kanata"
New-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Run" -Name "Kanata" -Value "`"$KanataDirectoryPath\kanata_cmd_allowed.exe`" -ExecutionPolicy Unrestricted -WindowStyle Hidden -ArgumentList `"--cfg $KanataDirectoryPath\kanata.kbd`" >> `"$([System.IO.Path]::GetTempPath())StartupLog.txt`""

Windows Defender incorrectly reports the "cmd allowed" executable as malware. You can exclude it like this:

Add-MpPreference -ExclusionPath "$env:UserProfile\Documents\Kanata\kanata_cmd_allowed.exe"

Workflow

I often prefer to have all my windows maximized. After launching an application, I typically maximize its window. When I need to access a specific window, I use the jump shortcut, which is notably faster than using Alt-Tab or the Windows search feature.

While this method works well for me. Using grv to toggle layers may not be the best choice. My shortcuts layer provides a way to type that character, but I need it rather often. This config should be enough to get you going.

Additionally, I have an AutoHotKey script that remaps CapsLock to Home when Emacs is open. This is necessary for my xah-fly-config setup. While simple key remapping can be accomplished with Windows PowerToys, I prefer using AutoHotKey since I already use it. You can find this script in my Xah Fly Keys and my Emacs config article.

Share

Diaspora X Facebook LinkedIn

Donate