Skip to content

Instantly share code, notes, and snippets.

@DavidBevi
Last active September 30, 2025 15:11
Show Gist options
  • Select an option

  • Save DavidBevi/43cccde5b282a92d970249f2895126d5 to your computer and use it in GitHub Desktop.

Select an option

Save DavidBevi/43cccde5b282a92d970249f2895126d5 to your computer and use it in GitHub Desktop.
AHKv2, Touch Emulator
; Touch Emulator by DavidBevi https://gist.github.com/DavidBevi/43cccde5b282a92d970249f2895126d5
; Hold ENABLER_KEY to tranform mouse-mov into touch-mov
#Requires AutoHotkey v2.0+
#SingleInstance Force
CoordMode("Mouse")
; Globals -----------------------------------------------------------------------------------------
ENABLER_KEY:="Home" ;<--- EDIT THIS LINE TO CHANGE ENABLER_KEY
STATE:="down"
; Hotkeys -----------------------------------------------------------------------------------------
Esc::(A_ThisHotkey=A_PriorHotkey and A_TimeSincePriorHotkey<200)? Reload(): {}
^!Esc:: MouseHook(), ExitApp()
Hotkey(ENABLER_KEY,(*)=>{})
; Main --------------------------------------------------------------------------------------------
; Initialize touch injection
DllCall("InitializeTouchInjection", "UInt",1, "UInt",0x2, "Int")?
(ToolTip("VirtualTouch started."), SetTimer(ToolTip,-1200)) :
(MsgBox("InitializeTouchInjection failed. Retry?",,"RC")="Retry"? Reload(): ExitApp())
; Install low-level mouse hook callback (fast)
MouseHook("install")
OnExit(MouseHook)
; Install message handler (in main thread)
OnMessage(0x47b, Router)
; Functions ---------------------------------------------------------------------------------------
; Mouse Hook manager
MouseHook(p*) {
If p[1]="install" {
Static pCallback:=CallbackCreate(MouseFilter,"",3)
pCallback? {}: MsgBox("CallbackCreate failed.")
Static hHook:=DllCall("SetWindowsHookEx", "Int",14, "Ptr",pCallback, "Ptr",0, "UInt",0, "Ptr")
hHook? {}: (CallbackFree(pCallback), MsgBox("SetWindowsHookEx failed. Try running as admin."))
} Else Try (DllCall("UnhookWindowsHookEx", "Ptr",hHook), CallbackFree(pCallback))
}
; Low-level mouse callback manager (in hook thread)
MouseFilter(nCode, wParam, lParam) {
Global STATE
Static ox:=0, oy:=0
;;
P:=GetKeyState(ENABLER_KEY,"P")
STATE="null" ? (P? STATE:="down" : {} ):
STATE="down" ? (P? STATE:="update": STATE:="up"):
STATE="update"? (P? {} : STATE:="up"):
STATE="up" ? STATE:="null" : MsgBox("Invalid STATE")
;;
If STATE="null" {
ox:=NumGet(lParam+0,0,"Int"), oy:=NumGet(lParam+0,4,"Int") ; Cache 1st touch
} Else If !nCode && wParam=512 {
dx:=ox-NumGet(lParam+0,0,"Int"), dy:=oy-NumGet(lParam+0,4,"Int") ; Calc delta
DllCall("PostMessage", "Ptr",0, "UInt",0x47b, "Ptr",dx, "Ptr",dy) ; Fwd→ Router
Return(1) ; Non-zero blocks mouse
}
; Fwd→ Windows
Return DllCall("CallNextHookEx", "Ptr",0, "Int",nCode, "Ptr",wParam, "Ptr",lParam, "Ptr")
}
; Filtered mouse-events manager (in main thread)
Router(dx, dy, msg, hwnd) {
Static ox:=0, oy:=0
STATE="down"? (MouseGetPos(&x,&y), ox:=x, oy:=y): {}
SendTouch(STATE, ox-=dx, oy-=dy)
}
; Touch injector
SendTouch(type, x, y) {
flag:= type="down"?0x10006: type="update"?0x20006: type="up"?0x40000: 0
If !flag
Return
; Write msg in a buffer (buf)
Static buf:=Buffer(A_PtrSize=4?136:144, 0)
NumPut("UInt",0x2, buf)
NumPut("UInt",flag, buf,12), NumPut("Int",x, "Int",y, buf,32+(A_PtrSize-8)*2)
NumPut("UInt",1, "Int",x-2, "Int",y-2, "Int",x+2, "Int",y+2, buf,100+(A_PtrSize-8)*2)
; Send buf, show error in tooltip
If !DllCall("InjectTouchInput", "UInt",1, "Ptr",buf, "Int") {
If type!="up" && (err:=DllCall("GetLastError", "UInt"))=87
ToolTip("SendTouch(" type ") err" err), SetTimer(ToolTip,-2000)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment