Skip to content

Instantly share code, notes, and snippets.

@ekinertac
Last active August 22, 2026 20:36
Show Gist options
  • Select an option

  • Save ekinertac/f358732bd7387ae96c8dd99ff97d6a26 to your computer and use it in GitHub Desktop.

Select an option

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.
#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")
}
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