Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I capture keyboard status when my application doesn't have the focus?

My girlfriend's new laptop doesn't have indicator LEDs for NumLock and CapsLock, so I wrote a small program which show their status on screen:

procedure TForm1.Timer1Timer(Sender: TObject);  
var  
  KeyState: TKeyboardState;  
begin  
  GetKeyboardState(KeyState);  
  if KeyState[VK_NUMLOCK] = 0 then  
    PanelNumLock.Color := clSilver  
  else  
    PanelNumLock.Color := clLime;  
  if KeyState[VK_CAPITAL] = 0 then  
    PanelCapsLock.Color := clSilver  
  else  
    PanelCapsLock.Color := clLime;  
end;

enter image description here

This works as long as my program has the focus, but when the focus goes to anther program statuses are no longer updated. (However, just moving the mouse over the form, no clicking, is sufficient for updating.)

How can I let the program update when another application has the focus?

like image 770
Joris Groosman Avatar asked Dec 22 '15 07:12

Joris Groosman


2 Answers

You can simply use GetKeyState in your Timer.

if GetKeyState(VK_NUMLOCK) = 1 then
  PanelNumLock.Color := clLime
else
  PanelNumLock.Color := clSilver;

if GetKeyState(VK_CAPITAL) = 1 then
  PanelCapsLock.Color := clLime
else
  PanelCapsLock.Color := clSilver;

This works even when your application doesn't have the focus. Tested on XP.

like image 125
kobik Avatar answered Nov 05 '22 13:11

kobik


You should use a Low Level Keyboard Hook, because then you can get notified for every keystroke even if you application does not have focus.

I've create a small example for you

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Vcl.ExtCtrls;

const
  WM_UpdateScreen = WM_USER + 1;

type
  TForm1 = class(TForm)
    PanelCapsLock: TPanel;
    PanelNumlock: TPanel;
    procedure FormCreate(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    FHook: hHook;
    KeyState: TKeyboardState;
  public
    procedure UpdateScreen(var message: TMessage); message WM_UpdateScreen;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

type
  pKBDLLHOOKSTRUCT = ^KBDLLHOOKSTRUCT;

  KBDLLHOOKSTRUCT = packed record
    vkCode: DWORD;
    scanCodem: DWORD;
    flags: DWORD;
    time: DWORD;
    dwExtraInfo: ULONG_PTR;
  end;

var
  hkHook: hHook;

function LowLevelKeyboardProc(code: Integer; WParam: WParam; LParam: LParam): LRESULT stdcall;
const
  LLKHF_UP = $0080;
var
  Hook: pKBDLLHOOKSTRUCT;
  bControlKeyDown: Boolean;
begin
  try
    Hook := pKBDLLHOOKSTRUCT(LParam);
    case code of
      HC_ACTION:
        begin
          if (Hook^.flags and LLKHF_UP) <> 0 then
            if Hook.vkCode in [VK_NUMLOCK, VK_CAPITAL] then
              PostMessage(Form1.Handle, WM_UpdateScreen, Hook.vkCode, 0);
        end;
    end;
  finally
    Result := CallNextHookEx(hkHook, code, WParam, LParam);
  end;
end;

procedure HookIt;
begin
  hkHook := SetWindowsHookEx(WH_KEYBOARD_LL, @LowLevelKeyboardProc, hInstance, 0);
end;

procedure UnHookIt;
begin
  UnHookWindowsHookEx(hkHook);
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  UnHookIt;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  GetKeyboardState(KeyState);
  PostMessage(Handle, WM_UpdateScreen, VK_CAPITAL, 1);
  PostMessage(Handle, WM_UpdateScreen, VK_NUMLOCK, 1);
  HookIt;
end;

procedure TForm1.UpdateScreen(var message: TMessage);
begin
  if message.LParam = 0 then
    if KeyState[message.WParam] = 0 then
      KeyState[message.WParam] := 1
    else
      KeyState[message.WParam] := 0;

  if KeyState[VK_NUMLOCK] = 0 then
    PanelNumlock.Color := clSilver
  else
    PanelNumlock.Color := clLime;
  if KeyState[VK_CAPITAL] = 0 then
    PanelCapsLock.Color := clSilver
  else
    PanelCapsLock.Color := clLime;

end;

end.

Basically on formCreate I hook the Keyboard, and tells my program in which function I need my notification. In my example I called it LowLevelKeyboardProc

Then you just need to test fot which key is pressed and if it is one of CapsLock of Num lock then nofify the form.

like image 5
Jens Borrisholt Avatar answered Nov 05 '22 13:11

Jens Borrisholt