Colemak

I switched to Colemak a couple of years ago. I love it. Never going back. Not really any faster than QWERTY, but so much more comfortable.

One issue though is whenever I jump on someone else's machine or remote into some server. I want to switch the layout but I don't really want to install anything.

Here's a little seventy-line F# app that will intercept all keys on the system on which it's running and remap to Colemak:

 open System
open System.Diagnostics
open System.Runtime.InteropServices
open System.Windows.Forms
 
 [<Literal>] 
let WH_KEYBOARD_LL = 13
 
 [<StructLayout(LayoutKind.Sequential)>] 
type KBDLLHOOKSTRUCT =  
val vkCode : uint32 
val scanCode : uint32 
val flags : uint32 
val time : uint32 
val dwExtraInfo : nativeint

type LowLevelKeyboardProc = delegate of int * nativeint * KBDLLHOOKSTRUCT -> nativeint
 
 [<DllImport("kernel32.dll", SetLastError = true)>] 
extern nativeint GetModuleHandle(string lpModuleName) 
 
 [<DllImport("user32.dll", SetLastError = true)>] 
extern bool UnhookWindowsHookEx(nativeint hhk) 
 
 [<DllImport("user32.dll", SetLastError = true)>] 
extern nativeint SetWindowsHookEx(int idhook, LowLevelKeyboardProc proc, nativeint hMod, uint32 threadId) 
 
 [<DllImport("user32.dll", SetLastError = true)>] 
extern nativeint CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, KBDLLHOOKSTRUCT lParam) 
 
let SetHook (proc: LowLevelKeyboardProc) = 
 use curProc = Process.GetCurrentProcess () 
 use curMod = curProc.MainModule
 SetWindowsHookEx(WH_KEYBOARD_LL, proc, GetModuleHandle(curMod.ModuleName), 0u) 
 
type App(handler) as x = 
 inherit ApplicationContext() 
 let rec callback (code : int) (wparam : nativeint) (lparam : KBDLLHOOKSTRUCT) : nativeint = 
 match handler code wparam lparam with
  | Some keys -> SendKeys.Send keys; nativeint 1
  | None -> CallNextHookEx(hook, code, wparam, lparam) 
 and proc = new LowLevelKeyboardProc(callback) 
 and hook = SetHook proc
 override x.ExitThreadCore() = 
 UnhookWindowsHookEx(hook) |> ignore
 base.ExitThreadCore() 
 
printfn "Welcome to Colemak!" 
 
Application.Run(new App(fun code wparam lparam -> 
 let key = int >> enum
 if lparam.flags &&& 0b10010000u = 0u then // key down and not injected
 match lparam.vkCode |> key with
  | Keys.D -> Some "s" 
  | Keys.E -> Some "f" 
  | Keys.F -> Some "t" 
  | Keys.G -> Some "d" 
  | Keys.I -> Some "u" 
  | Keys.J -> Some "n" 
  | Keys.K -> Some "e" 
  | Keys.L -> Some "i" 
  | Keys.N -> Some "k" 
  | Keys.O -> Some "y" 
  | Keys.P -> Some ";" 
  | Keys.R -> Some "p" 
  | Keys.S -> Some "r" 
  | Keys.T -> Some "g" 
  | Keys.U -> Some "l" 
  | Keys.Y -> Some "j" 
  | Keys.OemSemicolon -> Some "o" 
  | _ -> None
 else None))