Update v1.2: It fixes a bug with the keyboard leds. The leds not used by DrivesLeds (for example the NumLock and CapsLock leds when the ScrollLock led is selected in the DrivesLeds menu) were not handled correctly. For example, after having turned the NumLock on with the keyboard key, the leds was still off.
As usual, the source code, the icon and the compiled exe can be downloaded here:
http://www.4shared.com/file/RrKx1B94/DrivesLeds.html
And here is the source of v1.2:
Code:
#Persistent
#NoEnv
#UseHook off
#SingleInstance, force
version = 1.2
title = Drives Leds v%version%
Menu, Tray, NoStandard
Menu, Tray, Add, Info Window, ToggleInfo
Menu, Tray, Default, Info Window
Menu, Tray, Add, Always On Top, ToggleAlwaysOnTop
Menu, Tray, Add, Transparent bars, ToggleTransparent
Menu, Tray, Add
Menu, Blink, Add, None, BlinkNone
Menu, Blink, Add, Scroll Lock, BlinkScrollLock
Menu, Blink, Add, Num Lock, BlinkNumLock
Menu, Blink, Add, Caps Lock, BlinkCapsLock
Menu, Tray, Add, Blink Keyboard Led, :Blink
Menu, Tray, Add
Menu, Tray, Add, Edit settings, EditSettings
Menu, Tray, Add
Menu, Tray, Add, Rescan drives, GetDrivesList
Menu, Tray, Add, Pause, TogglePause
Menu, Tray, Add
Menu, Tray, Add, Exit, InteractiveExit
Menu, Tray, Click, 1
if (FileExist("paused.ico"))
Menu, Tray, Icon, paused.ico
else
Menu, Tray, Icon
lasticonname = ----
if (FileExist("DrivesLeds.ini"))
inifile = DrivesLeds.ini
else
inifile = %A_AppData%\DrivesLeds.ini
GoSub, LoadSettings
if (! FileExist(inifile))
{
IniWrite, %period%, %inifile%, Config, Period
IniWrite, %preferreddrvs%, %inifile%, Config, PreferredDrives
IniWrite, %includeremovable%, %inifile%, Config, IncludeRemovable
IniWrite, %fontcolor%, %inifile%, Config, FontColor
IniWrite, %backgroundcolor%, %inifile%, Config, BackgroundColor
IniWrite, %barsbkgcolor%, %inifile%, Config, BarsBkgColor
IniWrite, %barswidth%, %inifile%, Config, BarsWidth
IniWrite, %barsheight%, %inifile%, Config, BarsHeight
IniWrite, %keyboardclassnum%, %inifile%, Config, KeyboardClassNum
}
; define the tray icon (Later, only the color map is changed)
IconDataTemplateHex =
( join
2800000010000000
2000000001000400
00000000C0000000
0000000000000000
0000000000000000
00000000
22222200
55555500
88888800
00000000
22222200
55555500
88888800
00000000
22222200
55555500
88888800
00000000
22222200
55555500
88888800
88899888CCCDDCCC
89AAAA98CDEEEEDC
8AABBAA8CEEFFEEC
9ABBBBA9DEFFFFED
9ABBBBA9DEFFFFED
8AABBAA8CEEFFEEC
89AAAA98CDEEEEDC
88899888CCCDDCCC
0001100044455444
0122221045666654
0223322046677664
1233332156777765
1233332156777765
0223322046677664
0122221045666654
0001100044455444
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
)
VarSetCapacity( IconData, 296 )
Loop 296
NumPut( "0x" . SubStr(IconDataTemplateHex,2*A_Index-1,2), IconData, A_Index-1, "Char" )
IconDataTemplateHex := ""
; The 4 parts of the icon's color map are assigned dynamically in different order
; to change the color of each led independently
; No disc activity (grey)
IconDataNHex = 00000000222222005555550088888800
; Read operation only (green)
IconDataRHex = 003300000088000000BB000000EE0000
; Write operation only (red)
IconDataWHex = 00004400000099000000CC000000FF00
; Read and write operations (yellow)
IconDataBHex = 003333000088880000BBBB0000EEEE00
; Unused led (blue)
IconDataUHex = 3000000070000000B0000000F0000000
; initialize the icon structure
InitHIconStruct()
OnExit, CleanExit
OnMessage(0x205, "POPUPMENU")
GoSub, GetDrivesList
GoSub, OpenBlinkLedHandle
Process, Priority,, High
SetTimer, HDD_Monitor, %period%
Return
HDD_Monitor:
iconname=
activity=0
loop, % NumDrives
{
VarSetCapacity(dp, 88)
DllCall("DeviceIoControl", Uint,hDrv%A_Index%, Uint,0x00070020, Uint,0, Uint,0, Uint,&dp, Uint,88, Uint,0, Uint,0 )
nRC := *(&dp+40) | *(&dp+41) << 8 | *(&dp+42) << 16 | *(&dp+43) << 24
nWC := *(&dp+44) | *(&dp+45) << 8 | *(&dp+46) << 16 | *(&dp+47) << 24
RC := Round(100-(100*(1/(1+(nRC-oRC%A_Index%)))))
WC := Round(100-(100*(1/(1+(nWC-oWC%A_Index%)))))
oRC%A_Index% := nRC
oWC%A_Index% := nWC
if (WC + RC)
activity = 1
icon := RC && WC ? "B" : RC ? "R" : WC ? "W" : "N"
iconname = %iconname%%icon%
if %showInfo%
{
GuiControl,, % "ProgressR" . A_Index, % RC
GuiControl,, % "ProgressW" . A_Index, % WC
}
}
if (blinkled && activity != oActivity)
{
DllCall("DeviceIoControl", "Uint",hKeybd, "Uint",0x000B0040, "Uint",0, "Uint",0, "UintP",_ledStatus_, "Uint",4, "UintP",nReturn, "Uint",0)
if (activity)
ledStatus := _ledStatus_ | blinkled << 16
else
ledStatus := _ledStatus_ & ~(blinkled << 16)
DllCall("DeviceIoControl", "Uint",hKeybd, "Uint",0x000B0008, "UintP",ledStatus, "Uint",4, "Uint",0, "Uint",0, "UintP",nReturn, "Uint",0)
oActivity = %activity%
}
iconname := SubStr(iconname . "UUUU", 1, 4)
SetTrayIco(iconname)
Return
GetDrivesList:
GoSub, CloseHandles
HDD_List =
; get the list of fixed drives
DriveGet, tmplist1, List, FIXED
; adds also the removable drives that are not empty
if (includeremovable)
{
DriveGet, tmplist2, List, REMOVABLE
Loop, parse, tmplist2
{
DriveSpaceFree, tmp, %A_LoopField%:\
If (tmp > 0)
tmplist1 = %tmplist1%%A_LoopField%
}
}
; sort the list of drives so that the preferred drives defined in the INI file
; are at the beginning of the list and will be displayed in the tray icon.
Loop, parse, preferreddrvs
{
if (InStr(tmplist1, A_LoopField))
HDD_List = %HDD_List%%A_LoopField%
}
Loop, parse, tmplist1
{
if (NOT InStr(preferreddrvs, A_LoopField))
HDD_List = %HDD_List%%A_LoopField%
}
NumDrives := StrLen(HDD_List)
tmp=%title%`nDrives in tray icon:`n
Loop, parse, HDD_List
{
HDD%A_Index% := A_LoopField
hDrv%A_Index% := DllCall( "CreateFile", Str,"\\.\" . A_LoopField . ":", Uint,0 ,Uint,3, Uint,0, Uint,3, Uint,0, Uint,0), oRC%A_Index% := 0, oWC%A_Index% := 0
if (A_Index <= 4)
{
tmp = %tmp%%A_Space%%A_Space%%A_LoopField%:
if (A_Index == 2)
tmp = %tmp%`n
}
}
Menu, Tray, Tip, %tmp%
GoSub, BuildGUI
if %showInfo%
GoSub, ShowGUI
Return
CloseHandles:
Loop, parse, HDD_List
{
tmp := hDrv%A_Index%
DllCall( "CloseHandle", Uint,tmp)
}
HDD_List =
NumDrives = 0
Return
BuildGUI:
gui, Destroy
Gui, Color, %backgroundcolor%, %backgroundcolor%
gui, font, s7 bold, Arial
; adds a Text widget covering the entire surface of the GUI so that we can drag it
barsrealwidth := barswidth+2
guimaxwidth := barsrealwidth + 12
barsrealheight := (barsheight * 2) + 1
guimaxheight := barsrealheight*NumDrives+2
Gui, Add, Text, x0 y0 w%guimaxwidth% h%guimaxheight% GGuiMove
y = 0
Loop, parse, HDD_List
{
py := round((barsrealheight - 9) / 2) + y
gui, add, Text, x1 y%py% C%fontcolor%, %A_LoopField%:
py := y+1
gui, add, Progress, vProgressR%A_Index% w%barsrealwidth% h%barsheight% x12 y%py% Background%barsbkgcolor% CLime
py := y+barsheight+1
gui, add, Progress, vProgressW%A_Index% w%barsrealwidth% h%barsheight% x12 y%py% Background%barsbkgcolor% CRed
y := y+barsrealheight
}
gui, Margin, 1, -1
gui, -Theme +ToolWindow -Caption
if %alwaysontop%
gui, +AlwaysOnTop
Return
ShowGUI:
if (ypos >= 0)
y = %ypos%
else
y := A_ScreenHeight + ypos - barsrealheight*NumDrives
Gui, Show, X%xpos% Y%y%, %title%
if %transparent%
WinSet, TransColor, %barsbkgcolor%, %title%
Return
ToggleAlwaysOnTop:
if %alwaysontop%
{
gui, -AlwaysOnTop
Menu, Tray, UnCheck, Always On Top
alwaysontop = 0
}
else
{
gui, +AlwaysOnTop
Menu, Tray, Check, Always On Top
alwaysontop = 1
}
Return
TogglePause:
if %A_IsPaused%
{
GoSub, GetDrivesList
Pause Off
SetTrayIco("NNNN")
Menu, Tray, Uncheck, Pause
if %showInfo%
Gui, Show
}
else
{
Menu, Tray, Check, Pause
GoSub, CloseInfoWindow
GoSub, RestoreKeyboardLeds
; we need to freeze the paused icon, so we cannot use SetTrayIco()
if (FileExist("paused.ico"))
Menu, Tray, Icon, paused.ico, 1, 1
lasticonname = ----
GoSub, CloseHandles
Pause On
}
Return
ToggleTransparent:
if %transparent%
{
Menu, Tray, UnCheck, Transparent bars
WinSet, TransColor, OFF, %title%
transparent = 0
}
else
{
Menu, Tray, Check, Transparent bars
WinSet, TransColor, %barsbkgcolor%, %title%
transparent = 1
}
Return
ToggleInfo:
if %showInfo%
{
showInfo = 0
Menu, Tray, UnCheck, Info Window
GoSub, CloseInfoWindow
}
else
{
showInfo = 1
Menu, Tray, Check, Info Window
GoSub, ShowGUI
if %A_IsPaused%
GoSub, TogglePause
}
Return
BlinkNone:
GoSub, RestoreKeyboardLeds
blinkled = 0
GoSub, UpdateBlinkLedMenu
GoSub, CloseBlinkLedHandle
GoSub, SaveDynamicSettings
Return
BlinkScrollLock:
GoSub, RestoreKeyboardLeds
blinkled = 1
GoSub, UpdateBlinkLedMenu
GoSub, OpenBlinkLedHandle
GoSub, SaveDynamicSettings
Return
BlinkNumLock:
GoSub, RestoreKeyboardLeds
blinkled = 2
GoSub, UpdateBlinkLedMenu
GoSub, OpenBlinkLedHandle
GoSub, SaveDynamicSettings
Return
BlinkCapsLock:
GoSub, RestoreKeyboardLeds
blinkled = 4
GoSub, UpdateBlinkLedMenu
GoSub, OpenBlinkLedHandle
GoSub, SaveDynamicSettings
Return
UpdateBlinkLedMenu:
Menu, Blink, UnCheck, None
Menu, Blink, UnCheck, Scroll Lock
Menu, Blink, UnCheck, Num Lock
Menu, Blink, UnCheck, Caps Lock
if (blinkled == 0)
Menu, Blink, Check, None
else if (blinkled == 1)
Menu, Blink, Check, Scroll Lock
else if (blinkled == 2)
Menu, Blink, Check, Num Lock
else if (blinkled == 4)
Menu, Blink, Check, Caps Lock
Return
OpenBlinkLedHandle:
if (blinkled) {
hKeybd := DllCall("CreateFile", "str","\\.\GLOBALROOT\Device\KeyboardClass" . keyboardclassnum, "Uint",0, "Uint",3, "Uint",0, "Uint",3, "Uint",0, "Uint",0)
DllCall("DeviceIoControl", "Uint",hKeybd, "Uint",0x000B0040, "Uint",0, "Uint",0, "UintP",_ledStatus_, "Uint",4, "UintP",nReturn, "Uint",0) ; IOCTL_KEYBOARD_QUERY_INDICATORS
}
oActivity =
Return
CloseBlinkLedHandle:
if (_ledStatus_ != "")
{
DllCall("CloseHandle", "Uint", hKeybd)
_ledStatus_ =
}
oActivity =
Return
RestoreKeyboardLeds:
if (_ledStatus_ != "")
{
old_pause = %A_IsPaused%
SetTimer, HDD_Monitor, off
Sleep, 100
DllCall("DeviceIoControl", "Uint",hKeybd, "Uint",0x000B0040, "Uint",0, "Uint",0, "UintP",_ledStatus_, "Uint",4, "UintP",nReturn, "Uint",0)
if (blinkled != 0)
{
if (blinkled == 1)
CurKey = ScrollLock
else if (blinkled == 2)
CurKey = NumLock
else
CurKey = CapsLock
if (GetKeyState(CurKey, "T"))
_ledStatus_ := _ledStatus_ | blinkled << 16
else
_ledStatus_ := _ledStatus_ & ~(blinkled << 16)
}
DllCall("DeviceIoControl", "Uint",hKeybd, "Uint",0x000B0008, "UintP",_ledStatus_, "Uint",4, "Uint",0, "Uint",0, "UintP",nReturn, "Uint",0) ; IOCTL_KEYBOARD_SET_INDICATORS
if (! old_pause)
SetTimer, HDD_Monitor, %period%
}
oActivity =
Return
LoadSettings:
IniRead, period, %inifile%, Config, Period, 200
IniRead, preferreddrvs, %inifile%, Config, PreferredDrives, CDEF
IniRead, includeremovable, %inifile%, Config, IncludeRemovable, 1
IniRead, fontcolor, %inifile%, Config, FontColor, C0C0C0
IniRead, backgroundcolor, %inifile%, Config, BackgroundColor, 000000
IniRead, barsbkgcolor, %inifile%, Config, BarsBkgColor, 808080
IniRead, barswidth, %inifile%, Config, BarsWidth, 100
IniRead, barsheight, %inifile%, Config, BarsHeight, 4
IniRead, keyboardclassnum, %inifile%, Config, KeyboardClassNum, 0
IniRead, showInfo, %inifile%, AutoConfig, ShowInfoWindow, 0
IniRead, alwaysontop, %inifile%, AutoConfig, AlwaysOnTop, 0
IniRead, transparent, %inifile%, AutoConfig, Transparent, 0
IniRead, xpos, %inifile%, AutoConfig, XPos, 200
IniRead, ypos, %inifile%, AutoConfig, YPos, 200
IniRead, blinkled, %inifile%, AutoConfig, BlinkLed, 1
if (barsheight < 4)
barsheight = 4
if %showInfo%
Menu, Tray, Check, Info Window
if %alwaysontop%
Menu, Tray, Check, Always On Top
if %transparent%
Menu, Tray, Check, Transparent bars
GoSub, UpdateBlinkLedMenu
Return
SaveDynamicSettings:
IniWrite, %showInfo%, %inifile%, AutoConfig, ShowInfoWindow
IniWrite, %alwaysontop%, %inifile%, AutoConfig, AlwaysOnTop
IniWrite, %transparent%, %inifile%, AutoConfig, Transparent
WinGetPos, xpos1, ypos1, , , %title%
if xpos1 !=
{
xpos = %xpos1%
ypos = %ypos1%
if (ypos > A_ScreenHeight * 0.8)
{
ypos := (A_ScreenHeight - ypos - barsrealheight*NumDrives) * -1
if (ypos >= 0)
ypos = %ypos1%
}
IniWrite, %xpos%, %inifile%, AutoConfig, XPos
IniWrite, %ypos%, %inifile%, AutoConfig, YPos
}
IniWrite, %blinkled%, %inifile%, AutoConfig, BlinkLed
Return
EditSettings:
if %editsettings%
Return
editsettings = 1
Menu, Tray, Check, Edit settings
if (showInfo && ! A_IsPaused)
GoSub, CloseInfoWindow
RunWait, Notepad.exe "%inifile%"
GoSub, LoadSettings
GoSub, BuildGUI
if (showInfo && ! A_IsPaused)
GoSub, ShowGUI
if (! A_IsPaused)
{
SetTimer, HDD_Monitor, OFF
SetTimer, HDD_Monitor, %period%
}
Menu, Tray, UnCheck, Edit settings
editsettings = 0
Return
GuiClose:
GuiEscape:
showInfo = 0
Menu, Tray, UnCheck, Info Window
GoSub, CloseInfoWindow
Return
CloseInfoWindow:
GoSub, SaveDynamicSettings
Gui, Hide
Return
GuiMove:
PostMessage, 0xA1, 2,,, A
Return
POPUPMENU(wParam, lParam)
{
if (A_Gui == 1)
Menu, Tray, Show
Return 0
}
InteractiveExit:
ExitApp
Return
CleanExit:
SetTimer, HDD_Monitor, OFF
GoSub, CloseInfoWindow
sleep, 100
GoSub, CloseHandles
GoSub, RestoreKeyboardLeds
GoSub, CloseBlinkLedHandle
ExitApp
SetTrayIco(IcoName)
{
global NID, lasticonname, IconData, IconDataNHex, IconDataRHex, IconDataWHex, IconDataBHex, IconDataUHex
static h_icon
if (IcoName == lasticonname)
return
; build icon data
offset = 39
Loop, parse, IcoName
{
if (A_LoopField != SubStr(lasticonname,A_Index,1))
{
icmh := IconData%A_LoopField%Hex
loop, 16
NumPut( "0x" . SubStr(icmh,2*A_Index-1,2), IconData, offset+A_Index, "Char" )
}
offset := offset + 16
}
; build icon
old_h_icon := h_icon
h_icon := DllCall( "CreateIconFromResourceEx", UInt,&IconData
, UInt,0, Int,1, UInt,196608, Int,16, Int,16, UInt,0 )
; display tray icon
NumPut( h_Icon,NID,20 )
DllCall( "shell32\Shell_NotifyIcon", UInt,0x1, UInt,&NID )
if (old_h_icon)
DllCall( "DestroyIcon", Uint,old_h_Icon)
lasticonname = %IcoName%
return
}
InitHIconStruct()
{
global NID
PID := DllCall("GetCurrentProcessId")
VarSetCapacity( NID, 444, 0 )
NumPut( 444,NID )
DetectHiddenWindows, On
NumPut( WinExist(A_ScriptFullPath " ahk_class AutoHotkey ahk_pid " PID), NID,4 )
DetectHiddenWindows, Off
NumPut( 1028,NID,8 )
NumPut( 2,NID,12 )
return
}