-
-
Save nomand/9bb9fac582e2191685804c2ab96e9916 to your computer and use it in GitHub Desktop.
AutoHotKey script that allows Apple wireless keyboard work in Windows 10 with some layout changes. See header for details and credits to original authors and modders.
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
;=========================================================================================================================================== | |
; Additional Modifications by nomand | |
;========================================================================================================================================== | |
; | |
; - fixed Media_Next functinality | |
; - moved Eject key screenshots to active window screenshot shortcut (Ctrl+F3) | |
; - restored Lctrl <> Fn swap | |
; - restored page up/down, home/end functinality | |
; - F12 Insert remap kept disabled | |
; - cleaned up code indentations and comments | |
; | |
; ISSUES: | |
; Windows interrupts lock up remapped keys (windows dialogue popup will interrupt AHK when crl, fn, eject etc. are held) | |
; Page Up/Page Down/to Start/to End text selections while holding Shift don't work | |
; Some applications (Ableton Live) don't respect AHK at all and talk to the keyboard only through apple designated keys (wtf) | |
; Alt and Command keys need to be remapped to put the "Windows" key where it should be | |
; Any remapping related to Ctrl and Fn swap is only done on the left of the spacebar | |
; Ctrl + Shift key combinations don't work with remapped keys (Fn + Shift) (for example unclose last tab with Ctrl + Shift + T) | |
; A bunch more bugs most likely | |
; | |
; - Previous header follows: | |
; | |
; Apple Wireless Keyboard Eject Key and Function kKey remapper for Windows. | |
; You can get the latest version of this file at: | |
; https://gist.github.com/TheZoc/a913642e9bedf62ba3ef | |
; | |
;========================================================================================================================================== | |
; Please, if you make a significant change, fix or would like to improve this script, | |
; I'd really appreciate if you can contact me so we can merge both works :) | |
;========================================================================================================================================== | |
; | |
; This is a havily modified version of the script: | |
; https://gist.github.com/anonymous/4294405 | |
; | |
; This is a "hack" of the original script with was updated to support 64 bits Windows. | |
; With a file compare tool, it's easy to update the original script, if you prefer it's remaps. | |
; | |
; Changes from the original script: | |
; - Windows x64 support. | |
; - Eject key changed to take screenshots to <user>/Pictures/Screenshots | |
; - Added a "bootcampWindows" config variable, so users in bootcamp can disable some remappings (System volume changes) - untested. | |
; - Disabled fn key + arrow key remapping | |
; - Disabled F12 to Insert Key remapping | |
; - Disabled swapping of the fn key and left Ctrl key. | |
; | |
;========================================================================================================================================== | |
; Original script header follows | |
;========================================================================================================================================== | |
; | |
; AutoHotkey Version: 1.x | |
; Language.........: English | |
; Platform.........: NT/XP/Vista | |
; Author...........: mrBTK | |
; | |
; Script Function..: Make Apple Wireless Keyboard useful in MS Windows: | |
; - EJECT = Delete with repeat deleting on long pressing. Shift-DEL and other combinations works too. | |
; - Swap FN & left Control | |
; - FN-functions оn F3-F12 keys and arrow keys (use new-FN) | |
; - FN+EJECT = drive eject | |
; - F12 = INSERT (on your need, it simple to delete this feature: find in script "switch F12 to Insert" and delete the command) | |
; - AWK POWER button toggle script suspending. Place in script directory two any sound files "on.wav" & "off.wav" for notification | |
; | |
; Based on.........: DLLCall: Support for Human Interface devices: some keymapping code | |
; By...............: Micha | |
; URL..............: http://www.autohotkey.com/forum/viewtopic.php?t=6367 | |
; Based on.........: HID/Extended input devices (MS Natural Keyboard 4000 etc.): HID operations code | |
; By...............: Shaun | |
; URL..............: http://www.autohotkey.com/board/topic/36304-hidextended-input-devices-ms-natural-keyboard-4000-etc/ | |
; | |
; Use..............: Spread the word! Just be sure to credit the writer of the original config | |
; and the authors of this file. | |
; | |
#NoEnv ; Recommended for performance and compatibility with future AutoHotkey releases. | |
#MaxHotkeysPerInterval 1000 | |
#SingleInstance force ; Replace any previous instance | |
DetectHiddenWindows, on | |
OnMessage(0x00FF, "InputMessage") | |
SendMode Input ; Recommended for new scripts due to its superior speed and reliability. | |
SetWorkingDir %A_ScriptDir% ; Ensures a consistent starting directory. | |
;#NoTrayIcon | |
; Set screen title, to set the HWND | |
Gui, Show, x0 y0 h0 w0, AppleWKHelper | |
HWND := WinExist("AppleWKHelper") | |
hidMessage := 0 | |
isSuspend := 0 | |
; Variable for the modifier key | |
fnPressed := 0 | |
fnPrevState := 0 | |
ejPressed := 0 | |
ejPrevState := 0 | |
pwrPressed := 0 | |
pwrPrevState := 0 | |
; Variable for Fn <> Lctrl | |
lctrlPressed := 0 | |
lctrlPrevState := 0 | |
; set this to 0 if you're not running windows on Bootcamp (aka: Normal PC) | |
bootcampWindows := 0 | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
; handle HID input, set global vars, call modKeysProcessing | |
; List all of the "Raw Input" devices available for use and allow | |
; capture of output | |
; | |
; There may be more than one 'raw' device per device actually attached | |
; to the system. This is because these devices generally represent | |
; "HID Collections", and there may be more than one HID collection per | |
; USB device. For example, the Natural Keyboard 4000 supports a normal | |
; keyboard HID collection, plus an additional HID collection that can | |
; be used for the zoom slider and other important buttons | |
SizeofRawInputDeviceList := A_PtrSize * 2 | |
SizeofRawInputDevice := 8 + A_PtrSize | |
RIM_TYPEMOUSE := 0 | |
RIM_TYPEKEYBOARD := 1 | |
RIM_TYPEHID := 2 | |
RIDI_DEVICENAME := 0x20000007 | |
RIDI_DEVICEINFO := 0x2000000b | |
RIDEV_INPUTSINK := 0x00000100 | |
RID_INPUT := 0x10000003 | |
DoCapture := 0 | |
;;get count of HID devices | |
Res := DllCall("GetRawInputDeviceList", "Ptr", 0, "UInt*", Count, UInt, SizeofRawInputDeviceList) | |
VarSetCapacity(RawInputList, SizeofRawInputDeviceList * Count) | |
;;get list of HID devices | |
Res := DllCall("GetRawInputDeviceList", "Ptr", &RawInputList, "UInt*", Count, "UInt", SizeofRawInputDeviceList) | |
rimHIDregistered := 0 | |
Loop %Count% ;for all HID devices | |
{ | |
Handle := NumGet(RawInputList, (A_Index - 1) * SizeofRawInputDeviceList, "UInt") | |
Type := NumGet(RawInputList, ((A_Index - 1) * SizeofRawInputDeviceList) + A_PtrSize, "UInt") | |
if (Type = RIM_TYPEMOUSE) | |
TypeName := "RIM_TYPEMOUSE" | |
else if (Type = RIM_TYPEKEYBOARD) | |
TypeName := "RIM_TYPEKEYBOARD" | |
else if (Type = RIM_TYPEHID) | |
TypeName := "RIM_TYPEHID" | |
else | |
TypeName := "RIM_OTHER" | |
; get HID device name length | |
Res := DllCall("GetRawInputDeviceInfo", "Ptr", Handle, "UInt", RIDI_DEVICENAME, "Ptr", 0, "UInt *", nLength) | |
VarSetCapacity(Name, (nLength + 1) * 2) | |
; get HID device name | |
Res := DllCall("GetRawInputDeviceInfo", "Ptr", Handle, "UInt", RIDI_DEVICENAME, "Str", Name, "UInt*", nLength) | |
; get HID device info | |
Res := DllCall("GetRawInputDeviceInfo", "Ptr", Handle, "UInt", RIDI_DEVICEINFO, "Ptr", 0, "UInt *", iLength) | |
VarSetCapacity(Info, iLength) | |
NumPut(iLength, Info, 0, "UInt") ;Put length in struct RIDI_DEVICEINFO | |
Res := DllCall("GetRawInputDeviceInfo", "Ptr", Handle, "UInt", RIDI_DEVICEINFO, "UInt", &Info, "UInt *", iLength) | |
; Keyboards are always Usage 6, Usage Page 1, Mice are Usage 2, Usage Page 1, | |
; HID devices specify their top level collection in the info block | |
; AWK modifier buttons is separate TYPEHID (rather keyboard standard buttons) | |
if (Type = RIM_TYPEHID) | |
{ | |
Vendor := NumGet(Info, 4 * 2, "UShort") | |
Product := NumGet(Info, 4 * 3, "UShort") | |
Version := NumGet(Info, 4 * 4, "UShort") | |
UsagePage := NumGet(Info, (4 * 5), "UShort") | |
Usage := NumGet(Info, (4 * 5) + 2, "UShort") | |
} | |
VarSetCapacity(RawDevice, SizeofRawInputDevice, 0) | |
NumPut(RIDEV_INPUTSINK, RawDevice, 4) | |
NumPut(HWND, RawDevice, 8) | |
if (Type = RIM_TYPEHID && Vendor = 1452 && rimHIDregistered = 0) ; AWK Vendor number | |
{ | |
rimHIDregistered := 1 | |
NumPut(UsagePage, RawDevice, 0, "UShort") | |
NumPut(Usage, RawDevice, 2, "UShort") | |
; Register AWK modifier buttons HID | |
Res := DllCall("RegisterRawInputDevices", "UInt", &RawDevice, UInt, 1, UInt, SizeofRawInputDevice) | |
if (Res = 0) | |
{ | |
MsgBox, Failed to register for AWK device! | |
ExitApp | |
} | |
} | |
} | |
Count := 1 | |
InputMessage(wParam, lParam, msg, hwnd) | |
{ | |
global hidMessage | |
global isSuspend | |
global RIM_TYPEMOUSE, RIM_TYPEKEYBOARD, RIM_TYPEHID | |
global RID_INPUT | |
; get HID input | |
Res := DllCall("GetRawInputData", "UInt", lParam, "UInt", RID_INPUT, "Ptr", 0, "UInt *", Size, "UInt", 8 + A_PtrSize * 2) | |
VarSetCapacity(Buffer, Size) | |
Res := DllCall("GetRawInputData", "UInt", lParam, "UInt", RID_INPUT, "Ptr", &Buffer, "UInt *", Size, "UInt", 8 + A_PtrSize * 2) | |
Type := NumGet(Buffer, 0, "UInt") | |
if (Type = RIM_TYPEHID) | |
{ | |
SizeHid := NumGet(Buffer, (8+A_PtrSize*2), "UInt") | |
InputCount := NumGet(Buffer, (12+A_PtrSize*2), "UInt") | |
Loop %InputCount% { | |
Addr := &Buffer + (16+A_PtrSize*2) + ((A_Index - 1) * SizeHid) | |
hidMessage := Mem2Hex(Addr, SizeHid) | |
ProcessHIDData(wParam, lParam) | |
} | |
} | |
return | |
} | |
Mem2Hex( pointer, len ) | |
{ | |
multiply := 0x100 | |
Hex := 0 | |
Loop, %len% | |
{ | |
Hex := Hex * multiply | |
Hex := Hex + *Pointer+0 | |
Pointer ++ | |
} | |
Return Hex | |
} | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
; set global vars for further handling | |
ProcessHIDData(wParam, lParam) | |
{ | |
global hidMessage | |
global isSuspend | |
global fnPressed | |
global fnPrevState | |
global ejPressed | |
global ejPrevState | |
global pwrPressed | |
global pwrPrevState | |
SetTimer, SendDelete, Off | |
; Filter bit 5 (Fn key) | |
Transform, FnValue, BitAnd, 0xFF10, hidMessage | |
if (FnValue = 0x1110) ; Fn is pressed | |
{ | |
fnPrevState := fnPressed | |
fnPressed := 1 | |
} | |
else ; Fn is released | |
{ | |
fnPrevState := fnPressed | |
fnPressed := 0 | |
} | |
; Filter bit 4 (Eject key) | |
Transform, FnValue, BitAnd, 0xFF08, hidMessage | |
if (FnValue = 0x1108) ; Eject is pressed | |
{ | |
ejPrevState := ejPressed | |
ejPressed := 1 | |
} | |
else ; Eject is Released | |
{ | |
ejPrevState := ejPressed | |
ejPressed := 0 | |
} | |
; Filter bit 1 fnd 2 (Power key) | |
Transform, FnValue, BitAnd, 0xFF03, hidMessage | |
if (FnValue = 0x1303) ; Power is pressed | |
{ | |
pwrPrevState := 0 | |
pwrPressed := 1 | |
chkSuspend() ; Toggle Suspend of this script. | |
} | |
if (fnValue = 0x1302) ; Power is released | |
{ | |
pwrPrevState := 1 | |
pwrPressed := 0 | |
; no anything doing оn power button release | |
} | |
if (isSuspend = 0) | |
modKeysProcessing() | |
} | |
modKeysProcessing() ; handle keypressing for modifier keys | |
{ | |
global fnPressed | |
global ejPressed | |
global ejPrevState | |
global fnPrevState | |
global lctrlPressed | |
global lctrlPrevState | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
; Eject = delete with delay and repeat | |
if(lctrlPressed = 0) ; eject only pressed | |
{ | |
if(ejPressed = 1 and ejPrevState = 0) | |
{ | |
if(GetKeyState("Shift") or GetKeyState("Alt") or GetKeyState("Control")) | |
{ | |
SendInput {Blind}{Delete} ; Edit::Cut and other w|o repeating | |
} | |
else if(fnPressed = 1) | |
{ | |
SendInput {Ctrl}{Delete} ; ctrl - del | |
} | |
else | |
{ ; No modifiers = Del with repeating | |
SendInput {Delete} | |
SetTimer, SendDelete, -700 ; Delay for start repeating | |
} | |
} | |
} | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
; fn = rCtrl | |
if (ejPressed = 0) ; fn only pressed | |
{ | |
if (fnPressed = 1 and fnPrevState = 0) ; fn down | |
SendInput {rCtrl Down} | |
if (fnPressed = 0 and fnPrevState = 1) ; fn up | |
SendInput {rCtrl Up} | |
} | |
} | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
; Send Delete keystroke repeatedly while Eject still pressed | |
SendDelete: | |
; repeating del while Eject still down | |
if (ejPressed = 1) | |
{ | |
SendInput {delete} | |
SetTimer, SendDelete, -40 | |
} | |
Return | |
chkSuspend() | |
{ | |
global fnPressed | |
global fnPrevState | |
global ejPressed | |
global ejPrevState | |
global lctrlPressed | |
global lctrlPrevState | |
global pwrPressed | |
global pwrPrevState | |
global isSuspend | |
if (isSuspend = 0) | |
{ | |
isSuspend := 1 | |
fnPressed := 0 | |
fnPrevState := 0 | |
ejPressed := 0 | |
ejPrevState := 0 | |
pwrPressed := 0 | |
pwrPrevState := 0 | |
lctrlPressed := 0 | |
lctrlPrevState := 0 | |
Suspend , On | |
SetTimer, SendDelete, Off | |
SendInput {rCtrl Up} | |
TrayTip, AWK Helper, Suspended, 1, 1 | |
Soundplay , off.wav | |
} | |
else | |
{ | |
isSuspend := 0 | |
Suspend , Off | |
TrayTip, AWK Helper, Restored, 1, 1 | |
Soundplay , on.wav | |
} | |
} | |
; switch F12 to Insert | |
;*F12::sendInput {Blind}{Insert} | |
; lctrl = fn | |
; get up and down Lcontrol, sets global variables | |
$*lControl up::LCtrlUp() | |
LCtrlUp() | |
{ | |
global lctrlPressed | |
global lctrlPrevState | |
lctrlPrevState := 1 | |
lctrlPressed := 0 | |
SendInput {F24 up} ; previously {LCtrl up} | |
} | |
Return | |
$*lControl::LCtrlDn() | |
LCtrlDn() | |
{ | |
global lctrlPressed | |
global lctrlPrevState | |
lctrlPrevState := 0 | |
lctrlPressed := 1 | |
SetTimer, SendDelete, Off | |
SendInput {F24 down} ; previously {LCtrl down} | |
} | |
Return | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
; | |
; Fn modifier: PrintScreen, Task Manager | |
; lctrl uses as fn | |
; fn+alt F3 = PrtScr for Active Window | |
$!F3::hotkeyAltF3() | |
hotkeyAltF3() | |
{ | |
global ejPressed | |
global fnPressed | |
if(fnPressed = 1 and ejPressed = 0) | |
{ | |
SendInput !{PrintScreen} ; print screen to clipboard | |
SendInput #{PrintScreen} ; print screen to <user>/Pictures/Screenshots | |
} | |
else | |
SendInput !{F3} | |
} | |
Return | |
; fn+F3 = PrtScr | |
$F3::hotkeyF3() | |
hotkeyF3() | |
{ | |
global ejPressed | |
global fnPressed | |
if(fnPressed = 1 and ejPressed = 0) | |
SendInput {PrintScreen} | |
else | |
SendInput {F3} | |
} | |
Return | |
; fn+F4 = Run TM | |
$F4::hotkeyF4() | |
hotkeyF4() | |
{ | |
global ejPressed | |
global fnPressed | |
if(fnPressed = 1 and ejPressed = 0) | |
SendInput +^{Esc} | |
else | |
SendInput {F4} | |
} | |
Return | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
; | |
; Fn modifier: Audio hotkeys, specified for WMP | |
; WinMPlayer: Previous | |
$F7::hotkeyF7() | |
hotkeyF7() | |
{ | |
global ejPressed | |
global fnPressed | |
if(fnPressed = 1 and ejPressed = 0) | |
SendInput {Media_Prev} | |
else | |
SendInput {F7} | |
} | |
Return | |
; WinMPlayer: Pause/Unpause | |
$F8::hotkeyF8() | |
hotkeyF8() | |
{ | |
global ejPressed | |
global fnPressed | |
if(fnPressed = 1 and ejPressed = 0) | |
SendInput {Media_Play_Pause} | |
else | |
SendInput {F8} | |
} | |
Return | |
; WinMPlayer: Next | |
$F9::hotkeyF9() | |
hotkeyF9() | |
{ | |
global ejPressed | |
global fnPressed | |
if(fnPressed = 1 and ejPressed = 0) | |
SendInput {Media_Next} | |
else | |
SendInput {F9} | |
} | |
Return | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
; | |
; Fn modifier: Audio hotkeys, system volume | |
if (bootcampWindows = 0) | |
{ | |
; System volume: Mute/Unmute | |
$F10::hotkeyF10() | |
hotkeyF10() { | |
global ejPressed | |
global fnPressed | |
if(fnPressed = 1 and ejPressed = 0) | |
SendInput {Volume_Mute} ; Mute/unmute the master volume. | |
else | |
SendInput {F10} | |
} | |
Return | |
; System volume: Volume Down | |
$F11::hotkeyF11() | |
hotkeyF11() | |
{ | |
global ejPressed | |
global fnPressed | |
if(fnPressed = 1 and ejPressed = 0) | |
SendInput {Volume_Down} ; Lower the master volume by 1 interval (typically 5%) | |
else | |
SendInput {F11} | |
} | |
Return | |
; System volume: Volume Down | |
$F12::hotkeyF12() | |
hotkeyF12() | |
{ | |
global ejPressed | |
global fnPressed | |
if(fnPressed = 1 and ejPressed = 0) | |
SendInput {Volume_Up} ; Raise the master volume by 1 interval (typically 5%). | |
else | |
SendInput {F12} | |
} | |
Return | |
} | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
; Fn modifier: Arrow keys | |
; Page Up | |
$UP::hotkeyPgUp() | |
hotkeyPgUp() | |
{ | |
global ejPressed | |
global fnPressed | |
if(fnPressed = 1 and ejPressed = 0) | |
{ | |
if (GetKeyState("Shift")) | |
SendInput {rCtrl Up}+{PgUp} | |
else | |
SendInput {rCtrl Up}{PgUp} | |
} else | |
SendInput {UP} | |
} | |
Return | |
; Page Down | |
$Down::hotkeyPgDn() | |
hotkeyPgDn() | |
{ | |
global fnPressed | |
global ejPressed | |
global ejPrevState | |
global fnPrevState | |
global fnPressed | |
if(fnPressed = 1 and ejPressed = 0) | |
SendInput {rCtrl Up}{PgDn} | |
else | |
SendInput {Down} | |
} | |
Return | |
; Home | |
$Left::hotkeyHome() | |
hotkeyHome() | |
{ | |
global ejPressed | |
global fnPressed | |
if(fnPressed = 1 and ejPressed = 0) | |
SendInput {rCtrl Up}{Home} | |
else | |
SendInput {Left} | |
} | |
Return | |
; End | |
$Right::hotkeyEnd() | |
hotkeyEnd() | |
{ | |
global ejPressed | |
global fnPressed | |
if(fnPressed = 1 and ejPressed = 0) | |
SendInput {rCtrl Up}{End} | |
else | |
SendInput {Right} | |
} | |
Return |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Any way can disable the Random window?