Прикручиваем мультимедиа-кнопки к клаве

для раздела Блоги
Есть в продаже отличные клавы, например эти две, но с существенным недостатком: отсутствием мультимедиа-кнопок. Видимо это сделано специально, что бы уменьшить размер клавы. ИМХО подход неправильный. Даже ыпл на своей компактной клаве нашла место для кнопок регулировки громкости, другие производители добавляют всего одну кнопку Fn, что тоже неплохо. В общем варианты есть, жаль что конструкторы ножничных клав не в теме. :(

Это недоразумение можно исправить с помощью гениальной проги Autohotkey. Какой только фигней на моем компе она не занималась… Вот сейчас за вечер добавил поддержку мультимедиа-кнопок для любой клавы.

Функции мм-кнопок будут выполнять обычные кнопки в комбинации с кнопкой Win. Конечно, придется обходить стороной комбинации, которые используются виндою, например Win+Home в семерке. Shift, Ctrl, Alt использовать нельзя, потому что будут конфликты с «горячими кнопками» в различных приложениях. Использовать несколько модификаторов что бы избежать конфликтов, например Alt+Shift+Кнопка, мне неудобно. Есть еще вариант заменить Win на ScrollLock (видел в проге, уже не помню какой), да тянуться далековато. Оставлю как запасной вариант.

Регулировка звука:
  • Win+Q — повышение громкости
  • Win+A — понижение громкости
  • Win+Z — приглушить звук
Эти комбинации используются наиболее часто, их можно быстро нажать левой рукой, не отрывая правую от крысы.

Но регулировать громкость кнопками неудобно. Идеальный вариант — крутилка (была на моей старой клаве A4 KX-5MU). Единственная крутилка, которая у меня есть — это колесо мыши. Поэтому:
  • Win+Колесо вверх — повышение громкости
  • Win+Колесо вниз — понижение громкости
Теоретически иногда могут быть ложные срабатывания, если колесо без щелчков и очень легко крутится. У меня все норм. Возможно, потом добавлю регулировку чувствительности и ускорения.

Дополнительный бонус вышеприведенных сочетаний в том, что они всегда регулируют только главную громкость звуковой карты независимо от запущенных приложений. Нажатие «настоящих» мм-кнопок некоторые проигрыватели перехватывают и изменяют свою громкость, что меня жутко раздражает. Теперь эти кривые проги не смогут пакостить, потому что регулировкой звука займется Проводник.

Управление проигрывателем:
  • Win+F1 — проигрывание/пауза
  • Win+F2 — остановка
  • Win+F3 — предыдущая композиция
  • Win+F4 — следующая композиция

У некоторых клав кнопка вызова куркулятора расположена рядом с блоком цифровых кнопок. Очень удобно. Поэтому:
  • Win+NumLock — запуск куркулятора
В качестве бонуса после запуска куркулятора всегда будет включен NumLock. Так же, если куркулятор уже запущен, то он будет активирован вместо запуска еще одной копии.

Ну и последняя кнопка, которая есть на многих клавах — это отправление винды в спячку. Я не использую спящий режим и гибернацию, поэтому комп полностью отключается. В скрипте несложно заменить выключение на спящий режим. Уже ощутили гибкость программных мм-кнопок? :)
  • Правая Win+Esc — выключение компьютера
Использование только правой кнопки Win предотвратит случайное нажатие.

Далее приведен текст скрипта. Как видите, он очень короткий. Сохраните его в кодировке Unicode в файле с расширением ahk. Установите Autohotkey Unicode последней версии по ссылке в начале этой заметки и пихайте скрипт в автозагрузку. В некоторых сетевых играх PunkBuster может ругаться на запущенный Autohotkey. Довольно редкий случай. Решение: скомпилируйте скрипт с помощью ahk2exe.exe (входит в состав Autohotkey) и запускайте вместо скрипта полученный exe-шник.

Дополнение от 12.08.2013
Добавлена вторая версия скрипта. Улучшения:
  • Добавлен индикатор громкости.
  • Добавлена настройка скорости регулировки громкости кнопками и колесом мыши.
  • Добавлен обход пары косяков Криватива.
  • Отключен автоповтор у кнопок управления проигрывателем.



;
; Version 2013.08.23
; Без индикатора громкости.
;
#Warn
#NoEnv
#KeyHistory 0
#SingleInstance FORCE
#UseHook
#NoTrayIcon
#MenuMaskKey VK07
ListLines OFF
SetBatchLines -1
Process PRIORITY,, HIGH

global CALCULATOR_EXECUTABLE   := "CALC.EXE"
global APPCOMMAND_VOLUME_MUTE  := 0x80000
global APPCOMMAND_VOLUME_DOWN  := 0x90000
global APPCOMMAND_VOLUME_UP    := 0xa0000

SendAppCommand(nCommand, fShell)
{
    hwnd := fShell ? DllCall("USER32.DLL\GetShellWindow", "UPTR") : WinExist("A")
    if (hwnd)
    {
        DllCall("USER32.DLL\SendMessageW", "UPTR", hwnd, "UINT", 0x0319, "PTR", 0, "PTR", nCommand) ; WM_APPCOMMAND
    }
}

HandlePlayerHotkey(strOriginalKey, strReplaceKey)
; Встроенное в autohotkey переназначение кнопок не используется, потому что нужно запретить автоповтор.
{
    SendInput {BLIND}{%strReplaceKey% DOWNTEMP}
    KeyWait %strOriginalKey%
    SendInput {BLIND}{%strReplaceKey% UP}
}

#Q::SendAppCommand(APPCOMMAND_VOLUME_UP, true)
#A::SendAppCommand(APPCOMMAND_VOLUME_DOWN, true)
#Z::SendAppCommand(APPCOMMAND_VOLUME_MUTE, true)
#WheelUp::SendAppCommand(APPCOMMAND_VOLUME_UP, true)
#WheelDown::SendAppCommand(APPCOMMAND_VOLUME_DOWN, true)
#F1::HandlePlayerHotkey("F1", "MEDIA_PLAY_PAUSE")
#F2::HandlePlayerHotkey("F2", "MEDIA_STOP")
#F3::HandlePlayerHotkey("F3", "MEDIA_PREV")
#F4::HandlePlayerHotkey("F4", "MEDIA_NEXT")

~#NumLock::
    Process EXIST, %CALCULATOR_EXECUTABLE%
    if (ErrorLevel)
    {
        WinActivate AHK_PID %ErrorLevel%
    }
    else
    {
        Run %CALCULATOR_EXECUTABLE%, %A_WinDir%\SYSTEM32
    }
    KeyWait NUMLOCK
    SetNumLockState ON
    return

>#Esc::Shutdown 8



;
; Version 2013.08.23
; С индикатором громкости.
;
#Warn
#NoEnv
#KeyHistory 0
#SingleInstance FORCE
#NoTrayIcon
#UseHook
#MenuMaskKey VK07
ListLines OFF
SetBatchLines -1
Process PRIORITY,, HIGH

global VOLUME_STEP_FIRST        := 2
global VOLUME_STEP_NEXT         := 2
global VOLUME_STEP_WHEEL        := 4
global VOLUME_INTERVAL_FIRST    := 200
global VOLUME_INTERVAL_NEXT     := 40
global VOLUME_CRIVATIVE_STEP    := 1 ; 0.3
global VOLUME_CRIVATIVE_FLOOR   := 0 ; 4.2
global INDICATOR_LOCATION_X     := 80
global INDICATOR_LOCATION_Y     := 80
global INDICATOR_SHOW_TIME      := 1000
global CALCULATOR_EXECUTABLE    := "CALC.EXE"

global g_pIDirectDraw7 := 0 ; #Warn
global g_nVolume := -1, g_fMute := -1
global g_hwndGui := 0, g_hwndProgress, g_idProgress, g_idImage, g_fThemed, g_hTheme

InitInstance()
return

InitInstance()
{
    DllCall("KERNEL32.DLL\LoadLibraryW", "WSTR", "DDRAW.DLL", "UPTR")
    VarSetCapacity(IID_IDirectDraw7, 16)
    DllCall("OLE32.DLL\CLSIDFromString", "WSTR", "{15E65EC0-3B9C-11D2-B92F-00609797EA5B}", "UPTR", &IID_IDirectDraw7)
    ; Указать DDCREATE_EMULATIONONLY вместо 0, что бы создание IDirectDraw7 не потребляло 20 MiB оперативной памяти.
    DllCall("DDRAW.DLL\DirectDrawCreateEx", "UPTR", 2, "UPTR*", g_pIDirectDraw7, "UPTR", &IID_IDirectDraw7, "UPTR", 0)
    ; Если тема оформления отлична от windows classic, то у виндового progress bar появляется анимация, отключить
    ; которую простыми способами нельзя. Придется рисовать progress bar самому.
    DllCall("KERNEL32.DLL\LoadLibraryW", "WSTR", "UXTHEME.DLL", "UPTR")
    OnMessage(0x2B, "DrawIndicator") ; WM_DRAWITEM
}

IsNotificationsAllowed()
; Вывод окна, даже неактивного, конфликтует с полноэкранными приложениями Direct3D. SHQueryUserNotificationState() по
; непонятной причине не возвращает QUNS_RUNNING_D3D_FULL_SCREEN. IDirectDraw7::TestCooperativeLevel() возвращает
; DDERR_EXCLUSIVEMODEALREADYSET если активно полноэкранное Direct3D или OpenGL приложение, а так же если неактивна
; сессия пользователя (Win+L, Alt+Ctrl+Del, FUS). Проверялось в Windows 7.
{
    ; IDirectDraw7::TestCooperativeLevel() != DDERR_EXCLUSIVEMODEALREADYSET
    return DllCall(NumGet(NumGet(g_pIDirectDraw7 + 0) + 26 * A_PtrSize), "UPTR", g_pIDirectDraw7) != -2005532091
}

ShowIndicator(nOldVolume, fOldMute)
{
    if (g_hwndGui)
    {
        UpdateIndicator(nOldVolume, fOldMute)
    }
    else if (IsNotificationsAllowed())
    {
        g_fThemed := DllCall("UXTHEME.DLL\IsAppThemed")
        Gui -CAPTION +BORDER +ALWAYSONTOP +DISABLED +E0x8000000 HWNDg_hwndGui ; WS_EX_NOACTIVATE
        DllCall("USER32.DLL\SetClassLongW", "UPTR", g_hwndGui, "INT", -26, "INT"
            , DllCall("USER32.DLL\GetClassLongW", "UPTR", g_hwndGui, "INT", -26)
            | 0x20000) ; CS_DROPSHADOW
        if (g_fThemed)
        {
            Gui ADD, TEXT, W16 H150 X5 Y5 +0x0D HWNDg_hwndProgress Vg_idProgress ; SS_OWNERDRAW
            g_hTheme := DllCall("UXTHEME.DLL\OpenThemeData", "UPTR", g_hwndProgress, "WSTR", "PROGRESS", "UPTR")
            DllCall("UXTHEME.DLL\BufferedPaintInit")
        }
        else
        {
            Gui ADD, PROGRESS, W16 H150 X5 Y5 +VERTICAL -SMOOTH HWNDg_hwndProgress Vg_idProgress
            SendMessage 0x401,, 0x00640000 | VOLUME_CRIVATIVE_FLOOR,, AHK_ID %g_hwndProgress% ; PBM_SETRANGE VOLUME_CRIVATIVE_FLOOR..100
        }
        Gui ADD, PICTURE, W16 H16 X5 Y160 Vg_idImage
        SysGet nMwa, MONITORWORKAREA
        Gui SHOW, % "NOACTIVATE W26 H181"
            . " X" . (nMwaRight  - (INDICATOR_LOCATION_X +  26) * A_ScreenDPI / 96)
            . " Y" . (nMwaBottom - (INDICATOR_LOCATION_Y + 181) * A_ScreenDPI / 96)
        ; BUG OnMessage() по непонятной причине не посылает сообщение во время выполнения Gui SHOW,
        ; поэтому g_hwndProgress не прорисовывается. Обновляем окно уже после Gui SHOW. Это может
        ; привести к небольшому мерцанию.
        UpdateIndicator(-1, -1)
    }
}

DestroyIndicator()
{
    if (g_hwndGui)
    {
        ; На тень анимация не действует, но с короткой длительностью этого не заметно.
        DllCall("USER32.DLL\AnimateWindow", "UPTR", g_hwndGui, "UINT", 80, "UINT", 0x90000) ; AW_BLEND | AW_HIDE
        if (g_fThemed)
        {
            DllCall("UXTHEME.DLL\BufferedPaintUnInit")
            DllCall("UXTHEME.DLL\CloseThemeData", "UPTR", g_hTheme)
        }
        Gui DESTROY
        g_hwndGui := 0
    }
}

UpdateIndicator(nOldVolume, fOldMute)
{
    if (g_nVolume == nOldVolume && g_fMute == fOldMute)
    {
        return
    }
    if (g_fMute != fOldMute)
    {
        if (g_fMute)
        {
            GuiControl,, g_idImage, *ICON3 %A_WinDir%\SYSTEM32\SNDVOL.EXE
            if (!g_fThemed)
            {
                SendMessage 0x410, 2,,, AHK_ID %g_hwndProgress% ; PBM_SETSTATE PBST_ERROR
            }
        }
        else
        {
            GuiControl,, g_idImage, *ICON2 %A_WinDir%\SYSTEM32\SNDVOL.EXE
            if (!g_fThemed)
            {
                SendMessage 0x410, 1,,, AHK_ID %g_hwndProgress% ; PBM_SETSTATE PBST_NORMAL
            }
        }
    }
    if (g_nVolume != nOldVolume && !g_fThemed)
    {
        GuiControl,, g_idProgress, %g_nVolume%
        if (g_fMute)
        {
            ; HACK В состоянии PBST_ERROR по непонятной причине первое PBM_SETPOS не срабатывает.
            GuiControl,, g_idProgress, %g_nVolume%
        }
    }
    if (g_fThemed)
    {
        ; GuiControl MOVEDRAW не подходит, потому что посылает приводящий к мерцанию WM_ERASEBKGND.
        WinSet REDRAW,, AHK_ID %g_hwndProgress%
    }
}

DrawIndicator(wParam, lParam)
{
    hdcScreen := NumGet(lParam + 16 + A_PtrSize + A_PtrSize)
    prcItem := lParam + 16 + A_PtrSize + A_PtrSize + A_PtrSize
    hdcBuffer := 0 ; #Warn
    hBuffer := DllCall("UXTHEME.DLL\BeginBufferedPaint", "UPTR", hdcScreen, "UPTR", prcItem, "INT", 0, "UPTR", 0, "UPTR*", hdcBuffer) ; BPBF_COMPATIBLEBITMAP
    if (DllCall("UXTHEME.DLL\IsThemeBackgroundPartiallyTransparent", "UPTR", g_hTheme, "INT", 12, "INT", 1)) ; PP_TRANSPARENTBARVERT, PBBVS_NORMAL
    {
        DllCall("UXTHEME.DLL\DrawThemeParentBackground", "UPTR", g_hwndProgress, "UPTR", hdcBuffer, "UPTR", prcItem)
    }
    DllCall("UXTHEME.DLL\DrawThemeBackground", "UPTR", g_hTheme, "UPTR", hdcBuffer, "INT", 12, "INT", 1, "UPTR", prcItem, "UPTR", 0) ; PP_TRANSPARENTBARVERT, PBBVS_NORMAL
    NumPut(NumGet(prcItem + 12, "INT") * (100 - g_nVolume) / (100 - VOLUME_CRIVATIVE_FLOOR), prcItem + 4, "INT")
    DllCall("UXTHEME.DLL\DrawThemeBackground", "UPTR", g_hTheme, "UPTR", hdcBuffer, "INT", 6, "INT", g_fMute + 1, "UPTR", prcItem, "UPTR", 0) ; PP_FILLVERT, PBFS_NORMAL / PBFS_ERROR
    DllCall("UXTHEME.DLL\EndBufferedPaint", "UPTR", hBuffer, "INT", 1)
    return 1
}

SetMasterVolume(nValue)
{
    if (g_nVolume < 0)
    {
        SoundGet g_nVolume
        SoundGet g_fMute,, MUTE
        if (g_nVolume < VOLUME_CRIVATIVE_FLOOR)
        {
            g_nVolume := VOLUME_CRIVATIVE_FLOOR
        }
        g_fMute := g_fMute != "Off"
    }
    
    nOldVolume := g_nVolume, fOldMute := g_fMute
    if (nValue == 0)
    {
        g_fMute := !g_fMute
    }
    else
    {
        ; Пока и линейный сгодится.
        nStepFactor := (1 - VOLUME_CRIVATIVE_STEP) * g_nVolume / 100 + VOLUME_CRIVATIVE_STEP
        g_nVolume += nValue * nStepFactor
        if (g_nVolume < VOLUME_CRIVATIVE_FLOOR)
        {
            g_nVolume := VOLUME_CRIVATIVE_FLOOR
        }
        if (g_nVolume > 100)
        {
            g_nVolume := 100
        }
        if (nValue > 0)
        {
            g_fMute := false
        }
    }
    ShowIndicator(nOldVolume, fOldMute)
    SetTimer Timer_DestroyIndicator, % -INDICATOR_SHOW_TIME
    if (g_nVolume != nOldVolume)
    {
        SoundSet g_nVolume
    }
    if (g_fMute != fOldMute)
    {
        SoundSet g_fMute,, MUTE
    }
    return
Timer_DestroyIndicator:
    Critical
    DestroyIndicator()
    g_nVolume := -1, g_fMute := -1
    return
}

HandleVolumeHotkey(nValue, strKey := "", nFirstInterval := 0, nNextInterval := 0, nNextStep := 0)
{
    ; Если нажато несколько кнопок управлением громкостью, то обрабатываться будет только первая из них.
    static fBusy := false
    if (!fBusy)
    {
        fBusy := true
        SetMasterVolume(nValue)
        if (strKey)
        {
            if (!nFirstInterval)
            {
                KeyWait % strKey
            }
            else
            {
                KeyWait % strKey, % "T" . (nFirstInterval / 1000)
                if (ErrorLevel)
                {
                    SetTimer Timer_ChangeVolume, % nNextInterval
                    gosub Timer_ChangeVolume
                    KeyWait % strKey
                    SetTimer Timer_ChangeVolume, OFF
                }
            }
        }
        fBusy := false
    }
    return
Timer_ChangeVolume:
    SetMasterVolume(nNextStep)
    return
}

HandlePlayerHotkey(strOriginalKey, strReplaceKey)
; Встроенное в autohotkey переназначение кнопок не используется, потому что нужно запретить автоповтор.
{
    SendInput {BLIND}{%strReplaceKey% DOWNTEMP}
    KeyWait %strOriginalKey%
    SendInput {BLIND}{%strReplaceKey% UP}
}

#Q::HandleVolumeHotkey(VOLUME_STEP_FIRST, "Q", VOLUME_INTERVAL_FIRST, VOLUME_INTERVAL_NEXT, VOLUME_STEP_NEXT)
#A::HandleVolumeHotkey(-VOLUME_STEP_FIRST, "A", VOLUME_INTERVAL_FIRST, VOLUME_INTERVAL_NEXT, -VOLUME_STEP_NEXT)
#Z::HandleVolumeHotkey(0, "Z")
#WheelUp::HandleVolumeHotkey(VOLUME_STEP_WHEEL)
#WheelDown::HandleVolumeHotkey(-VOLUME_STEP_WHEEL)
#F1::HandlePlayerHotkey("F1", "MEDIA_PLAY_PAUSE")
#F2::HandlePlayerHotkey("F2", "MEDIA_STOP")
#F3::HandlePlayerHotkey("F3", "MEDIA_PREV")
#F4::HandlePlayerHotkey("F4", "MEDIA_NEXT")

~#NUMLOCK::
    Process EXIST, %CALCULATOR_EXECUTABLE%
    if (ErrorLevel)
    {
        WinActivate AHK_PID %ErrorLevel%
    }
    else
    {
        Run %CALCULATOR_EXECUTABLE%, %A_WinDir%\SYSTEM32
    }
    KeyWait NUMLOCK
    SetNumLockState ON
    return

>#ESC::Shutdown 8
Telegram-канал @overclockers_news - это удобный способ следить за новыми материалами на сайте. С картинками, расширенными описаниями и без рекламы.
Оценитe материал
рейтинг: 1.0 из 5
голосов: 1

Возможно вас заинтересует

Популярные новости

Сейчас обсуждают