Текст опубликован в личном блоге и его автор не имеет отношения к администрации сайта
;
; 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