1 (изменено: Dima_v_Tomske, 2012-01-12 00:45:09)

Тема: AHK: Дополнительные кнопки USB устройств через HID-протокол

Став обладателем мышки "World of Warcraft® Cataclysm MMO Gaming Mouse",
столкнулся с проблемой программного обеспечения для нее, оно конечно есть,
но точно не соответствует названию "... Gaming Mouse", мало функциональное и не корректно работающее.
Альтернативных программ не нашел. Программы типа Mkey, KeyMan хоть и работаю с HID-протоколом, но тоже не подходят.
Так вот, изучив темы по AHK мной был составлен скрипт:

#NoEnv 

SetKeyDelay, 61     ;задержка между нажатиями клавиш
SetMouseDelay, 61   ;задержка между нажатиями мыши

k:=0                ;Задается переменная для счета в части для чередования нажатий

But:=0x0000         ;Задается переменная счета нажатых клавиш, для возможности определения отжатия клавиши
ButAll:=0x0000      ;Задается переменная для суммы счета нажатых клавиш, для возможности определения отжатия клавиши

;////
; Задаются значения соответствующие отдельно нажатой кнопки устройства, для подсчета нажатий
Butk1:=0x0001
Butk2:=0x0100
Butk3:=0x0200
Butk4:=0x0400
Butk5:=0x0800
Butk6:=0x1000
Butk7:=0x2000
Butk8:=0x4000
Butk9:=0x8000
;////


DetectHiddenWindows, On 
Process, Exist 
hwnd := WinExist("ahk_class AutoHotkey ahk_pid " ErrorLevel) 
DetectHiddenWindows, Off 
 
OnMessage(0xFF, "WM_INPUT") 


;////
;Данные HID устройства, можно взять из скрипта "AutoHotkeyRemoteControlDLL", требуется библиотека "AutohotkeyRemoteControl.dll"
Usage       = 1 
UsagePage   = 65280 
;////


;////
;Дальше взято из подобных программ, так что корректно описать действия не могу, далек до программиста
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) 
 
ret := DllCall("RegisterRawInputDevices" 
    , "uint", &dev  ; pRawInputDevices (pointer to an array of RAWINPUTDEVICE) 
    , "uint", 1     ; uiNumDevices 
    , "uint", 12)   ; cbSize (size of a RAWINPUTDEVICE structure) 
if (ErrorLevel or !ret) { 
;    MsgBox, RegisterRawInputDevices failed. 
    ExitApp 
    } 

 
WM_INPUT(wParam, lParam) 
{ 
    Critical 
   
    ; Get required buffer size. 
    DllCall("GetRawInputData", "uint", lParam, "uint", 0x10000003 
        , "uint", 0, "uint*", size, "uint", 16) 
    
    VarSetCapacity(raw, size, 0) 
    
    ; Get raw input data from handle (lParam) 
    ret := DllCall("GetRawInputData", "uint", lParam, "uint", 0x10000003 
        , "uint", &raw, "uint*", size, "uint", 16, "int") 
    
    if (ErrorLevel or ret = -1) { 
;        MsgBox, Ошибка получения данных 
        return 
        } 
 
    SetFormat, INTEGER, H 
        count := NumGet(raw, 20) ; dwCount 
        n = 0 
        Loop, %count% 
        { 
            size := NumGet(raw, 16) ; dwSizeHid 
            VarSetCapacity(hex, size*3) 
            Loop, %size% 
            { 
                char := SubStr(NumGet(raw, 24+n+A_Index-1, "UChar"), 3) 
                StringUpper, char, char 
                if (StrLen(char) < 2) 
                    char = 0%char% 
                hex = %hex%%char%

            } 
            n += size 
            if !size 
                hex = "--" 
;//// 
               
Cod1 := SubStr(hex, 3 , 4)      ;Вырезается часть получаемого кода, достаточную для просчета клавиш,
                                ;тут можно вырезать и другую информацию, соответствующую какой либо функции,
                                ;например переключение профилей (аля мыши A-TECH серии XL)
Cod1 = 0x%Cod1%                 ;делаем символьную строку числовой, формата Hex

; Задаются значения соответствующие отдельно нажатой кнопки устройства, для вычисления нажатой кнопки или комбинации кнопок
k1:=0x0001
k2:=0x0100
k3:=0x0200
k4:=0x0400
k5:=0x0800
k6:=0x1000
k7:=0x2000
k8:=0x4000
k9:=0x8000

;       Тут, что придумал, то придумал для вычисления нажатий, вроде работает нормально
  if Cod1 = 0
     gosub nol

  if (Cod1-k9) >= 0
     gosub kk9
     else Cod1:=Cod1+k9
 
  if (Cod1-(k9+k8)) >=0
     gosub kk8
     else Cod1:=Cod1+k8

  if (Cod1-(k9+k8+k7)) >= 0
     gosub kk7
     else Cod1:=Cod1+k7

  if (Cod1-(k9+k8+k7+k6)) >= 0
     gosub kk6
     else Cod1:=Cod1+k6

  if (Cod1-(k9+k8+k7+k6+k5)) >= 0
     gosub kk5
     else Cod1:=Cod1+k5

  if (Cod1-(k9+k8+k7+k6+k5+k4)) >= 0
     gosub kk4
     else Cod1:=Cod1+k4

  if (Cod1-(k9+k8+k7+k6+k5+k4+k3)) >= 0
     gosub kk3
     else Cod1:=Cod1+k3

  if (Cod1-(k9+k8+k7+k6+k5+k4+k3+k2)) >= 0
     gosub kk2
     else Cod1:=Cod1+k2

  if (Cod1-(k9+k8+k7+k6+k5+k4+k3+k2+k1)) >= 0
     gosub kk1
     else Cod1:=Cod1+k1
    

; Ссылки на исполнение команд при нажатии
gosub kkk9     ;Клавиша 5
gosub kkk8     ;Клавиша 4
gosub kkk7     ;Клавиша 13
gosub kkk6     ;Клавиша 14
gosub kkk5     ;Клавиша 12
gosub kkk4     ;Клавиша 10
gosub kkk3     ;Клавиша 11
gosub kkk2     ;Клавиша 7
gosub kkk1     ;Клавиша 6

gosub nol1      ;ссылка на присвоение суммы нажатых клавиш и обнуления их счета
                ;это для возможности определения отжатия клавиш

        } 
} 
return

;////
;Эта часть считает сумму нажатых кнопок, почему так, в цикле выше происходит сброс получаемых данных при каждом последующем исполнении
;и их сравнение с предыдущими данными не получается
nol1:
ButAll:=But
But=0x0000
return

nol:
But=0x0000
ButAll=0x0000
return

kk9:
But:=But+Butk9
return

kk8:
But:=But+Butk8
return

kk7:
But:=But+Butk7
return

kk6:
But:=But+Butk6
return

kk5:
But:=But+Butk5
return

kk4:
But:=But+Butk4
return

kk3:
But:=But+Butk3
return

kk2:
But:=But+Butk2
return

kk1:
But:=But+Butk1
return
;////

;////
;Тут сами действия для конкретной клавиши
kkk9:
if But-ButAll=Butk9
    Send , {Numpad6}
return

kkk8:
if But-ButAll=Butk8
   {
    Send {Numpad5}
   }
return

kkk7:
if But-ButAll=Butk7
   Send , !^8
return

kkk6:
if But-ButAll=Butk6
   Send , {Numpad8}
return

kkk5:
if But-ButAll=Butk5
   {
    if k=0
       {
        Send {NumpadMult}
        k=1
        SetTimer, Counter5, 3000
       }
    else
       {
        if k=1
           {
            Send {NumpadDiv}
            k=0
            SetTimer, Counter5, off
           }
       }
   }
return
return

Counter5:
k=0
return



kkk4:
if But-ButAll=Butk4
   {
    Send , {Numpad9}
   }
return


kkk3:
if But-ButAll=Butk3
   {
    Send , {Numpad0}
   }
return

kkk2:
if But-ButAll=Butk2
    Send {Numpad7}
return




kkk1:
if But-ButAll=Butk1
   {
    Send , {Numpad4}
   }
return

 
MButton::
Send {Numpad3}
return

WheelUp::
send {Numpad1}
return

WheelDown::
send {Numpad2}
return

XButton1::
  KeyWait, XButton1, D
   Send {Space Down}
  KeyWait, XButton1, U
   Send {Space Up} 

 return


XButton2::
  KeyWait, XButton2, D
   SetTimer, CounterX2, 50
  KeyWait, XButton2, U
   SetTimer, CounterX2, off
 return
CounterX2:
   Send , {F12}
return
;////

CapsLock::Suspend 



;///// Пример нажатия и отжатия кнопки
;kkk9:
; if But-ButAll=Butk9
;   {
;   Send {Numpad5 Down}
;   }
; if ButAll-But=Butk9 or But=0
;   Send {Numpad5 Up}
; return
; /////


;///// Пример цикличного нажатия при удержании кнопки
;kkk9:
; if But-ButAll=Butk9
;   {
;   SetTimer, Counter9, 200
;   }
; if ButAll-But=Butk9 or But=0
;   SetTimer, Counter9, off
; return
;Counter9:
;   Send {Numpad5}
;return
;/////


;///// Пример при первом нажатии кномпки происходит нажатие Num7,
;      при повторном нажатии кнопки, в течении 2 сек, происходит нажатие Num1 и дальнейшее чередование,
;      после 2 сек, без нажатия, происходит снова первое нажатие Num7
;kkk9:
;if But-ButAll=Butk9
;   {
;    if k=0
;       {
;        Send {Numpad7}
;        k=1
;        SetTimer, Counter9, 2000
;       }
;    else
;       {
;        if k=1
;           {
;            Send {Numpad1}
;            k=0
;            SetTimer, Counter9, off
;           }
;       }
;   }
;return
;return
;
;Counter9:
;k=0
;return
;/////


;///// Пример первым нажатием включается скрипт, повторным нажатием отключается
;kkk9:
; if But-ButAll=Butk9
;      {
;    if k=0
;       {
;        k=1
;        SetTimer, Counter9, 500
;       }
;    else
;       {
;        if k=1
;           {
;            k=0
;            SetTimer, Counter9, off
;           }
;       }
;   }
;return
;return
;
;Counter9:
;Send , 9
;return
;/////



;XButton1::
;    {
;    Send {Space Down}
;    SetTimer, CounterX, 32
;    }
;return
;CounterX:
;Send {Space Up}
;SetTimer, CounterX, off
;return

Скрип вроде работает нормально, но при исполнении данной части скрипта,

;///// Пример цикличного нажатия при удержании кнопки
  ;kkk9:
  ; if But-ButAll=Butk9
  ;   {
  ;   SetTimer, Counter9, 200
  ;   }
  ; if ButAll-But=Butk9 or But=0
  ;   SetTimer, Counter9, off
  ; return
  ;Counter9:
  ;   Send {Numpad5}
  ;return
  ;////

для циклического нажатия Numpad5 при удержании нажатой кнопки устройства,
а при отпускании кнопки устройства - остановка, происходит не понятное.
Может и корректно работать, а может происходить "залипание" и при отжатии, остановка не происходит.
Вот тут и загвоздка. Подскажите как это можно решить эту проблему.
Приветствуются решения для возможности усовершенствования данного скрипта, желательно с пояснениями.

2

Re: AHK: Дополнительные кнопки USB устройств через HID-протокол

Сложно сразу въехать в алгоритм. Насколько я понял из выделенного фрагмента, при отпускании кнопки должно выполниться второе условие и таймер заблокируется. Раз не блокируется, значит условие не выполняется. Попробуйте отследить работу этой подпрограммы пошагово.

3 (изменено: Dima_v_Tomske, 2012-01-11 20:20:54)

Re: AHK: Дополнительные кнопки USB устройств через HID-протокол

Нет, условие точно выполняется ""математически", если корректно определяется код клавиши, но может быть проходит не корректный код, хотя при визуальном отслеживании такого не наблюдается, либо слишком быстро проходит изменение параметра, что SetTimer не реагирует, так как при нажатии и отжатии одной и той же кнопке устройства, происходит то, как должно, то нет, особенно когда быстро жмешь.

Скрипт в принципе рабочий, менять надо только данные HID устройства и соответствующие клавишам, их значение, а также вырезать нужную часть кода (в моем случае 4 цифры, которые меняются), так как бывает в коде другая информация о состоянии устройства.

4

Re: AHK: Дополнительные кнопки USB устройств через HID-протокол

Можно после обоих If добавить команду Else:

  kkk9:
   if But-ButAll=Butk9
     {
     SetTimer, Counter9, 200
     }
   if ButAll-But=Butk9 or But=0
     SetTimer, Counter9, off
Else MsgBox, But %But% `r`n ButAll %ButAll% `r`n Butk9 %Butk9%
   return
  Counter9:
     Send {Numpad5}
  return

5 (изменено: creature.ws, 2012-01-12 00:47:27)

Re: AHK: Дополнительные кнопки USB устройств через HID-протокол

Приветствуются решения для возможности усовершенствования данного скрипта, желательно с пояснениями.

Кардинальных улучшений не обещаю, но по-придираться  берусь


DetectHiddenWindows, On 
Process, Exist 
hwnd := WinExist("ahk_class AutoHotkey ahk_pid " ErrorLevel) 
DetectHiddenWindows, Off

Вышеприведенный фрагмент безболезненно замещается (для AHK-L v1.1.01+) на


hwnd := A_ScriptHwnd

Присваивание:

k1:=0x0001
k2:=0x0100
…

— происходит в теле цикла начинающегося в 80-й строке оригинального скрипта, целесообразно вынести эти операции вне функции (объявив переменные в теле функции как global)

6 (изменено: Dima_v_Tomske, 2012-01-12 05:00:38)

Re: AHK: Дополнительные кнопки USB устройств через HID-протокол

Этот вариант не проходит,

hwnd := A_ScriptHwnd

а причину выяснил, не происходит периодически изменение значение "hex" при отжатии клавиш устройства, т.е.
при нажатии любой одной клавиши "hex" формирует значение кода (формирует при нажатии исправно), а вот когда
отжимаешь клавишу устройства, такое ощущение, что цикл на вычисление "hex" не запускается, значение "hex" не вычисляется, не происходит выполнение скрипта для просчета отключения таймера.
Как я понял за отслеживание нажатий клавиш устройства отвечает,

OnMessage(0xFF, "WM_INPUT")

который и запускает дальше цикл, но тут я только догадываюсь и принцип его работы не знаю.

7 (изменено: creature.ws, 2012-01-12 00:53:10)

Re: AHK: Дополнительные кнопки USB устройств через HID-протокол

hwnd := A_ScriptHwnd должен проходить при использовании ahk-l актуальной версии. Это бесспорно
Видимо вы используете иную версию ahk, увы.

При зажатии Xbutton1 и Xbutton2 автоповтор не начинается, потому фрагмент:


 XButton1::
  KeyWait, XButton1, D
   Send {Space Down}
  KeyWait, XButton1, U
   Send {Space Up} 
 return
 
XButton2::
  KeyWait, XButton2, D
   SetTimer, CounterX2, 50
  KeyWait, XButton2, U
   SetTimer, CounterX2, off
 return

целесообразно изменить так:


XButton1::
    Send {Space Down}
    KeyWait, XButton1
    Send {Space Up}
Return

XButton2::
    SetTimer, CounterX2, 50
    KeyWait, XButton2
    SetTimer, CounterX2, off
Return

8 (изменено: Dima_v_Tomske, 2012-01-13 15:44:24)

Re: AHK: Дополнительные кнопки USB устройств через HID-протокол

Дело оказалось в

SetKeyDelay, 61     ;задержка между нажатиями клавиш
SetMouseDelay, 61   ;задержка между нажатиями мыши

поменяв на

SetKeyDelay, 0     ;задержка между нажатиями клавиш
SetMouseDelay, 0   ;задержка между нажатиями мыши

проблема почти решена при запуске в AutoHotkey Version 1.0.48, только иногда, очень редко, все таки не происходит отжатие.
А вот  AutoHotkey_L Version 1.1.05.06 такое решение не помогло.
Вот мой скрип немного переработанный:

#NoEnv 
SetKeyDelay, 0     ;задержка между нажатиями клавиш
SetMouseDelay, 0   ;задержка между нажатиями мыши
But:=0x0000        ;Задается переменная счета нажатых клавиш, для возможности определения отжатия клавиши
ButAll:=0x0000     ;Задается переменная для суммы счета нажатых клавиш, для возможности определения отжатия клавиши
;////
; Задаются значения соответствующие отдельно нажатой кнопки устройства, для подсчета нажатий,
; если кнопок меньше, просто ставим 0 в оставшиеся значения и помечаем ремаркой соответствующий gosub kkk..

k1:=0x0001
k2:=0x0100
k3:=0x0200
k4:=0x0400
k5:=0x0800
k6:=0x1000
k7:=0x2000
k8:=0x4000
k9:=0x8000
kk5:=0             ;Задается переменная для счета в части для чередования нажатий
;////

DetectHiddenWindows, On 
Process, Exist 
hwnd := WinExist("ahk_class AutoHotkey ahk_pid " ErrorLevel) 
DetectHiddenWindows, Off 
OnMessage(0xFF, "WM_INPUT") 

;Данные HID устройства, можно взять из скрипта "AutoHotkeyRemoteControlDLL", требуется библиотека "AutohotkeyRemoteControl.dll"
Usage       = 1 
UsagePage   = 65280 
;////

;Дальше взято из подобных программ, так что корректно описать действия не могу, далек до программиста
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) 
 
ret := DllCall("RegisterRawInputDevices" 
    , "uint", &dev  ; pRawInputDevices (pointer to an array of RAWINPUTDEVICE) 
    , "uint", 1     ; uiNumDevices 
    , "uint", 12)   ; cbSize (size of a RAWINPUTDEVICE structure) 
if (ErrorLevel or !ret) { 
;    MsgBox, RegisterRawInputDevices failed. 
    ExitApp 
    } 
 
WM_INPUT(wParam, lParam) 
{ 
    global
    Critical 
    ; Get required buffer size. 
    DllCall("GetRawInputData", "uint", lParam, "uint", 0x10000003 
        , "uint", 0, "uint*", size, "uint", 16) 
    
    VarSetCapacity(raw, size, 0) 
    
    ; Get raw input data from handle (lParam) 
    ret := DllCall("GetRawInputData", "uint", lParam, "uint", 0x10000003 
        , "uint", &raw, "uint*", size, "uint", 16, "int") 
    
    if (ErrorLevel or ret = -1) { 
;        MsgBox, Ошибка получения данных 
        return 
        } 
 
    SetFormat, INTEGER, H 
        count := NumGet(raw, 20) ; dwCount 
        n = 0 
        Loop, %count% 
        { 
            size := NumGet(raw, 16) ; dwSizeHid 
            VarSetCapacity(hex, size*3) 
            Loop, %size% 
            { 
                char := SubStr(NumGet(raw, 24+n+A_Index-1, "UChar"), 3) 
                StringUpper, char, char 
                if (StrLen(char) < 2) 
                    char = 0%char% 
                hex = %hex%%char%

            } 
            n += size 
            if !size 
                hex = "--" 
;//// 

Cod := SubStr(hex, 3 , 4)      ;Вырезается часть получаемого кода, достаточную для просчета клавиш,
                               ;тут можно вырезать и другую информацию, соответствующую какой либо функции,
                               ;например переключение профилей (аля мыши A-TECH серии XL)
;ToolTip hex=%hex%             ;Это для того, что бы посмотреть весь код
Cod = 0x%Cod%                  ;делаем символьную строку числовой, формата Hex

;       Тут, что придумал, то придумал для вычисления нажатий, отжатий и их комбинаций, вроде работает нормально
  if Cod = 0
     {But=0x0000
      ButAll=0x0000
     }

  if (Cod-k9) >= 0
     But:=But+k9
     else Cod:=Cod+k9
 
  if (Cod-(k9+k8)) >=0
     But:=But+k8
     else Cod:=Cod+k8

  if (Cod-(k9+k8+k7)) >= 0
     But:=But+k7
     else Cod:=Cod+k7

  if (Cod-(k9+k8+k7+k6)) >= 0
     But:=But+k6
     else Cod:=Cod+k6

  if (Cod-(k9+k8+k7+k6+k5)) >= 0
     But:=But+k5
     else Cod:=Cod+k5

  if (Cod-(k9+k8+k7+k6+k5+k4)) >= 0
     But:=But+k4
     else Cod:=Cod+k4

  if (Cod-(k9+k8+k7+k6+k5+k4+k3)) >= 0
     But:=But+k3
     else Cod:=Cod+k3

  if (Cod-(k9+k8+k7+k6+k5+k4+k3+k2)) >= 0
     But:=But+k2
     else Cod:=Cod+k2

  if (Cod-(k9+k8+k7+k6+k5+k4+k3+k2+k1)) >= 0
     But:=But+k1
     else Cod:=Cod+k1
    
; Ссылки на исполнение команд при нажатии
gosub kkk9     ;Клавиша 5
gosub kkk8     ;Клавиша 4
gosub kkk7     ;Клавиша 13
gosub kkk6     ;Клавиша 14
gosub kkk5     ;Клавиша 12
gosub kkk4     ;Клавиша 10
gosub kkk3     ;Клавиша 11
gosub kkk2     ;Клавиша 7
gosub kkk1     ;Клавиша 6

ButAll:=But
But=0x0000
         } 
} 
return
;////

;Тут сами действия для конретной клавиши

kkk9:
 if But-ButAll=k9
   {
   SetTimer, Counter9, 50
   }
 if ButAll-But=k9 or But=0
   SetTimer, Counter9, off
 return
Counter9:
   Send {Numpad6}
return

kkk8:
if But-ButAll=k8
   {
    Send {Numpad5}
   }
return

kkk7:
if But-ButAll=k7
   Send , !^8
return

kkk6:
if But-ButAll=k6
   Send , {Numpad8}
return

kkk5:
if But-ButAll=k5
   {
    if kk5=0
       {
        Send {NumpadMult}
        kk5=1
        SetTimer, Counter5, 3000
       }
    else
       {
        if kk5=1
           {
            Send {NumpadDiv}
            kk5=0
            SetTimer, Counter5, off
           }
       }
   }
return
return
Counter5:
kk5=0
return

kkk4:
if But-ButAll=k4
   {
    Send , {Numpad9}
   }
return

kkk3:
if But-ButAll=k3
   {
    Send , {Numpad0}
   }
return

kkk2:
if But-ButAll=k2
    Send {Numpad7}
return

kkk1:
if But-ButAll=k1
   {
    Send , {Numpad4}
   }
return

 
MButton::
Send {Numpad3}
return

WheelUp::
send {Numpad1}
return

WheelDown::
send {Numpad2}
return

XButton1::
  KeyWait, XButton1, D
   Send {Space Down}
  KeyWait, XButton1, U
   Send {Space Up} 
return


XButton2::
  KeyWait, XButton2, D
   SetTimer, CounterX2, 50
  KeyWait, XButton2, U
   SetTimer, CounterX2, off
 return
CounterX2:
   Send , {F12}
return

CapsLock::Suspend 



;///// Пример нажатия и отжатия кнопки
;kkk9:
; if But-ButAll=Butk9
;   {
;   Send {Numpad5 Down}
;   }
; if ButAll-But=Butk9 or But=0
;   Send {Numpad5 Up}
; return
; /////


;///// Пример цикличного нажатия при удержании кнопки
;kkk9:
; if But-ButAll=Butk9
;   {
;   SetTimer, Counter9, 200
;   }
; if ButAll-But=Butk9 or But=0
;   SetTimer, Counter9, off
; return
;Counter9:
;   Send {Numpad5}
;return
;/////


;///// Пример при первом нажатии кномпки происходит нажатие Num7,
;      при повторном нажатии кнопки, в течении 2 сек, происходит нажатие Num1 и дальнейшее чередование,
;      после 2 сек, без нажатия, происходит снова первое нажатие Num7
;kkk9:
;if But-ButAll=Butk9
;   {
;    if k=0
;       {
;        Send {Numpad7}
;        k=1
;        SetTimer, Counter9, 2000
;       }
;    else
;       {
;        if k=1
;           {
;            Send {Numpad1}
;            k=0
;            SetTimer, Counter9, off
;           }
;       }
;   }
;return
;return
;
;Counter9:
;k=0
;return
;/////


;///// Пример первым нажатием включается скрипт, повторным нажатием отключается
;kkk9:
; if But-ButAll=Butk9
;      {
;    if k=0
;       {
;        k=1
;        SetTimer, Counter9, 500
;       }
;    else
;       {
;        if k=1
;           {
;            k=0
;            SetTimer, Counter9, off
;           }
;       }
;   }
;return
;return
;
;Counter9:
;Send , 9
;return
;/////



;XButton1::
;    {
;    Send {Space Down}
;    SetTimer, CounterX, 32
;    }
;return
;CounterX:
;Send {Space Up}
;SetTimer, CounterX, off
;return

Спасибо creature.ws за подсказку с global.
Если кто знает, как полностью избавиться от "залипания" клавиш, прошу писать свои предложения.

9

Re: AHK: Дополнительные кнопки USB устройств через HID-протокол

Вот вариант, который достаточно хорошо работает, только при очень быстром спаме кнопок мышки и высокой загрузки процессора наблюдается залипание кнопок (не отжатие).


Process, Priority, , A  ; приоритет процесса скрипта
#Persistent   ; держать скрипт запущенным
#NoEnv  ; Recommended for performance and compatibility with future AutoHotkey releases.
        ; Отменяет проверку для пустых переменных, не являются ли они переменными окружения (рекомендуется для всех новых скриптов).
;#Warn  ; Recommended for catching common errors.
#Singleinstance, force  ; Определяет, разрешено ли скрипту запускаться снова, если он уже выполняется.
#UseHook, off  ; Заставляет скрипт использовать хук для реализации всех или некоторых горячих клавиш.
SendMode Input  ; Recommended for new scripts due to its superior speed and reliability. ускорренный ввод
                ; Делает Send синонимом SendInput или SendPlay вместо SendEvent (по умолчанию).
                ; Это также заставляет команды Click и MouseMove/Click/Drag использовать указанный метод посылки. 
SetWorkingDir %A_ScriptDir%  ; Ensures a consistent starting directory.
                             ; Изменяет текущую рабочую папку скрипта. 
SetKeyDelay, -1, -1   ; Устанавливает величину паузы, которая вставляется после каждого нажатия, посылаемого командами Send и ControlSend.
SetMouseDelay, -1     ; Задаёт паузу после каждого движения или щелчка мыши.
SetBatchLines, -1

;======================== Меню ===================================
Menu, Tray, NoStandard
Menu, Tray, Icon, %A_WinDir%\system32\shell32.dll, 121, 1
Menu, Tray, Tip, Мouse
Menu, Tray, Add, Off, sub1
Menu, Tray, Add, Help, sub2
Menu, Tray, Add
Menu, Tray, Add, Exit%a_tab%, sub3
;=================================================================

; Задаются значения соответствующие отдельно нажатой кнопки устройства, для подсчета нажатий
k1:=0x0001
k2:=0x0100
k3:=0x0200
k4:=0x0400
k5:=0x0800
k6:=0x1000
k7:=0x2000
k8:=0x4000
k9:=0x8000

Codp:=0x0000        ; Код предыдущего нажатия
Codr:=0x0000        ; Код используемый для расчета нажатий
Codi:=0x0000        ; Код текущего нажатия
;kk5:=0             ;Задается переменная для счета в части для чередования нажатий
;////

DetectHiddenWindows, On  ; Определяет, будут ли скрытые (невидимые) окна видимы для скрипта. 
Process, Exist  ; Выполняет над процессом одну из следующих операций: проверяет существование, изменяет приоритет, закрывает, ожидает закрытия. 
hwnd := WinExist("ahk_class AutoHotkey ahk_pid " ErrorLevel) 
DetectHiddenWindows, Off 
OnMessage(0xFF, "WM_INPUT") 

;Данные HID устройства, можно взять из скрипта "AutoHotkeyRemoteControlDLL", требуется библиотека "AutohotkeyRemoteControl.dll"
Usage       = 1 
UsagePage   = 65280 
;////

;Дальше взято из подобных программ, так что корректно описать действия не могу, далек до программиста
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) 
 
ret := DllCall("RegisterRawInputDevices" 
    , "uint", &dev ; pRawInputDevices (pointer to an array of RAWINPUTDEVICE) 
    , "uint", 1     ; uiNumDevices 
    , "uint", 12)   ; cbSize (size of a RAWINPUTDEVICE structure) 
if (ErrorLevel or !ret) { 
;    MsgBox, RegisterRawInputDevices failed. 
    ExitApp 
    } 
 
WM_INPUT(wParam, lParam) 
{ 
    global
    Critical 
    ; Get required buffer size. 
    DllCall("GetRawInputData", "uint", lParam, "uint", 0x10000003 
        , "uint", 0, "uint*", size, "uint", 16) 

    VarSetCapacity(raw, size, 0) 
    
    ; Get raw input data from handle (lParam) 
    ret := DllCall("GetRawInputData", "uint", lParam, "uint", 0x10000003 
        , "uint", &raw, "uint*", size, "uint", 16, "int") 
  
    if (ErrorLevel or ret = -1) { 
;        MsgBox, Ошибка получения данных 
        return 
        } 

    SetFormat, INTEGER, H 
        count := NumGet(raw, 20) ; dwCount 
        n = 0 
        Loop, %count% 
        { 
            size := NumGet(raw, 16) ; dwSizeHid 
            VarSetCapacity(hex, size*3) 
            Loop, %size% 
            { 
                char := SubStr(NumGet(raw, 24+n+A_Index-1, "UChar"), 3) 
                StringUpper, char, char 
                if (StrLen(char) < 2) 
                    char = 0%char% 
                hex = %hex%%char%

            } 
            n += size 
            if !size 
                hex = "--" 
;//// 

Cod := SubStr(hex, 3 , 4)      ;Вырезается часть получаемого кода, достаточную для просчета клавиш,
                               ;тут можно вырезать и другую информацию, соответствующую какой либо функции,
                               ;например переключение профилей (аля мыши A-TECH серии XL)
;ToolTip hex=%hex%             ;Это для того, что бы посмотреть весь код
Cod = 0x%Cod%                  ;делаем символьную строку числовой, формата Hex

;       Тут, что придумал, то придумал для вычисления нажатий, отжатий и их комбинаций, вроде работает нормально

if Cod = 0
{
;   SetTimer, Counter9, off
   SetTimer, Counter8, off
   SetTimer, Counter7, off
   SetTimer, Counter6, off
   SetTimer, Counter5, off
   SetTimer, Counter4, off
   SetTimer, Counter3, off
   SetTimer, Counter2, off
;   SetTimer, Counter1, off 
}

Codi:=Cod

if (Codi-Codp) > 0 ; нажатие кнопок
Codi:=Codi-Codp
else
Codi:=Codp-Codi

Codr:=Codi


  if (Codr-k9) >= 0
     gosub kkk9
     else Codr:=Codr+k9
 
  if (Codr-(k9+k8)) >=0
     gosub kkk8
     else Codr:=Codr+k8

  if (Codr-(k9+k8+k7)) >= 0
     gosub kkk7
     else Codr:=Codr+k7

  if (Codr-(k9+k8+k7+k6)) >= 0
     gosub kkk6
     else Codr:=Codr+k6

  if (Codr-(k9+k8+k7+k6+k5)) >= 0
     gosub kkk5
     else Codr:=Codr+k5

  if (Codr-(k9+k8+k7+k6+k5+k4)) >= 0
     gosub kkk4
     else Codr:=Codr+k4

  if (Codr-(k9+k8+k7+k6+k5+k4+k3)) >= 0
     gosub kkk3
     else Codr:=Codr+k3

  if (Codr-(k9+k8+k7+k6+k5+k4+k3+k2)) >= 0
     gosub kkk2
     else Codr:=Codr+k2

  if (Codr-(k9+k8+k7+k6+k5+k4+k3+k2+k1)) >= 0
     gosub kkk1
     else Codr:=Codr+k1

Codp:=Cod

         } 
} 

return


;Тут сами действия для конретной клавиши

kkk9:

if (Cod-Codp) > 0
   {
   Send {Numpad6}
   }
else
   {
   
   }
return


kkk8:
if (Cod-Codp) > 0
   {
   SetTimer, Counter8, 50
   }
else
{
   SetTimer, Counter8, off
}
 return
Counter8:
   Send {Numpad5}
return

kkk7:
if (Cod-Codp) > 0
   {
   SetTimer, Counter7, 50
   }
else
{
   SetTimer, Counter7, off
}
 return
Counter7:
   Send {F12}
return


kkk6:
if (Cod-Codp) > 0
{
   SetTimer, Counter6, 50
}
else
{
   SetTimer, Counter6, off
}
 return
Counter6:
   Send {Numpad8}
return



kkk5:
if (Cod-Codp) > 0
{
   SetTimer, Counter5, 50
}
else
{
   SetTimer, Counter5, off
}
 return
Counter5:
   Send {NumpadDiv}{NumpadMult}
return

/*
kkk5:
if But-ButAll=k5
   {
    if kk5=0
       {
        Send {NumpadMult}
        kk5=1
        SetTimer, Counter5, 3000
       }
    else
       {
        if kk5=1
           {
            Send {NumpadDiv}
            kk5=0
            SetTimer, Counter5, off
           }
       }
  }
return
return
Counter5:
kk5=0
return
*/


kkk4:
if (Cod-Codp) > 0
{
   SetTimer, Counter4, 50
}
else
{
   SetTimer, Counter4, off
}
 return
Counter4:
   Send {Numpad9}
return


kkk3:
if (Cod-Codp) > 0
{
   SetTimer, Counter3, 50
}
else
{
   SetTimer, Counter3, off
}
 return
Counter3:
   Send {Numpad0}
return



kkk2:
if (Cod-Codp) > 0
{
   SetTimer, Counter2, 50
}
else
{
   SetTimer, Counter2, off
}
 return
Counter2:
   Send {Numpad7}
return


kkk1:
if (Cod-Codp) > 0
{
   Send , {Numpad4}
}
return



MButton::
  KeyWait, MButton, D
   SetTimer, CounterM, 50
  KeyWait, MButton, U
   SetTimer, CounterM, off
 return
CounterM:
   Send , {Numpad3}
return


WheelUp::
send {Numpad1}
return

WheelDown::
send {Numpad2}
return

XButton1::
  KeyWait, XButton1, D
   Send {Space Down}
  KeyWait, XButton1, U
   Send {Space Up} 
return


XButton2::
  KeyWait, XButton2, D
   SetTimer, CounterX2, 50
  KeyWait, XButton2, U
   SetTimer, CounterX2, off
 return
CounterX2:
   Send , A
   Send , E
return


sub1:
*CapsLock::
Suspend
If A_IsSuspended = 1
    {
    Menu, Tray, Icon, %A_WinDir%\system32\shell32.dll, 220, 1
    menu, tray, rename, Off, On
    }
Else
    {
    menu, tray, icon, %A_WinDir%\system32\shell32.dll, 121, 1
    menu, tray, rename, On, Off
    }
return

sub2:

return

sub3:
Exitapp
return


/*
;///// Пример нажатия и отжатия кнопки
kkk9:
 if But-ButAll=Butk9
   {
   Send {Numpad5 Down}
   }
 if ButAll-But=Butk9 or But=0
   Send {Numpad5 Up}
 return
; /////


;///// Пример цикличного нажатия при удержании кнопки
kkk9:
 if But-ButAll=Butk9
   {
   SetTimer, Counter9, 200
   }
 if ButAll-But=Butk9 or But=0
   SetTimer, Counter9, off
 return
Counter9:
   Send {Numpad5}
return
;/////


;///// Пример при первом нажатии кномпки происходит нажатие Num7,
;      при повторном нажатии кнопки, в течении 2 сек, происходит нажатие Num1 и дальнейшее чередование,
;      после 2 сек, без нажатия, происходит снова первое нажатие Num7
kkk9:
if But-ButAll=Butk9
   {
    if k=0
       {
        Send {Numpad7}
        k=1
        SetTimer, Counter9, 2000
       }
    else
       {
        if k=1
           {
            Send {Numpad1}
            k=0
            SetTimer, Counter9, off
           }
       }
   }
return
return

Counter9:
k=0
return
;/////


;///// Пример первым нажатием включается скрипт, повторным нажатием отключается
kkk9:
 if But-ButAll=Butk9
      {
    if k=0
       {
        k=1
        SetTimer, Counter9, 500
       }
    else
       {
        if k=1
           {
            k=0
            SetTimer, Counter9, off
           }
       }
   }
return
return

Counter9:
Send , 9
return
;/////



XButton1::
    {
    Send {Space Down}
    SetTimer, CounterX, 32
    }
return
CounterX:
Send {Space Up}
SetTimer, CounterX, off
return

*/

Если есть у кого варианты устранения данного недостатка, прошу писать.
Как вариант, думаю заменить часть скрипта, при которой код нажатой кнопки вычисляется после нажатия, на постоянный опрос устройства (мышки), с целью обнаружения нажатой кнопки,а при совпадении двух раз подряд одного и того же кода выполнение действия для данной кнопки.
Только как это сделать, я пока не знаю.