Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate

HID/Extended input devices (MS Natural Keyboard 4000 etc.)


  • Please log in to reply
44 replies to this topic
shaun
  • Guests
  • Last active:
  • Joined: --
Hi All,

The Microsoft Natural Keyboard 4000 has a "Zoom" slider in the centre of the keyboard that is kind of useless that I wanted to change to be a scroll slider. Unfortunately, it's not detected as a key by AutoHotkey normally. This is because the keyboard registers as a keyboard (which gets most of the input) AND a different HID device that receives input from the special keys.

Many other USB devices behave this way (e.g remote controls, mice with extended features). There have been a number of posts on this and Micha has posted a DLL to make this easier in AutoHotKey, but here's my stab at it.

First a script that lists all of the available raw input devices and can capture and decode all of the input. This is useful to work out what messages the device sends as well as the "Usage" and "Usage Page" of the top level collection of the HID device:

; 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 

; Replace any previous instance 
#SingleInstance force

DetectHiddenWindows, on
OnMessage(0x00FF, "InputMessage")

SizeofRawInputDeviceList := 8
SizeofRidDeviceInfo := 32
SizeofRawInputDevice := 12

RIM_TYPEMOUSE := 0
RIM_TYPEKEYBOARD := 1
RIM_TYPEHID := 2

RIDI_DEVICENAME := 0x20000007
RIDI_DEVICEINFO := 0x2000000b

RIDEV_INPUTSINK := 0x00000100

RID_INPUT       := 0x10000003

DoCapture := 0

Gui, Add, Edit, HScroll w460 h300 vInfoOut -Wrap ReadOnly HwndInfoHwnd
Gui, Add, Edit, HScroll w460 h300 vEditOut -Wrap ReadOnly HwndEditHwnd
Gui, Add, Button, Default gCapture vCaptureButton w150, &Capture
Gui, Show, , HIDList

HWND := WinExist("HIDList")

Res := DllCall("GetRawInputDeviceList", UInt, 0, "UInt *", Count, UInt, SizeofRawInputDeviceList)

InfoOutput("There are " . Count . " raw input devices`r`n`r`n")

VarSetCapacity(RawInputList, SizeofRawInputDeviceList * Count)

Res := DllCall("GetRawInputDeviceList", UInt, &RawInputList, "UInt *", Count, UInt, SizeofRawInputDeviceList)

MouseRegistered := 0 
KeyboardRegistered := 0

Loop %Count% {
   Handle := NumGet(RawInputList, (A_Index - 1) * SizeofRawInputDeviceList)
   Type := NumGet(RawInputList, (A_Index - 1) * SizeofRawInputDeviceList + 4)
   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"

   InfoOutput("Device " . A_Index . ": Handle " . Handle . " Type " . Type . " (" . TypeName . ") ")
   
   Res := DllCall("GetRawInputDeviceInfo", UInt, Handle, UInt, RIDI_DEVICENAME, UInt, 0, "UInt *", Length)
   
   VarSetCapacity(Name, Length + 2)
   
   Res := DllCall("GetRawInputDeviceInfo", UInt, Handle, UInt, RIDI_DEVICENAME, "Str", Name, "UInt *", Length)
   
   InfoOutput("Name " . Name . "`r`n")
   
   VarSetCapacity(Info, SizeofRidDeviceInfo)   
   NumPut(SizeofRidDeviceInfo, Info, 0)
   Length := SizeofRidDeviceInfo
   
   Res := DllCall("GetRawInputDeviceInfo", UInt, Handle, UInt, RIDI_DEVICEINFO, UInt, &Info, "UInt *", SizeofRidDeviceInfo)

   if (Type = RIM_TYPEMOUSE)
      InfoOutput("Buttons " . NumGet(Info, 4 * 3) . " Sample rate " . NumGet(Info, 4 * 4) . "`r`n")
   else if (Type = RIM_TYPEKEYBOARD)
      InfoOutput("Mode " . NumGet(Info, 4 * 4) . " Function keys " . NumGet(Info, 4 * 5) . "`r`n")
   else if (Type = RIM_TYPEHID)
   {      
      InfoOutput("Vendor " . NumGet(Info, 4 * 2) . " Product " . NumGet(Info, 4 * 3) . " Version " . NumGet(Info, 4 * 4) . " ")
      UsagePage := NumGet(Info, (4 * 5), "UShort")
      Usage := NumGet(Info, (4 * 5) + 2, "UShort")
      InfoOutput("Usage " . Usage . " Usage Page " . UsagePage . "`r`n")      
   }
   
   ; 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    

   VarSetCapacity(RawDevice, SizeofRawInputDevice)
   NumPut(RIDEV_INPUTSINK, RawDevice, 4)
   NumPut(HWND, RawDevice, 8)
   
   DoRegister := 0
   
   if (Type = RIM_TYPEMOUSE && MouseRegistered = 0)
   {
      DoRegister := 1
      ; Mice are Usage 2, Usage Page 1
      NumPut(1, RawDevice, 0, "UShort")
      NumPut(2, RawDevice, 2, "UShort")
      MouseRegistered := 1
   }
   else if (Type = RIM_TYPEKEYBOARD && KeyboardRegistered = 0)
   {
      DoRegister := 1
      ; Keyboards are always Usage 6, Usage Page 1
      NumPut(1, RawDevice, 0, "UShort")
      NumPut(6, RawDevice, 2, "UShort")
      KeyboardRegistered := 1
   }
   else if (Type = RIM_TYPEHID)
   {
      DoRegister := 1
      NumPut(UsagePage, RawDevice, 0, "UShort")
      NumPut(Usage, RawDevice, 2, "UShort")     
   }
   
   if (DoRegister)
   {
      Res := DllCall("RegisterRawInputDevices", "UInt", &RawDevice, UInt, 1, UInt, SizeofRawInputDevice) 
      if (Res = 0)
         InfoOutput("Failed to register for this device!`r`n")
      else
         InfoOutput("Registered for this device`r`n")
   }
}


Count := 1 

InputMessage(wParam, lParam, msg, hwnd)
{
   global DoCapture
   global RIM_TYPEMOUSE, RIM_TYPEKEYBOARD, RIM_TYPEHID 
   global RID_INPUT 
   
   if (DoCapture = 0)
      return
   
   Res := DllCall("GetRawInputData", UInt, lParam, UInt, RID_INPUT, UInt, 0, "UInt *", Size, UInt, 16)
      
   VarSetCapacity(Buffer, Size)
   
   Res := DllCall("GetRawInputData", UInt, lParam, UInt, RID_INPUT, UInt, &Buffer, "UInt *", Size, UInt, 16)

   ; AppendOutput(Mem2Hex(&Buffer, Size))
   
   Type := NumGet(Buffer, 0 * 4)
   Size := NumGet(Buffer, 1 * 4)
   Handle := NumGet(Buffer, 2 * 4)
   
   ;AppendOutput("Got Input with " . Res . " size " . Size . " Type " . Type . " Handle " . Handle . "`r`n")
   if (Type = RIM_TYPEMOUSE)
   {
      LastX := NumGet(Buffer, (16 + (4 * 3)), "Int")
      LastY := NumGet(Buffer, (16 + (4 * 4)), "Int")
      AppendOutput("HND " . Handle . " MOUSE LastX " . LastX . " LastY " . LastY . "`r`n")
   }
   else if (Type = RIM_TYPEKEYBOARD)
   {
      ScanCode := NumGet(Buffer, (16 + 0), "UShort")
      VKey := NumGet(Buffer, (16 + 6), "UShort")
      Message := NumGet(Buffer, (16 + 8))
      AppendOutput("HND " . Handle . " KBD ScanCode " . ScanCode . " VKey " . VKey . " Msg " . Message . "`r`n")
   }
   else if (Type = RIM_TYPEHID)
   {
      SizeHid := NumGet(Buffer, (16 + 0))
      InputCount := NumGet(Buffer, (16 + 4))
      AppendOutput("HND " . Handle . " HID Size " . SizeHid . " Count " . InputCount . " Ptr " . &Buffer . "`r`n")
      Loop %InputCount% {
         Addr := &Buffer + 24 + ((A_Index - 1) * SizeHid)
         BAddr := &Buffer
         ;MsgBox, BAddr %BAddr% Addr %Addr%
         AppendOutput("Input " . Mem2Hex(Addr, SizeHid) . "`r`n")
      }
   }
   else
   {
      AppendOutput("HND " . Handle . " Unknown Type " . Type)
   }
   
   return
}
return

Exit

Capture:
{
   if (DoCapture = 0) 
   {  
      GuiControl, , CaptureButton, Stop &Capture
      DoCapture := 1
   }
   else
   {
      GuiControl, , CaptureButton, &Capture
      DoCapture := 0
   }
   return
}


GuiClose:
ExitApp

Mem2Hex( pointer, len )
{
 A_FI := A_FormatInteger
 SetFormat, Integer, Hex
 Loop, %len%  {
                   Hex := *Pointer+0
                   StringReplace, Hex, Hex, 0x, 0x0
                   StringRight Hex, Hex, 2           
                   hexDump := hexDump . hex
                   Pointer ++
                 }
 SetFormat, Integer, %A_FI%
 StringUpper, hexDump, hexDump
Return hexDump
}

InfoOutput(Text)
{
   global InfoHwnd
   GuiControlGet, InfoOut
   NewText := InfoOut . Text
   GuiControl, , InfoOut, %NewText%
   return
}

AppendOutput(Text)
{
   global EditHwnd
   GuiControlGet, EditOut
   NewText := EditOut . Text
   GuiControl, , EditOut, %NewText%
   ; WM_VSCROLL (0x115), SB_BOTTOM (7)
   ;MsgBox, %EditHwnd%
   SendMessage, 0x115, 0x0000007, 0, , ahk_id %EditHwnd%
   return
}
For example, the Natural keyboard 4000 shows up as:

Device 1: Handle 196713 Type 2 (RIM_TYPEHID) Name \??\HID#Vid_045e&Pid_00db&MI_01#8&34dbdc06&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}
Vendor 1118 Product 219 Version 371 Usage 1 Usage Page 12

Here's a script that converts the zoom slider to be a scroll slider. It can easily be converted to accept input from any HID device:

; Capture input from the "Zoom" slider on the Natural Keyboard 4000 
; and use it to scroll up and scroll down 

; The HID top level collection for the Natural Keyboard 4000 is: 
;   Usage         1
;   Usage Page   12

#NoTrayIcon 

; Replace any previous instance 
#SingleInstance force

DetectHiddenWindows, on
OnMessage(0x00FF, "InputMessage")

SizeofRawInputDeviceList := 8
SizeofRidDeviceInfo := 32

RIM_TYPEMOUSE := 0
RIM_TYPEKEYBOARD := 1
RIM_TYPEHID := 2

RIDI_DEVICENAME := 0x20000007
RIDI_DEVICEINFO := 0x2000000b

RIDEV_INPUTSINK := 0x00000100

RID_INPUT       := 0x10000003

Usage := 1
UsagePage := 12

Gui, Show, Hide, NaturalCapture

HWND := WinExist("NaturalCapture")

; 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    

VarSetCapacity(RawDevice, 12)
NumPut(RIDEV_INPUTSINK, RawDevice, 4)
NumPut(HWND, RawDevice, 8)
NumPut(UsagePage, RawDevice, 0, "UShort")
NumPut(Usage, RawDevice, 2, "UShort")     

Res := DllCall("RegisterRawInputDevices", "UInt", &RawDevice, UInt, 1, UInt, 12) 
if (Res = 0)
   MsgBox, Failed to register for Natural Keyboard

InputMessage(wParam, lParam, msg, hwnd)
{
   global DoCapture
   global RIM_TYPEMOUSE, RIM_TYPEKEYBOARD, RIM_TYPEHID 
   global RID_INPUT 
   
   if (DoCapture = 0)
      return
   
   Res := DllCall("GetRawInputData", UInt, lParam, UInt, RID_INPUT, UInt, 0, "UInt *", Size, UInt, 16)
      
   VarSetCapacity(Buffer, Size)
   
   Res := DllCall("GetRawInputData", UInt, lParam, UInt, RID_INPUT, UInt, &Buffer, "UInt *", Size, UInt, 16)
   
   Type := NumGet(Buffer, 0 * 4)
   Size := NumGet(Buffer, 1 * 4)
   Handle := NumGet(Buffer, 2 * 4)
   
   ;AppendOutput("Got Input with " . Res . " size " . Size . " Type " . Type . " Handle " . Handle . "`r`n")
   
   if (Type = RIM_TYPEHID)
   {
      SizeHid := NumGet(Buffer, (16 + 0))
      InputCount := NumGet(Buffer, (16 + 4))
      ;Debug("HND " . Handle . " HID Size " . SizeHid . " Count " . InputCount . " Ptr " . &Buffer . "`r`n")
      Loop %InputCount% {
         Addr := &Buffer + 24 + ((A_Index - 1) * SizeHid)
         BAddr := &Buffer
         ;MsgBox, BAddr %BAddr% Addr %Addr%
         Input := Mem2Hex(Addr, SizeHid)
         ;Debug("Input " . Input . "`r`n")
         if (IsLabel(Input))
            Gosub, %Input%
      }
   }
   
   return
}
return

Exit

GuiClose:
ExitApp

; 1 = UP, 2 = DOWN
ScrollDir := 0

DoScroll: 
ControlGetFocus, fcontrol, A
; WM_VSCROLL = 0x115, SB_LINEUP = 0, SB_LINEDOWN = 1

WinGetClass, ClassName, A
;Debug("Class " . ClassName)
if (InStr(ClassName, "Mozilla"))
{
   IsMozilla := 1
}
else
{
   IsMozilla := 0
}
Loop 3 {
   if (ScrollDir = 1) 
   {
      if (IsMozilla)
         SendInput, {Up}
      else
         SendMessage, 0x115, 0, 0, %fcontrol%, A
   }
   else
   {
      if (IsMozilla)
         SendInput, {Down}
      else
         SendMessage, 0x115, 1, 0, %fcontrol%, A
   }
}
;Debug("Done")
Return

; Zoom down 
012E020000010000:
ScrollDir := 2
GoSub, DoScroll 
SetTimer, DoScroll, 80
return

; Zoom up
012D020000010000:
ScrollDir := 1
GoSub, DoScroll 
SetTimer, DoScroll, 80
return

; All up 
0100000000010000:
ScrollDir := 0
SetTimer, DoScroll, Off
return

Debug(msg)
{
   OutputDebug, %msg%
   return
}

Mem2Hex( pointer, len )
{
 A_FI := A_FormatInteger
 SetFormat, Integer, Hex
 Loop, %len%  {
                   Hex := *Pointer+0
                   StringReplace, Hex, Hex, 0x, 0x0
                   StringRight Hex, Hex, 2           
                   hexDump := hexDump . hex
                   Pointer ++
                 }
 SetFormat, Integer, %A_FI%
 StringUpper, hexDump, hexDump
Return hexDump
}
Cheers,
Shaun

John Burgos
  • Guests
  • Last active:
  • Joined: --
Hey, looks you have done a nice jod.... maybe you can help me out, im new with AutoHotkey, but what I want to do is to be able to remap my external USB HID Keyboard keys (but only remapping the external HID, without affecting the keys of the HID keyboard included in my laptop).

I have read the basics and followed some tutorials, but so far I can only remap keys but afecting both keyboards... Is there a way to only chose one keyboard to remap???

Hope you can help me out, since I tryied to understand your code, but it is too advanced for me...

Thanks in advance

John

lilalurl.T32
  • Members
  • 391 posts
  • Last active: Jul 05 2011 03:39 PM
  • Joined: 17 May 2007
Fantastic job Shaun.

First, in case you don't know, there is a simpler approach to changing zoom to scroll, by editing the commands.xml file in the intellitype folder and changing the current value for C139 and C320 to
<C319 Type="6" Activator="ScrollUp" />
<C320 Type="6" Activator="ScrollDown" />

See there for more details:
http://huddledmasses...-4000-keyboard/
The second comment (by Scott) is also quite useful to learn how the 4000 works.


Anyway, as far as I know, your script is the only way to remap the "my favorites" key ( 0182010000010000 ) to something.
Same goes for the F-Lock key ( 0100000000020000 and 0100000000030000 ).

So once again, good job.

Edit: Just thought of another application for your script, remapping the numpad additional keys (= ( ) and <-). Here are the results of my test for people interested.

The backspace key behaves as a normal backspace key, so I guess it can't be remapped to something else.
However the three others register as speficic HID keys. Here are the values:
;numpad=
0100006700010000:
msgbox equal
return

;numpad(
010000B600010000:
msgbox (
return

;numpad)
010000B700010000:
msgbox )
return
________
Box vaporizer

j0dan
  • Members
  • 11 posts
  • Last active: Jun 10 2009 05:55 AM
  • Joined: 02 Feb 2009
Thank-you so much for this!!!

Here are the mappings for all the favorite buttons:
; My Favorites
0182010000010000:
	MsgBox My Favorites Button
return
; Favorites 1
0100000000050000:
	MsgBox Favorites 1
return
; Favorites 2
0100000000090000:
	MsgBox Favorites 2
return
; Favorites 3
0100000000110000:
	MsgBox Favorites 3
return
; Favorites 4
0100000000210000:
	MsgBox Favorites 4
return
; Favorites 5
0100000000410000:
	MsgBox Favorites 5
return

Now I just need to find out why the scrolling sometimes gets stuck in Firefox. I would rather it just executed the Mousewheel commands. That way I can just use it when my finger gets tired of mouse wheeling.

..:: Free Radical ::..
  • Members
  • 74 posts
  • Last active: May 30 2015 12:13 PM
  • Joined: 20 Sep 2006
This is awesome!

Thank you very much Shaun :D

TheGood
  • Members
  • 589 posts
  • Last active: Mar 22 2014 03:22 PM
  • Joined: 30 Jul 2007
So, those api calls can be used to replace Micha's dll?
If so, that's amazing!

mprost
  • Members
  • 5 posts
  • Last active: Jun 02 2014 03:25 PM
  • Joined: 15 Dec 2006
works great! thank you very much Shaun

Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006

Here are the mappings for all the favorite buttons:

I'm not sure if the HID device works the same for the Microsoft Natural Keyboard 4000 as for the Wireless Media Desktop 1000 (keyboard), but looking at the "codes" in this thread, I'd say there's a strong possibility it does. If it does, those mappings will only work if F-lock is on and certain other keys aren't being held.

Now I just need to find out why the scrolling sometimes gets stuck in Firefox.

Might also be related to this.


When I first tried the script with my (new) Wireless Media Desktop 1000, I noticed the codes differ when:
[*:lfb7e37f]F-lock is enabled/disabled.
[*:lfb7e37f]One or more extra keys [1] to [5] are held down.
[*:lfb7e37f]One or more standard modifier keys are held down.After a bit of experimentation, I've found the format seems to be:
[*:lfb7e37f]Byte 0: 0x01, 0x04 or 0x05. Only input packets beginning with 0x01 seem to be meaningful.
[*:lfb7e37f]Byte 1-2: Identifies any of the special keys, including standard media keys handled by Windows, but excluding [1] to [5] and F-lock. 0x0000 if a key was released (unfortunately I can't detect which one).
[*:lfb7e37f]Byte 3-4: Unknown.
[*:lfb7e37f]Byte 5: Bitfield representing the state of a number of keys. In low to high bit order: F-lock ON/OFF, F-lock pressed (not updated immediately on release), [1] .. [5], unknown.
[*:lfb7e37f]Byte 6: Unknown.
[*:lfb7e37f]Byte 7: Modifier key state. In low to high bit order: LCtrl, LShift, LAlt, LWin, RCtrl, RShift, RAlt.Based on shaun's script:
; Capture various button presses from Wireless Media Desktop 1000 (Keyboard).

; The HID top level collection for the Wireless Media Desktop 1000 (Keyboard) is:
;   Usage         1
;   Usage Page   12


#NoTrayIcon

; Replace any previous instance
#SingleInstance force

DetectHiddenWindows, on
OnMessage(0x00FF, "InputMessage")

SizeofRawInputDeviceList := 8
SizeofRidDeviceInfo := 32

RIM_TYPEMOUSE := 0
RIM_TYPEKEYBOARD := 1
RIM_TYPEHID := 2

RIDI_DEVICENAME := 0x20000007
RIDI_DEVICEINFO := 0x2000000b

RIDEV_INPUTSINK := 0x00000100

RID_INPUT       := 0x10000003

Usage := 1
UsagePage := 12

; Use the script's main window rather than creating a GUI. DetectHiddenWindows must be ON.
HWND := WinExist("ahk_class AutoHotkey ahk_pid " DllCall("GetCurrentProcessId"))

; 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   

VarSetCapacity(RawDevice, 12)
NumPut(RIDEV_INPUTSINK, RawDevice, 4)
NumPut(HWND, RawDevice, 8)
NumPut(UsagePage, RawDevice, 0, "UShort")
NumPut(Usage, RawDevice, 2, "UShort")     

Res := DllCall("RegisterRawInputDevices", "UInt", &RawDevice, "UInt", 1, "UInt", 12)
if (Res = 0)
   MsgBox, Failed to register for Natural Keyboard

InputMessage(wParam, lParam, msg, hwnd)
{
   global DoCapture
   global RIM_TYPEMOUSE, RIM_TYPEKEYBOARD, RIM_TYPEHID
   global RID_INPUT
   static LastKeyBits := 0
   Critical ; May help responsiveness.
   
   if (DoCapture = 0)
      return
   
   Res := DllCall("GetRawInputData", "UInt", lParam, "UInt", RID_INPUT, "UInt", 0, "UInt *", Size, "UInt", 16)
     
   VarSetCapacity(Buffer, Size)
   
   Res := DllCall("GetRawInputData", "UInt", lParam, "UInt", RID_INPUT, "UInt", &Buffer, "UInt *", Size, "UInt", 16)
   
   Type := NumGet(Buffer, 0)
   Size := NumGet(Buffer, 4)
   Handle := NumGet(Buffer, 8)
   
   if (Type = RIM_TYPEHID)
   {
      SizeHid := NumGet(Buffer, 16)
      if SizeHid != 8 ; Only interested in inputs 8 bytes long.
          return
      InputCount := NumGet(Buffer, 20)
      Loop %InputCount%
      {
         ; NumGet is faster with (variable, offset) than (address).
         Offset := 24 + ((A_Index - 1) * SizeHid)
         
         ; DECODING BASED ON MICROSOFT WIRELESS MEDIA DESKTOP 1000
         
         ; Events beginning with 04 or 05 seem to be meaningless spam.
         ; We are only interested in events beginning with 01.
         if NumGet(Buffer, Offset, "Char") != 1
            continue
         
         ; Some key-presses are identified by the second and third bytes.
         Key := NumGet(Buffer, Offset + 1, "UShort")
         ; Some key states are stored in a bitfield.
         KeyBits := NumGet(Buffer, Offset + 5, "UChar")
         
         ; Interpret 0000 as "some key released", but only if key bits haven't changed.
         if (Key || KeyBits = LastKeyBits)
         {
            VarSetCapacity(KeyHex, 4)
            DllCall("msvcrt\sprintf_s", "str", KeyHex, "uint", 5, "str", "%04X", "int", Key)
            if IsLabel("HID" KeyHex)
               ;Gosub HID%KeyHex%
               SetTimer, HID%KeyHex%, -1 ; Use a timer to avoid "locking" the message handler.
            Debug("HID" KeyHex)
         }
         ; Continue in case keys represented by "key bits" have also been pressed or released.
         if (KeyBits != LastKeyBits)
         {
            ; 1000000: F-lock ON/F-lock OFF.
            ; 0100000: F-lock pressed. Not updated immediately on release, therefore not useful.
            ; 0010000: [1]
            ; ...
            ; 0000010: [5]
            ; 0000001: Unused on this keyboard, but maybe used on others.
            Loop 8
            {
               KeyBit := (1<<(A_Index-1))
               if (KeyBits & KeyBit != LastKeyBits & KeyBit)
               {
                  KeyBitLabel := "HID"
                  Loop % A_Index-1
                     KeyBitLabel .= "0"
                  KeyBitLabel .= "1"
                  Loop % 8-A_Index
                     KeyBitLabel .= "0"
                  
                  if !(KeyBits & KeyBit)
                     KeyBitLabel .= "_UP"

                  if IsLabel(KeyBitLabel)
                     ;Gosub %KeyBitLabel%
                     SEtTimer, %KeyBitLabel%, -1
                  Debug(KeyBitLabel " (" LastKeyBits " -> " KeyBits ")")
               }
            }
            LastKeyBits := KeyBits
         }
         ; The last byte appears to contain modifier key state (LCtrl, LShift, LAlt, etc.)
         ; but it is easier to handle modifier keys using standard AutoHotkey functions.
         Debug("mod " NumGet(Buffer, Offset + 7, "Char"))
      }
   }
   
   return
}
return

Debug(msg)
{
;    OutputDebug, %msg%
;    ToolTip % msg
   return
}

HID10000000: ; F-lock ON
SoundPlay, F:\Downloads\Customization\Sounds\Wurt\user_free.wav
return
HID10000000_UP: ; F-lock OFF
SoundPlay, F:\Downloads\Customization\Sounds\Wurt\user_na.wav
return

HID00100000: ; [1]
HID00100000_UP:
HID00010000: ; [2]
HID00010000_UP:
HID00001000: ; [3]
HID00001000_UP:
HID00000100: ; [4]
HID00000100_UP:
HID00000010: ; [5]
HID00000010_UP:
   MsgBox % A_ThisLabel
return

HID0199: ; Messenger
FKey("")
   Send #e
return

HID0182: ; Favourites
FKey("")
   Send #q
return

HID01A2: ; Flip-3D
FKey("HID01A2_UP:") ; Run HID01A2_UP when FKey() is called with a different value.
   Send {LWin Down}{Tab}
return
HID01A2_UP: ; The next HIDxxxx where xxxx is not 01A2.
   Send {LWin Up}
return

HID0095:
FKey("F1")
return
HID021A:
FKey("F2")
return
HID0279:
FKey("F3")
return
HID0201:
FKey("F4")
return
HID0202:
FKey("F5")
return
HID0203:
FKey("F6")
return
HID0289:
FKey("F7")
return
HID028B:
FKey("F8")
return
HID028C:
FKey("F9")
return
HID01AB:
FKey("F10")
return
HID0207:
FKey("F11")
return
HID0208:
FKey("F12")
return
HID0000:
FKey("")
return

FKey(k)
{
   static last_k := ""

   if last_k && last_k != k
   {
      if SubStr(last_k,0) = ":" && IsLabel(SubStr(last_k,1,-1))
         Gosub % SubStr(last_k,1,-1)
      else
         Send {Blind}{%last_k% Up}
      last_k := ""
   }
   if k && (SubStr(last_k,0) != ":" || !IsLabel(SubStr(last_k,1,-1)))
      Send {Blind}{%k% Down}
   last_k := k
}
It isn't perfect, but seems to work well for my purposes.

ddh819
  • Members
  • 22 posts
  • Last active: Aug 02 2009 05:21 AM
  • Joined: 27 Dec 2006
I'd like to use this to remap the buttons on a Kensington presentation remote (which simulate PgUp, PgDn, F5 and "b") since Micha's DLL can't tell the difference between it and the regular keyboard keys. But I don't understand how it works.

This is what I get when I press all the keys using Shaun's test script:

HND 1508478 KBD ScanCode 73 VKey 33 Msg 256
HND 1508478 KBD ScanCode 73 VKey 33 Msg 257
HND 1508478 KBD ScanCode 81 VKey 34 Msg 256
HND 1508478 KBD ScanCode 81 VKey 34 Msg 257
HND 1508478 KBD ScanCode 63 VKey 116 Msg 256
HND 1508478 KBD ScanCode 63 VKey 116 Msg 257
HND 1508478 KBD ScanCode 48 VKey 66 Msg 256
HND 1508478 KBD ScanCode 48 VKey 66 Msg 257

How do i translate these into hotkeys?

mai9
  • Members
  • 63 posts
  • Last active: Aug 15 2011 04:23 PM
  • Joined: 17 Mar 2008
I can't help you much because I don't know a lot, but I have the same keyboard as Shaun and the string number that will be used to call the key is not present in what you posted.

If I use the HIDList and press the "My Favorites" key, it captures:

HND 65637 HID Size 8 Count 1 Ptr 43656788
Input 0182010000010000
HND 65637 HID Size 8 Count 1 Ptr 43656788
Input 0100000000010000

as you can see what you posted lacks the "input" string, maybe your kensington is not a HID device. In my keyboard when I press space, HIDList it doesn't list an "input" neither. Seems that it's only for those special keys.

TheGood
  • Members
  • 589 posts
  • Last active: Mar 22 2014 03:22 PM
  • Joined: 30 Jul 2007

I'd like to use this to remap the buttons on a Kensington presentation remote (which simulate PgUp, PgDn, F5 and "b") since Micha's DLL can't tell the difference between it and the regular keyboard keys. But I don't understand how it works.

This is what I get when I press all the keys using Shaun's test script:

HND 1508478 KBD ScanCode 73 VKey 33 Msg 256
HND 1508478 KBD ScanCode 73 VKey 33 Msg 257
HND 1508478 KBD ScanCode 81 VKey 34 Msg 256
HND 1508478 KBD ScanCode 81 VKey 34 Msg 257
HND 1508478 KBD ScanCode 63 VKey 116 Msg 256
HND 1508478 KBD ScanCode 63 VKey 116 Msg 257
HND 1508478 KBD ScanCode 48 VKey 66 Msg 256
HND 1508478 KBD ScanCode 48 VKey 66 Msg 257

How do i translate these into hotkeys?

You should look into AHKHID, an HID wrapper I made.
With the examples included (especially Example 2) you can find out exactly what each key on your remote sends and then you can make your own script with an OnMessage function that relays the event to a sub. Try it out and if you have problems, post in the other thread and I'll help you :D

j0dan
  • Members
  • 11 posts
  • Last active: Jun 10 2009 05:55 AM
  • Joined: 02 Feb 2009
It looks like modifiers like the windows key can be applied to the favorite buttons on the natural keyboard. The codes are the same except the last digit is an 8 instead of 0 for Windows.

But I'm not sure how to stop the start menu from coming up every time I press it. Ideas?

0100000000050008:
	MsgBox Windows Favorites 1
	; The Windows button press still goes to the OS
return


popoycanton
  • Members
  • 2 posts
  • Last active: Sep 12 2009 03:16 PM
  • Joined: 12 Sep 2009

Fantastic job Shaun.


Edit: Just thought of another application for your script, remapping the numpad additional keys (= ( ) and <-). Here are the results of my test for people interested.

The backspace key behaves as a normal backspace key, so I guess it can't be remapped to something else.
However the three others register as speficic HID keys. Here are the values:

;numpad=
0100006700010000:
msgbox equal
return

;numpad(
010000B600010000:
msgbox (
return

;numpad)
010000B700010000:
msgbox )
return


So, how do you actually use this in a script? Sorry I'm new to autohotkeys & learning as i go. I've learned remaping with with send, but that's as far as I've gotten. :(

lilalurl.T32
  • Members
  • 391 posts
  • Last active: Jul 05 2011 03:39 PM
  • Joined: 17 May 2007
[quote name="popoycanton"]So, how do you actually use this in a script?[/quote]

Take the second script posted by Shaun (the one that begins with ; Capture input from the "Zoom" slider on the Natural Keyboard 4000
; and use it to scroll up and scroll down ).

Then, you need to modify the part that is between line 147 and line 166 (i.e. just after ";Debug("Done") Return " and until "Debug(msg)".

There you will put the code for the keys you want to remap (you can find the code using Shaun's first script) and what you want the key to do.

So for instance, 0100006700010000 is what I found using the first script for the numpad equal key. What you have quoted is just the code and a msgbox that will be triggered when you press the correct key (to verify if the code is correct).

Hence, you just add this in the part I mentioned (or replace the zoom part if you don't want it) and put whatever you want the key to do instead of msgbox whatever.[/quote]

Then you run the script and as long as it is active, the key will be remapped.
________
VAPORIZER REVIEW

popoycanton
  • Members
  • 2 posts
  • Last active: Sep 12 2009 03:16 PM
  • Joined: 12 Sep 2009
@lilalurl.T32
thanks chief.
That much I understand, tried it out & was a success :)