Last active
August 22, 2026 20:36
-
-
Save ekinertac/f358732bd7387ae96c8dd99ff97d6a26 to your computer and use it in GitHub Desktop.
macOS muscle memory on Windows: the AutoHotkey layer + the SharpKeys scancode map. Cmd+arrows navigation, Cmd+Q, Finder reflexes in Explorer, and the left-vs-right Ctrl trick that stops Cmd+C from killing your dev server in a terminal.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #Requires AutoHotkey v2.0+ | |
| appList := ["blender.exe", "photoshop.exe",] ; Add apps here | |
| SetTimer(CheckApps, 500) | |
| CheckApps() { | |
| for app in appList { | |
| if WinActive("ahk_exe " app) { | |
| if !A_IsSuspended | |
| Suspend(true) | |
| return | |
| } | |
| } | |
| if A_IsSuspended | |
| Suspend(false) | |
| } | |
| ; Recommended settings for performance and compatibility | |
| SetWorkingDir(A_ScriptDir) ; Ensure consistent starting directory | |
| #SingleInstance Force ; Prevent multiple script instances | |
| ; Remap Alt + Tab to send "Insert" key | |
| !Tab::SendInput("{Insert}") | |
| ; Command-arrow navigation | |
| ^Left::SendInput("{Home}") | |
| ^Right::SendInput("{End}") | |
| ^Up::SendInput("^{Home}") | |
| ^Down::SendInput("^{End}") | |
| ; Command-Shift-arrow navigation + highlight | |
| ^+Left::SendInput("+{Home}") | |
| ^+Right::SendInput("+{End}") | |
| ^+Up::SendInput("^+{Home}") | |
| ^+Down::SendInput("^+{End}") | |
| ; Option-arrow navigation | |
| !Left::SendInput("^{Left}") | |
| !Right::SendInput("^{Right}") | |
| ; Option-Shift-arrow navigation + highlight | |
| !+Left::SendInput("^+{Left}") | |
| !+Right::SendInput("^+{Right}") | |
| ; Alt + Backspace → Delete Word | |
| !Backspace::SendInput("^{Backspace}") | |
| ; Cmd+Opt+Space -> Windows emoji picker (Win + .). This was on Cmd+Space, | |
| ; which swallowed Ctrl+Space globally and killed IntelliSense in every editor. | |
| ^!Space::SendInput("#.") | |
| ; Cmd+Opt+Left/Right -> previous / next tab, matching Chrome on macOS. | |
| ; SharpKeys makes the physical Ctrl key emit Win, so a literal Ctrl+Tab is | |
| ; untypeable on this machine. Ctrl+PgUp/PgDn is the chord every browser and | |
| ; editor accepts, and it avoids colliding with the app switcher on Ctrl+Tab. | |
| ^!Left::SendInput("^{PgUp}") | |
| ^!Right::SendInput("^{PgDn}") | |
| ; Cmd+Shift+[ / ] -> previous / next tab. The other chord macOS gives you for | |
| ; tab switching (Safari, Chrome, Firefox, Terminal all take it). Windows binds | |
| ; nothing to Ctrl+Shift+brackets, so these presses were falling on the floor. | |
| ; | |
| ; Same target as the Cmd+Opt+arrows above, deliberately: two macOS chords, one | |
| ; Windows behaviour, so whichever one the fingers reach for works. | |
| ^+[::SendInput("^{PgUp}") | |
| ^+]::SendInput("^{PgDn}") | |
| ; Backtick where macOS puts it. This board is ISO and puts grave on the key LEFT | |
| ; OF Z (sc029); the key left of 1 is the ISO 102nd key (sc056) and types \, as | |
| ; does the key near Enter (sc02B). Point sc056 at grave so the top-left key does | |
| ; what it does on a Mac. Nothing else moves: sc02B still gives \, and sc029 is | |
| ; untouched so AltAppSwitcher keeps Cmd+` window cycling (it is bound to 41 | |
| ; decimal = 0x29). | |
| ; | |
| ; Remap syntax (a::b), NOT SendInput. A SendInput pair here caused a hotkey loop: | |
| ; sending ` fired the ` hotkey which sent \ which fired this one again. Remaps | |
| ; run at send level 0 so they cannot retrigger, and Shift is passed through | |
| ; automatically, which is what makes Shift+key produce ~ without a second line. | |
| SC056::SC029 | |
| ; Command + Q → Alt + F4 (Quit application) | |
| ^q::SendInput("!{F4}") | |
| ; **Turkish Character Mappings with Alt** | |
| !c::SendInput("ç") | |
| !+c::SendInput("Ç") | |
| !s::SendInput("ş") | |
| !+s::SendInput("Ş") | |
| !i::SendInput("ı") | |
| !+i::SendInput("İ") | |
| !u::SendInput("ü") | |
| !+u::SendInput("Ü") | |
| !o::SendInput("ö") | |
| !+o::SendInput("Ö") | |
| !g::SendInput("ğ") | |
| !+g::SendInput("Ğ") | |
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
| ;;;;; Cmd+Tab ;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
| ; Cmd+Tab is NOT handled here. AltAppSwitcher owns it (hold key = left control, | |
| ; which is what SharpKeys makes the Cmd-position key emit). | |
| ; | |
| ; There used to be a ^Tab / ^+Tab block here that faked a held Alt+Tab. It fought | |
| ; AltAppSwitcher for the same chord, and it was slow by construction: a hardcoded | |
| ; Sleep(100) before it even started watching for the modifier release, then a 10ms | |
| ; polling loop. Two low-level hooks on one chord is also how keypresses get eaten. | |
| ; Recover it from git history if AltAppSwitcher is ever dropped for good. | |
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
| ;;;;; Screenshots -- owned by ShareX ;;;;; | |
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
| ; NOT bound here, and deliberately so. ShareX already holds these, and because | |
| ; SharpKeys makes the Cmd-position key emit Ctrl, what ShareX registers as | |
| ; Ctrl+Shift+N is what the fingers press as Cmd+Shift+N. macOS parity for free: | |
| ; | |
| ; Cmd+Shift+3 -> Capture entire screen | |
| ; Cmd+Shift+4 -> Capture region | |
| ; Cmd+Shift+5 -> Start/stop screen recording | |
| ; | |
| ; Routing these through AHK would mean intercepting the chord and synthesizing a | |
| ; different one for ShareX to catch: an extra hook and an extra failure mode for | |
| ; no behavioural gain. Listed here only so this file is a complete map of the | |
| ; keyboard, including the parts it does not implement. | |
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
| ;;;;; Finder Navigation Emulation ;;;;;;; | |
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
| #HotIf WinActive("ahk_class CabinetWClass") ; Detects File Explorer correctly | |
| ; Cmd + Up → Go Up One Folder Level | |
| ^Up::SendInput("!{Up}") ; Alt + Up (Go Up) | |
| ; Cmd+Delete → move to Recycle Bin. The two platforms are exact opposites here: | |
| ; in Finder, plain Delete does nothing and Cmd+Delete trashes; on Windows plain | |
| ; Delete trashes and Ctrl+Delete does nothing. This points the reflex at the key | |
| ; that actually works. | |
| ; | |
| ; Explorer-scoped deliberately. Globally, Ctrl+Delete is delete-word-forward in | |
| ; every text field on the system, which is not worth trading away. | |
| ^Delete::SendInput("{Delete}") | |
| ; Cmd+I → Get Info, i.e. the properties dialog (Alt+Enter). | |
| ; | |
| ; Also Explorer-scoped deliberately, and this one matters more: Ctrl+I is italic | |
| ; in every editor and text field on BOTH platforms. Binding it globally would | |
| ; break italic everywhere to fix one Finder shortcut. | |
| ^i::SendInput("!{Enter}") | |
| #HotIf ; Disable hotkeys outside Explorer | |
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
| ;;;;; WezTerm: copy vs interrupt ;;;;;;;;; | |
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
| ; In a terminal Cmd+C must copy and Ctrl+C must interrupt, and on Windows those | |
| ; are the same key: SharpKeys sends both the Cmd-position key and Caps Lock | |
| ; through as Ctrl, so WezTerm receives one indistinguishable Ctrl+C. It has no | |
| ; way to separate them (its mods vocabulary is just CTRL, not LEFT/RIGHT_CTRL -- | |
| ; that is an unmerged upstream PR). AHK can, because <^ matches left Ctrl only | |
| ; and >^ matches right Ctrl only. | |
| ; | |
| ; Caps now emits RIGHT Ctrl, see windows/scancode-map.reg. So in WezTerm: | |
| ; Cmd+C = left Ctrl -> Shift is added, making WezTerm's built-in Ctrl+Shift+C | |
| ; Caps+C = right Ctrl -> not matched here, reaches the pty as a real ^C | |
| ; | |
| ; {Blind} matters: it leaves the physically-held Ctrl alone instead of releasing | |
| ; and re-pressing it, which would desync the modifier state on key repeat. | |
| #HotIf WinActive("ahk_exe wezterm-gui.exe") | |
| <^c::SendInput("{Blind}{Shift down}c{Shift up}") | |
| #HotIf | |
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
| ;;;;; IrfanView: Cmd+W closes ;;;;;;;;;;; | |
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
| ; Cmd+W is "close the thing I am looking at" everywhere on macOS. IrfanView | |
| ; instead uses Ctrl+W for fit-window-to-image, so the reflex blows the picture | |
| ; up to fullscreen rather than closing it. Its actual close key is Esc. | |
| ; | |
| ; Scoped by exe rather than window class so it still applies once IrfanView has | |
| ; gone fullscreen, which spawns a different window. | |
| #HotIf WinActive("ahk_exe i_view64.exe") | |
| ^w::SendInput("{Esc}") | |
| #HotIf | |
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
| ;;;;; Screensaver Activation ;;;;;;;;;;;; | |
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
| ; Win + S → Activate Screensaver | |
| #s::{ | |
| ; Send WM_SYSCOMMAND with SC_SCREENSAVE to activate screensaver | |
| SendMessage(0x0112, 0xF140, 0, , "Program Manager") | |
| } | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Windows Registry Editor Version 5.00 | |
| ; Keyboard scancode map for the Windows box. This is what SharpKeys writes; the | |
| ; registry value is the real config, SharpKeys is just a GUI over it. | |
| ; | |
| ; Layout of the value: 8 zero bytes, then a little-endian mapping count (entries | |
| ; + 1 for the null terminator), then 4 bytes per mapping as | |
| ; [target scancode LE][source scancode LE], then 4 zero bytes. | |
| ; | |
| ; Mappings below (3 + terminator, so count = 04): | |
| ; 1D E0 3A 00 Caps Lock (0x3A) -> RIGHT Ctrl (0xE01D) | |
| ; 5B E0 1D 00 Left Ctrl (0x1D) -> Left Win (0xE05B) | |
| ; 1D 00 5B E0 Left Win (0xE05B) -> LEFT Ctrl (0x1D) | |
| ; | |
| ; Why Caps goes to RIGHT Ctrl: the Cmd-position key already emits LEFT Ctrl, and | |
| ; mapping Caps to left Ctrl too made the two keys byte-identical, so no | |
| ; application could ever tell them apart. That is why Cmd+C could not be | |
| ; separated from Ctrl+C in WezTerm. Both still behave as Ctrl everywhere in | |
| ; Windows; they are now merely distinguishable, which lets AHK route Cmd+C to | |
| ; copy while Caps+C stays a real interrupt. | |
| ; | |
| ; Takes effect after a reboot: the keyboard class driver reads this at boot. | |
| [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layout] | |
| "Scancode Map"=hex:00,00,00,00,00,00,00,00,04,00,00,00,1d,e0,3a,00,5b,e0,1d,00,1d,00,5b,e0,00,00,00,00 | |
| ; --------------------------------------------------------------------------- | |
| ; ROLLBACK: the value in place before 2026-08-21 (Caps -> LEFT Ctrl). To go | |
| ; back, replace the line above with this one and re-apply, then reboot. | |
| ; "Scancode Map"=hex:00,00,00,00,00,00,00,00,04,00,00,00,1d,00,3a,00,5b,e0,1d,00,1d,00,5b,e0,00,00,00,00 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment