geekhack

geekhack Projects => Making Stuff Together! => DIY Discussions ARCHIVE => Topic started by: sixty on Fri, 04 September 2009, 12:49:28

Title: Manipulating keyboard LED state
Post 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):

Quote
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.

Quote from: molto;113477
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:
Title: Manipulating keyboard LED state
Post by: sixty on Fri, 04 September 2009, 12:55:39
And to import my original question to Molto:

Quote from: molto;113477
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).
Title: Manipulating keyboard LED state
Post by: molto on Fri, 04 September 2009, 13:33:02
Can you please run WinObj (http://technet.microsoft.com/en-us/sysinternals/bb896657.aspx) and verify that there is only one \Device\KeyboardClass* object?
Title: Manipulating keyboard LED state
Post by: nanu on Wed, 11 November 2009, 19:15:01
I couldn't get any of this to work, but I found relevant Autohotkey functions, and thusly wrote a script:
Code: [Select]

#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(&quot;lstrlenW&quot;,&quot;str&quot;,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(&quot;ntdll\ZwCreateFile&quot;,&quot;str&quot;,fh,&quot;UInt&quot;,desiredaccess,&quot;str&quot;,objattrib,&quot;str&quot;,io,&quot;UInt&quot;,0,&quot;UInt&quot;,fattribs
                  ,&quot;UInt&quot;,sharemode,&quot;UInt&quot;,createdist,&quot;UInt&quot;,flags,&quot;UInt&quot;,0,&quot;UInt&quot;,0, &quot;UInt&quot;)
  return % ExtractInteger(fh)
}


SetUnicodeStr(ByRef out, str_)
{
  VarSetCapacity(st1, 8, 0)
  InsertInteger(0x530025, st1)
  VarSetCapacity(out, (StrLen(str_)+1)*2, 0)
  DllCall(&quot;wsprintfW&quot;, &quot;str&quot;, out, &quot;str&quot;, st1, &quot;str&quot;, str_, &quot;Cdecl UInt&quot;)
}


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(&quot;RtlFillMemory&quot;, &quot;UInt&quot;, &pDest + pOffset + A_Index-1, &quot;UInt&quot;, 1, &quot;UChar&quot;, pInteger >> 8*(A_Index-1) & 0xFF)
}
Title: Manipulating keyboard LED state
Post by: kohan69 on Sat, 08 May 2010, 03:13:48
Is it possible to disable an LED without making it 'blink' when the CAPSLOCK key is pressed?

I tried
Code: [Select]
~CapsLock::

  Sleep, 1 ; improve reliability of setting LED state sometimes
  If (GetKeyState(&quot;Capslock&quot;, &quot;T&quot;))
    KeyboardLED(4,&quot;off&quot;)
 
Return
and

Code: [Select]
~CapsLock::


  If (GetKeyState("Capslock", "T"))
    KeyboardLED(4,"off")
 
Return

But capslock stil blinks when held down
Title: Manipulating keyboard LED state
Post by: nanu on Sat, 08 May 2010, 07:02:29
Quote from: kohan69;180107
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.
Title: Manipulating keyboard LED state
Post by: didjamatic on Sat, 08 May 2010, 07:32:15
There is a free utility called NetworkLights (http://www.google.com/search?q=networklights) that turns keyboard LED's into network activity lights.

Title: Manipulating keyboard LED state
Post by: noctua on Sat, 08 May 2010, 12:01:44
DefineDosDevice() and DeviceIoControl() works traditional for PS/2 keyboards, this fails for USB keyboards..
Title: Manipulating keyboard LED state
Post by: kishy on Sat, 08 May 2010, 12:30:08
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.
Title: Manipulating keyboard LED state
Post by: kohan69 on Sat, 08 May 2010, 15:55:52
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.
Title: Manipulating keyboard LED state
Post by: kishy on Sat, 08 May 2010, 16:11:59
Oh, what? I thought this was about making them not operate :/

iFailed.
Title: Manipulating keyboard LED state
Post by: Nonmouse on Sat, 08 May 2010, 18:28:27
Quote from: kishy;180258
Oh, what? I thought this was about making them not operate :/

iFailed.

tl;dr failure?
Title: Manipulating keyboard LED state
Post by: kishy on Sat, 08 May 2010, 18:56:55
Quote from: Nonmouse;180275
tl;dr failure?


It didn't seem like one, but it must have been.

I thought I read it lol :/
Title: Manipulating keyboard LED state
Post by: kohan69 on Sat, 08 May 2010, 19:20:22
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)
Title: Manipulating keyboard LED state
Post by: HaaTa on Sat, 08 May 2010, 22:38:01
In Linux without X this is easy.

setleds -D -caps
Title: Manipulating keyboard LED state
Post by: didjamatic on Sat, 08 May 2010, 22:47:51
Quote from: kohan69;180282
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)
Title: Manipulating keyboard LED state
Post by: hoggy on Sun, 09 May 2010, 14:04:29
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.
Title: Manipulating keyboard LED state
Post by: nowsharing on Sun, 09 May 2010, 23:30:38
Great video! I would love to see this project progress.