Author Topic: Manipulating keyboard LED state  (Read 15940 times)

0 Members and 1 Guest are viewing this topic.

Offline sixty

  • Thread Starter
  • Posts: 984
    • http://deskthority.net
Manipulating keyboard LED state
« 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. 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 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:

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 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 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 instances by using the SetupAPI and RegisterDeviceNotification().
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)
    [/LIST]

    Offline sixty

    • Thread Starter
    • Posts: 984
      • http://deskthority.net
    Manipulating keyboard LED state
    « Reply #1 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).

    Offline molto

    • Posts: 52
      • http://www.friv.gs
    Manipulating keyboard LED state
    « Reply #2 on: Fri, 04 September 2009, 13:33:02 »
    Can you please run WinObj and verify that there is only one \Device\KeyboardClass* object?
    y8 | Miniclip | y3 | friv | ben10 | y8 | kizi | y8 | y3  | y8 | pogo

    Offline nanu

    • Posts: 290
      • http://T-T.be/portal
    Manipulating keyboard LED state
    « Reply #3 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)
    }

    Offline kohan69

    • Posts: 23
    Manipulating keyboard LED state
    « Reply #4 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
    « Last Edit: Sat, 08 May 2010, 03:16:09 by kohan69 »

    Offline nanu

    • Posts: 290
      • http://T-T.be/portal
    Manipulating keyboard LED state
    « Reply #5 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.
    « Last Edit: Sat, 08 May 2010, 07:19:57 by nanu »

    Offline didjamatic

    • Posts: 1352
    Manipulating keyboard LED state
    « Reply #6 on: Sat, 08 May 2010, 07:32:15 »
    There is a free utility called NetworkLights that turns keyboard LED's into network activity lights.

    IBM F :: IBM M :: Northgate :: Cherry G80 :: Realforce :: DAS 4

    Offline noctua

    • Posts: 188
    Manipulating keyboard LED state
    « Reply #7 on: Sat, 08 May 2010, 12:01:44 »
    DefineDosDevice() and DeviceIoControl() works traditional for PS/2 keyboards, this fails for USB keyboards..
    Selfmade Keyboard I (done)
    DT225 CH Trackball

    Selfmade Keyboard II (95% completed)
    L-Trac CST2545W-RC Trackball

    both use Cherry MX Blue switches, an Teensy++ controller and have an Colemak layout

    Offline kishy

    • Posts: 1576
    • Location: Windsor, ON Canada
    • Eye Bee M
      • http://kishy.ca/
    Manipulating keyboard LED state
    « Reply #8 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.
    Enthusiast of springs which buckle noisily: my keyboards
    Want to learn about the Kishsaver?
    kishy.ca

    Offline kohan69

    • Posts: 23
    Manipulating keyboard LED state
    « Reply #9 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.

    Offline kishy

    • Posts: 1576
    • Location: Windsor, ON Canada
    • Eye Bee M
      • http://kishy.ca/
    Manipulating keyboard LED state
    « Reply #10 on: Sat, 08 May 2010, 16:11:59 »
    Oh, what? I thought this was about making them not operate :/

    iFailed.
    Enthusiast of springs which buckle noisily: my keyboards
    Want to learn about the Kishsaver?
    kishy.ca

    Offline Nonmouse

    • Posts: 298
    Manipulating keyboard LED state
    « Reply #11 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?

    Offline kishy

    • Posts: 1576
    • Location: Windsor, ON Canada
    • Eye Bee M
      • http://kishy.ca/
    Manipulating keyboard LED state
    « Reply #12 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 :/
    Enthusiast of springs which buckle noisily: my keyboards
    Want to learn about the Kishsaver?
    kishy.ca

    Offline kohan69

    • Posts: 23
    Manipulating keyboard LED state
    « Reply #13 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)

    Offline HaaTa

    • Master Kiibohd Hunter
    • Posts: 794
    • Location: San Jose, CA, USA
    • Kiibohds!
      • http://kiibohd.com
    Manipulating keyboard LED state
    « Reply #14 on: Sat, 08 May 2010, 22:38:01 »
    In Linux without X this is easy.

    setleds -D -caps
    Kiibohd

    ALWAYS looking for cool and interesting switches
    I take requests for making keyboard converters (i.e. *old keyboard* to USB).

    Offline didjamatic

    • Posts: 1352
    Manipulating keyboard LED state
    « Reply #15 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)


    IBM F :: IBM M :: Northgate :: Cherry G80 :: Realforce :: DAS 4

    Offline hoggy

    • * Ergonomics Moderator
    • Posts: 1502
    • Location: Isle of Man
    Manipulating keyboard LED state
    « Reply #16 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.
    GH Ergonomic Guide (in progress)
    http://geekhack.org/index.php?topic=54680.0

    Offline nowsharing

    • Posts: 247
    • Swoop
    Manipulating keyboard LED state
    « Reply #17 on: Sun, 09 May 2010, 23:30:38 »
    Great video! I would love to see this project progress.