geekhack
geekhack Projects => Making Stuff Together! => DIY Discussions ARCHIVE => Topic started by: sixty on Fri, 04 September 2009, 12:49:28
-
The idea for this project came up in ripster's article about swapping LEDs in a Filco (http://geekhack.org/showwiki.php?title=Island:6953). The purpose here is to make use of the LEDs on a keyboard for things like notifications or simply for fun.
Until a while ago I was under the impression that changing the LED state would be impossible without also changing the lock state of the according lock key. JohnElliott had brought up (http://geekhack.org/showpost.php?p=113275&postcount=5) an ancient method that proved that LEDs can indeed be toggled, while keeping the lock state untouched.
The method used for this is IOCTL_KEYBOARD_SET_INDICATORS (http://msdn.microsoft.com/en-us/library/ms791035.aspx):
Windows Driver Kit: Human Input Devices
IOCTL_KEYBOARD_SET_INDICATORS
Operation
The IOCTL_KEYBOARD_SET_INDICATORS request sets the keyboard indicators.
Input
AssociatedIrp.SystemBuffer points to a client-allocated buffer that inputs a KEYBOARD_INDICATOR_PARAMETERS structure. The client sets the indicator parameters in this structure.
Parameters.DeviceIoControl.InputBufferLength is set to a value greater than or equal to the size, in bytes, of a KEYBOARD_INDICATOR_PARAMETERS structure.
Based on this old code example (http://www.codeguru.com/cpp/w-p/system/keyboard/article.php/c2825) I was able to wrap up a wrapper for the old DLL in .NET. The output so far can be seen below:
What I still plan on doing is to add support for plug and play - since according to Molto (http://geekhack.org/showpost.php?p=113477&postcount=12) it currently does not work because the code simply tries to access the first keyboard found. This results in this only working with a keyboard connected by PS2 on my machine. USB ones are not picked up.
It doesn't work because the sample code is not PnP-aware (hyphen correct?). It just tries to access the first keyboard device (which is often, but not necessarily, a virtual one) in an obsolete way.
The right approach is to enumerate all GUID_DEVINTERFACE_KEYBOARD (http://msdn.microsoft.com/en-us/library/bb663141.aspx) instances by using the SetupAPI (http://msdn.microsoft.com/en-us/library/ms791318.aspx) and RegisterDeviceNotification() (http://msdn.microsoft.com/en-us/library/aa363431.aspx).
Alternatively, you can set ConnectMultiplePorts and SendOutputToAllPorts in HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Kbdclass\Parameters to 1 (and restart) in order to run the keyboard class driver in compatibility mode.
So things planned to improve are:
- Improve the DLL to handle PnP
- Provide proper DLL wrappers for use in your own projects
- Provide a precompiled cli program that can be easily used in scripting (ahk, mirc, whatever!)
- Improve the DLL to allow permanent toggling (right now it only supports to light up a LED for a specified period of time)
-
And to import my original question to Molto:
Alternatively, you can set ConnectMultiplePorts and SendOutputToAllPorts in HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Kbdclass\Parameters to 1 (and restart) in order to run the keyboard class driver in compatibility mode.
This does not seem to work for me. I checked the reg entry and it was already on 1 for me - however yet only my PS2 keyboard responds. All USB keyboards do not respond (no matter if the PS2 is plugged in or not).
-
Can you please run WinObj (http://technet.microsoft.com/en-us/sysinternals/bb896657.aspx) and verify that there is only one \Device\KeyboardClass* object?
-
I couldn't get any of this to work, but I found relevant Autohotkey functions, and thusly wrote a script:
#NoTrayIcon
#SingleInstance Force
;;; to use this script properly, change the h_device parameter in KeyboardLED()
; (this could be potentially annoying in case you arbitrarily add/remove
; keyboards...)
; i'm just using this script to flash numlock and scrolllock states:
Bell() {
delay = 60
Loop, 4
{
KeyboardLED(1, "switch")
Sleep, %delay%
KeyboardLED(2, "switch")
Sleep, %delay%
}
KeyboardLED(3, "on")
}
Loop, 3
{
Bell()
Sleep, 500
}
Sleep, 500
KeyboardLED(0,"off") ; unpower all LEDs
return
/*
; some example patterns.
Loop, 3 ; flash all LEDs
{
KeyboardLED(7,"on")
Sleep, 500
KeyboardLED(7,"off")
Sleep, 500
}
Loop, 3 ; flash ScrollLock LED
{
KeyboardLED(7,"off")
KeyboardLED(4,"switch")
Sleep, 500
KeyboardLED(7,"off")
Sleep, 500
}
Loop, 3 ; cycle all LEDs left to right
{
KeyboardLED(7,"off")
Sleep, 250
KeyboardLED(2,"switch")
Sleep, 250
KeyboardLED(4,"switch")
Sleep, 250
KeyboardLED(1,"switch")
Sleep, 250
}
Loop, 3 ; progress bar in LEDs
{
KeyboardLED(7,"off")
Sleep, 500
KeyboardLED(2,"switch")
Sleep, 500
KeyboardLED(6,"switch")
Sleep, 500
KeyboardLED(7,"switch")
Sleep, 500
}
Loop, 3 ; Knight Rider KITT cycling all LEDs ;-)
{
KeyboardLED(2,"switch")
Sleep, 500
KeyboardLED(4,"switch")
Sleep, 500
KeyboardLED(1,"switch")
Sleep, 500
KeyboardLED(4,"switch")
Sleep, 500
}
*/
; Keyboard LED control (capslock/numlock/scrolllock lights), thanks to
; http://www.autohotkey.com/forum/viewtopic.php?t=10532
; USAGE: KeyboardLED(LEDvalue,"Cmd")
; @LEDvalue: ScrollLock=1, NumLock=2, CapsLock=4 ; Cmd = on/off/switch
KeyboardLED(LEDvalue, Cmd) ; LEDvalue: ScrollLock=1, NumLock=2, CapsLock=4 ; Cmd = on/off/switch
{
Static h_device
If ! h_device ; initialise
{
;;; increment the digit at the end to discover which keyboard's LEDs you want to control
device =\Device\KeyBoardClass1
SetUnicodeStr(fn,device)
h_device:=NtCreateFile(fn,0+0x00000100+0x00000080+0x00100000,1,1,0x00000040+0x00000020,0)
}
VarSetCapacity( output_actual, 4, 0 )
input_size = 4
VarSetCapacity( input, input_size, 0 )
If Cmd= switch ;switches every LED according to LEDvalue
KeyLED:= LEDvalue
If Cmd= on ;forces all choosen LED's to ON (LEDvalue= 0 ->LED's according to keystate)
KeyLED:= LEDvalue | (GetKeyState("ScrollLock", "T") + 2*GetKeyState("NumLock", "T") + 4*GetKeyState("CapsLock", "T"))
If Cmd= off ;forces all choosen LED's to OFF (LEDvalue= 0 ->LED's according to keystate)
{
LEDvalue:= LEDvalue ^ 7
KeyLED:= LEDvalue & (GetKeyState("ScrollLock", "T") + 2*GetKeyState("NumLock", "T") + 4*GetKeyState("CapsLock", "T"))
}
; EncodeInteger( KeyLED, 1, &input, 2 ) ;input bit pattern (KeyLED): bit 0 = scrolllock ;bit 1 = numlock ;bit 2 = capslock
input := Chr(1) Chr(1) Chr(KeyLED)
input := Chr(1)
input=
success := DllCall( "DeviceIoControl"
, "uint", h_device
, "uint", CTL_CODE( 0x0000000b ; FILE_DEVICE_KEYBOARD
, 2
, 0 ; METHOD_BUFFERED
, 0 ) ; FILE_ANY_ACCESS
, "uint", &input
, "uint", input_size
, "uint", 0
, "uint", 0
, "uint", &output_actual
, "uint", 0 )
}
CTL_CODE( p_device_type, p_function, p_method, p_access )
{
Return, ( p_device_type << 16 ) | ( p_access << 14 ) | ( p_function << 2 ) | p_method
}
NtCreateFile(ByRef wfilename,desiredaccess,sharemode,createdist,flags,fattribs)
{
VarSetCapacity(fh,4,0)
VarSetCapacity(objattrib,24,0)
VarSetCapacity(io,8,0)
VarSetCapacity(pus,8)
uslen:=DllCall("lstrlenW","str",wfilename)*2
InsertInteger(uslen,pus,0,2)
InsertInteger(uslen,pus,2,2)
InsertInteger(&wfilename,pus,4)
InsertInteger(24,objattrib,0)
InsertInteger(&pus,objattrib,8)
status:=DllCall("ntdll\ZwCreateFile","str",fh,"UInt",desiredaccess,"str",objattrib,"str",io,"UInt",0,"UInt",fattribs
,"UInt",sharemode,"UInt",createdist,"UInt",flags,"UInt",0,"UInt",0, "UInt")
return % ExtractInteger(fh)
}
SetUnicodeStr(ByRef out, str_)
{
VarSetCapacity(st1, 8, 0)
InsertInteger(0x530025, st1)
VarSetCapacity(out, (StrLen(str_)+1)*2, 0)
DllCall("wsprintfW", "str", out, "str", st1, "str", str_, "Cdecl UInt")
}
ExtractInteger(ByRef pSource, pOffset = 0, pIsSigned = false, pSize = 4)
; pSource is a string (buffer) whose memory area contains a raw/binary integer at pOffset.
; The caller should pass true for pSigned to interpret the result as signed vs. unsigned.
; pSize is the size of PSource's integer in bytes (e.g. 4 bytes for a DWORD or Int).
; pSource must be ByRef to avoid corruption during the formal-to-actual copying process
; (since pSource might contain valid data beyond its first binary zero).
{
Loop %pSize% ; Build the integer by adding up its bytes.
result += *(&pSource + pOffset + A_Index-1) << 8*(A_Index-1)
if (!pIsSigned OR pSize > 4 OR result < 0x80000000)
return result ; Signed vs. unsigned doesn't matter in these cases.
; Otherwise, convert the value (now known to be 32-bit) to its signed counterpart:
return -(0xFFFFFFFF - result + 1)
}
InsertInteger(pInteger, ByRef pDest, pOffset = 0, pSize = 4)
; The caller must ensure that pDest has sufficient capacity. To preserve any existing contents in pDest,
; only pSize number of bytes starting at pOffset are altered in it.
{
Loop %pSize% ; Copy each byte in the integer into the structure as raw binary data.
DllCall("RtlFillMemory", "UInt", &pDest + pOffset + A_Index-1, "UInt", 1, "UChar", pInteger >> 8*(A_Index-1) & 0xFF)
}
-
Is it possible to disable an LED without making it 'blink' when the CAPSLOCK key is pressed?
I tried
~CapsLock::
Sleep, 1 ; improve reliability of setting LED state sometimes
If (GetKeyState("Capslock", "T"))
KeyboardLED(4,"off")
Return
and
~CapsLock::
If (GetKeyState("Capslock", "T"))
KeyboardLED(4,"off")
Return
But capslock stil blinks when held down
-
Is it possible to disable an LED without making it 'blink'
Not without modifying the hardware to take a cancelable delay, methinks.
I tend to destroy, desolder, hide, or avoid powering LEDs I don't want to see lit. Ignoring them is a last resort since I ignore enough things in a day.
EDIT:
On second thought, a custom keyboard driver should be capable of decoupling a keyboard's LEDs from its traditional function, if that is indeed how keyboards work, if it's up to the OS to manage LED state, and the keyboard in question does not manage its own states.
-
There is a free utility called NetworkLights (http://www.google.com/search?q=networklights) that turns keyboard LED's into network activity lights.
-
DefineDosDevice() and DeviceIoControl() works traditional for PS/2 keyboards, this fails for USB keyboards..
-
If you wanna do it 1337ly, you could obtain a DIP switch bank (of 3 switches) and place them electrically in the way of the power for the LEDs. They couldn't light up unless a) the lock status was on and b) the switch was allowing the electricity to get to the LED.
Mounting would be interesting but that's why I say DIP switch...least intrusive type of switch.
-
I made the lights blink quickly when volume is maxed out, blink once slow when muted, and blinks slowly when volume is scrolled to zero.
My volume controls are Mouse Forward Side Button + Scroll Wheel, or Mouse Mouse Forward Side Button + CLICK scroll wheel (mouse 3)
I find this easier than media keys.
-
Oh, what? I thought this was about making them not operate :/
iFailed.
-
Oh, what? I thought this was about making them not operate :/
iFailed.
tl;dr failure?
-
tl;dr failure?
It didn't seem like one, but it must have been.
I thought I read it lol :/
-
no kishy, you're right.
I was just specifying my example.
Since the LEDs are so useless, I try to use them for something.
But my goal is to turn of CAPSLOCK led when the CAPSLOCK key is pressed (even if it means sacrificing all LED usage)
-
In Linux without X this is easy.
setleds -D -caps
-
My goal is to turn of CAPSLOCK led when the CAPSLOCK key is pressed (even if it means sacrificing all LED usage)
(http://www.wiringproducts.com/contents/media/electrical_tape.jpg)
-
I used the electrical tape 'nuclear option' on my Sky box as the stupid LEDs would insist on spinning around (had to keep enabling demo mode so I could then disable it - go figure). Looks ugly to everyone else, looks much better than the annoying lights to me.
-
Great video! I would love to see this project progress.