User Tools

Site Tools


using_autohotkey_to_make_a_second_mouse_into_a_multimedia_control

Using AutoHotkey to make a second mouse into a multimedia control

Using a rather fancy AutoHotKey recipe, we can use an extra mouse hooked to the system to act as a multimedia report (or whatever we want). This is a bit tricky, since Windows treats all mice as the same by default. To get around this, we use the Microsoft API for raw input.

This was inspired by (and somewhat copied from) EitherMouse and this guy's forum post. Note that this does NOT use Michael Simon's AutoHotKey HID support package.

To use, install the code below in AutoHotKey. Press Ctrl+Win+M then wiggle/click the "target mouse" to find out its name. Plug the name into the 'target_mouse' line. You can then change what each button does by editing the Send commands in WM_INPUT.

The code works by:

  1. Hooking all mouse events into WM_INPUT via RegisterRawInputDevices API.
  2. Using the GetRawInputData to get the call detail, and GetRawInputDeviceInfo to find the name of the mouse responsible.
  3. If the target mouse sent the event, then we (1) squelch the next mouse event that would be caught by the normal Windows event system, and (2) send another key sequence instead.
  4. Mouse squelching is done by hooking all the button events using normal AutoHotKey syntax, then forwarding the event if and only if squelch_mouse is off. If squelch_mouse is on, it is turned off by the event, so subsequent clicks WILL be received (e.g. from the regular mouse).

This supresses the button events – to supress the cursor movement, I suggest masking tape.

; === Using AutoHotkey to make a second mouse into a multimedia control ===
;
; Using a rather fancy AutoHotKey recipe, we can use an extra mouse hooked to the system 
; to act as a multimedia report (or whatever we want). This is a bit tricky, since 
; Windows treats all mice as the same by default. To get around this, we use the 
; Microsoft API for raw input.
;
; This was inspired by (and somewhat copied from) EitherMouse and this guy's forum post. 
; Note that this does NOT use Michael Simon's AutoHotKey HID support package.
;
; To use, install the code below in AutoHotKey. Press Ctrl+Win+M then wiggle/click the 
; "target mouse" to find out its name. Plug the name into the 'target_mouse' line. You 
; can then change what each button does by editing the Send commands in WM_INPUT.
;
; The code works by:
;
; 1. Hooking all mouse events into WM_INPUT via RegisterRawInputDevices API.
; 2. Using the GetRawInputData to get the call detail, and GetRawInputDeviceInfo to find the 
;    name of the mouse responsible.
; 3. If the target mouse sent the event, then we (1) squelch the next mouse event that would 
;    be caught by the normal Windows event system, and (2) send another key sequence instead.
; 4. Mouse squelching is done by hooking all the button events using normal AutoHotKey syntax, 
;    then forwarding the event if and only if squelch_mouse is off. If squelch_mouse is on,
;    it is turned off by the event, so subsequent clicks WILL be received (e.g. from the 
;    regular mouse).
;
; This supresses the button events -- to supress the cursor movement, I suggest masking tape.
;
RegisterRawInputDevice(1, 2, A_ScriptHWnd) ; Listen to all mouse events

target_mouse := "\\?\HID#VID_046D&PID_C52F&MI_00#8&569199f&0&0000#{378de44c-56ef-11d1-bc8c-00a0c91405dd}"

; Wrapper for the RegisterRawInputDevices API call
RegisterRawInputDevice(UsagePage, Usage, hwnd)
{
	VarSetCapacity(dev, 12, 0)
	NumPut(UsagePage,   dev, 0, "UShort")
	NumPut(Usage,	   dev, 2, "UShort")
	NumPut(0x100,	   dev, 4) ; dwFlags = RIDEV_INPUTSINK (don't require foreground)
	NumPut(hwnd,		dev, 8)
	
	if DllCall("RegisterRawInputDevices"
		, "uint", &dev  ; pRawInputDevices (pointer to an array of RAWINPUTDEVICE)
		, "uint", 1	 ; uiNumDevices
		, "uint", 12)   ; cbSize (size of a RAWINPUTDEVICE structure)
		OnMessage(0xFF, "WM_INPUT")
}

; Ctrl+Win+M -- find out the target mouse's name for pasting at the top of this script.
#^m::
	global report_mouse
	MsgBox, % "Click OK, then click or wiggle the mouse you wish to capture to get it's name."
	report_mouse:=1

squelch_mouse:=0 ; Silently eat the next mouse event (used so our target mouse doesn't act like an actual mouse, while the normal mice are unaffected)

; API hook for raw device events
WM_INPUT(wParam, lParam)
{
	Critical
	Global target_mouse, squelch_mouse
	
	; Get required buffer size. -- http://msdn.microsoft.com/en-us/library/windows/desktop/ms645596(v=vs.85).aspx
	DllCall("GetRawInputData", "uint", lParam, "uint", 0x10000003, "uint", 0, "uint*", size, "uint", 16)

	VarSetCapacity(raw, size, 0)
	
	; Get raw input data from handle (lParam) -- http://msdn.microsoft.com/en-us/library/windows/desktop/ms645596(v=vs.85).aspx
	ret := DllCall("GetRawInputData", "uint", lParam, "uint", 0x10000003, "uint", &raw, "uint*", size, "uint", 16, "int")
	
	if (ErrorLevel or ret = -1)
		return

	type := NumGet(raw, 0) ; -- http://msdn.microsoft.com/en-us/library/windows/desktop/ms645571(v=vs.85).aspx	
	
	; http://msdn.microsoft.com/en-us/library/windows/desktop/ms645581(v=vs.85).aspx
	if (type != 0) ; RIM_TYPEMOUSE -- ignore non-mouse events (though we shouldn't get any) 
		return
		 
	hDevice := NumGet(raw, 8) ; -- http://msdn.microsoft.com/en-us/library/windows/desktop/ms645571(v=vs.85).aspx
	name := get_mouse_name(hDevice) ; returns NULL if from a simulated source
	
	if (name and report_mouse) {
		MsgBox, Paste the following at the top of the script to set the target mouse:`n`ntarget_mouse="%name%"`n`nYou can press Ctrl+C to copy this message.
		report_mouse := 0
		return
	}
	
	is_target := name = target_mouse
	
	;usFlags := NumGet(raw,16, "USHORT")
	usButtonFlags := NumGet(raw,16+4, "USHORT")
	usButtonData := NumGet(raw,16+6, "SHORT")
	
	;FileAppend, % is_target " " usButtonFlags " " usButtonData " " name "`n", mouse-log.txt
	;if (is_target)
		;FileAppend, % is_target " " usButtonFlags " " usButtonData " " name "`n", mouse-log.txt
	
	; LMB usButtonFlags=1,2
	; RMB usButtonFlags=4,8
	; MMB usButtonFlags=16,32
	; Wheel usButtonFlags=1024 -- down usButtonData<0, up usButtonData>0
	; Tilt  usButtonFlags=2048 -- left usButtonData<0, right usButtonData>0  -- repeats typematically
	
	if (is_target) { 
		if (usButtonFlags & 1 or usButtonFlags & 4 or usButtonFlags & 16) { ; LMB/MMB/RMB down
			squelch_mouse := 1 ; ignore the next mouse even, since it will be this event, which comes from the target mouse
		}
		
		if (usButtonFlags & 2) { ; LMB up
			squelch_mouse := 1
			Send {Media_Play_Pause}
		}
		
		if (usButtonFlags & 8) { ; RMB up
			squelch_mouse := 1
			; NOTHING -- this is the button my dog ate
		}
		
		if (usButtonFlags & 32) { ; MMB up
			squelch_mouse := 1
			Send {Media_Stop}#q ; Stop *AND* toggle shuffle
		}
		
		if (usButtonFlags & 1024 && usButtonData<0) { ; Wheel down
			squelch_mouse := 1
			Send {Volume_Down}
		}
		if (usButtonFlags & 1024 && usButtonData>0) { ; Wheel up
			squelch_mouse := 1
			Send {Volume_up}
		}
		if (usButtonFlags & 2048 && usButtonData<0) { ; Wheel left
			squelch_mouse := 1
			Send {Media_Prev}
		}
		if (usButtonFlags & 2048 && usButtonData>0) { ; Wheel right
			squelch_mouse := 1
			Send {Media_Next}
		}
	}
}

$LButton::
	if (squelch_mouse)
		squelch_mouse:=0
	else
		Send {LButton down}
	return
$LButton up::
	if (squelch_mouse)
		squelch_mouse:=0
	else
		Send {LButton up}
	return
$RButton::
	if (squelch_mouse)
		squelch_mouse:=0
	else
		Send {RButton down}
	return
$RButton up::
	if (squelch_mouse)
		squelch_mouse:=0
	else
		Send {RButton up}
	return
$MButton::
	if (squelch_mouse)
		squelch_mouse:=0
	else
		Send {MButton down}
	return
$MButton up::
	if (squelch_mouse)
		squelch_mouse:=0
	else
		Send {MButton up}
	return
$WheelDown::
	if (squelch_mouse)
		squelch_mouse:=0
	else
		Send {WheelDown}
	return
$WheelUp::
	if (squelch_mouse)
		squelch_mouse:=0
	else
		Send {WheelUp}
	return
$WheelLeft::
	if (squelch_mouse)
		squelch_mouse:=0
	else
		Send {WheelLeft}
	return
$WheelRight::
	if (squelch_mouse)
		squelch_mouse:=0
	else
		Send {WheelRight}
	return
	
get_mouse_name(h) {
	DllCall("GetRawInputDeviceInfo",Int,h,UInt,0x20000007,Int,0,"UInt*",l)
	VarSetCapacity(Name,l*2+2)
	DllCall("GetRawInputDeviceInfo",Int,h,UInt,0x20000007,Str,Name,"UInt*",l)
	Return Name
}

; ===== END aux mouse multimedia control =====
using_autohotkey_to_make_a_second_mouse_into_a_multimedia_control.txt · Last modified: 2012/11/05 19:25 by tkbletsc