This example reads the toggle states of the three most commonly used toggle keys: Capslock, Numlock and Insert. The example also changes these three toggle states: you will see the LED's on your keyboard change.
The GetKeyboardState procedure fetches the keystates of 256 keys at once. There's also a GetKeyState procedure to fetch the state for 1 particular key, but there is no SetKeyState procedure for one key so if you need to change a keystate you will have to use SetKeyboadState and set them all.
{windows.i} DEFINE VARIABLE CapsLockToggle AS LOGICAL. DEFINE VARIABLE NumLockToggle AS LOGICAL. DEFINE VARIABLE InsertToggle AS LOGICAL. RUN GetKeyToggleState (OUTPUT CapsLockToggle, OUTPUT NumLockToggle, OUTPUT InsertToggle). /* alter all three of them, for demonstration purposes */ RUN SetKeyToggleState (NOT CapsLockToggle, NOT NumLockToggle, NOT InsertToggle). &GLOB VK_CAPITAL 20 &GLOB VK_INSERT 45 &GLOB VK_NUMLOCK 144 &GLOBAL-DEFINE VK_SCROLL 145 /* (hex 91) SCROLL LOCK key */ &GLOBAL-DEFINE VK_SHIFT 16 /* (hex 10) either SHIFT key */ &GLOBAL-DEFINE VK_CONTROL 17 /* (hex 11) either Ctrl key */ /* The following only work under Windows NT/2000/XP: */ &GLOBAL-DEFINE VK_LSHIFT 160 /* (hex A0) Left SHIFT key */ &GLOBAL-DEFINE VK_RSHIFT 161 /* (hex A1) Right SHIFT key */ &GLOBAL-DEFINE VK_LCONTROL 162 /* (hex A2) Left CTRL key */ &GLOBAL-DEFINE VK_RCONTROL 163 /* (hex A3) Right CTRL key */ PROCEDURE GetKeyToggleState : DEFINE OUTPUT PARAMETER CapsLockToggle AS LOGICAL. DEFINE OUTPUT PARAMETER NumLockToggle AS LOGICAL. DEFINE OUTPUT PARAMETER InsertToggle AS LOGICAL. DEFINE VARIABLE KBState AS MEMPTR. DEFINE VARIABLE ReturnValue AS INTEGER NO-UNDO. SET-SIZE(KBState) = 256. /* Get the current state of the keyboard */ RUN GetKeyboardState(GET-POINTER-VALUE(KBState), OUTPUT ReturnValue). CapsLockToggle = GET-BYTE(KBState, 1 + {&VK_CAPITAL}) MOD 2 = 1. NumLockToggle = GET-BYTE(KBState, 1 + {&VK_NUMLOCK}) MOD 2 = 1. InsertToggle = GET-BYTE(KBState, 1 + {&VK_INSERT}) MOD 2 = 1. SET-SIZE(KBState) = 0. END PROCEDURE. /* ======================================================= */ PROCEDURE SetKeyToggleState : DEFINE INPUT PARAMETER CapsLockToggle AS LOGICAL. DEFINE INPUT PARAMETER NumLockToggle AS LOGICAL. DEFINE INPUT PARAMETER InsertLockToggle AS LOGICAL. DEFINE VARIABLE KBState AS MEMPTR. DEFINE VARIABLE ReturnValue AS INTEGER NO-UNDO. SET-SIZE(KBState) = 256. /* Get the current state of the keyboard */ RUN GetKeyboardState(GET-POINTER-VALUE(KBState), OUTPUT ReturnValue). PUT-BYTE(KBState,1 + {&VK_CAPITAL}) = IF CapsLockToggle THEN 1 ELSE 0. PUT-BYTE(KBState,1 + {&VK_NUMLOCK}) = IF NumLockToggle THEN 1 ELSE 0. PUT-BYTE(KBState,1 + {&VK_INSERT}) = IF InsertLockToggle THEN 1 ELSE 0. RUN SetKeyboardState(INPUT GET-POINTER-VALUE(KBState), OUTPUT ReturnValue). SET-SIZE(KBState) = 0. END PROCEDURE. PROCEDURE GetKeyboardState EXTERNAL {&USER}: DEFINE INPUT PARAMETER KBState AS LONG. /* memptr */ DEFINE RETURN PARAMETER RetVal AS LONG. /* bool */ END PROCEDURE. PROCEDURE SetKeyboardState EXTERNAL {&USER} : DEFINE INPUT PARAMETER KBState AS LONG. /* memptr */ DEFINE RETURN PARAMETER RetVal AS LONG. /* bool */ END PROCEDURE.
GetKeyboardState fetches an array of 256 keystates, each keystate is one byte in size.
VK_CAPITAL (better known as Capslock) has index 20 in that array. Arrays in C are zero based while Progress starts counting at one, so the put-byte and get-byte statements are called with (1 + {&VK_CAPITAL}).
A key is toggled ON when the low-order bit of its keystate is set to 1. The example tests this by checking if the value is odd or even.
A key is pressed when the high-order bit is set to 1. This may be important to know, but not in this example.
Although the code does change the toggle state for the Insert-button you will not notice it; for some reason the Progress fill-in and editor widgets will keep behaving as if Insert is toggled on.
The Statusbar ActiveX from Microsoft Common Controls V5 has panels that automatically represent the toggle states. However, these panels don't respond to the code.
Based on an idea by Paul Koufalis.