Can I capture Capslock / Shift key presses? - delphi

I just discovered that the caps lock and shift key (and probably some more of the keys) affect all of the keyboards connected to the computer. (one of the hazards of testing a program that I coded, I only have two hands so it took me ages to realize a problem like this)
So separating the keystrokes is fine, but the shift/caps locks drives everything crazy (When one user shifts to capitalize, all users' input at that moment are capitalized as well)
Can I capture Capslock and Shift keypress in FormKeyPress?
Anywhere else for that matter?
Can I save a Shift keypress? (so I can properly apply the 'shift' to the respective user input)
Any other suggestion to solve this problem is welcome as well.

to check if shift ley is down try this:
if GetKeyState(VK_SHIFT)<0 //tests if shiftkey is down
then ShowMessage('shift key is down'');
and to detect if caps lock is on try this:
if Odd(GetKeyState(VK_CAPITAL)) //tests if caps lock is on
then showmessage('caps lock is on');
and if you want to check status of both shift and capslock :
if Odd(GetKeyState(VK_CAPITAL)) then
if GetKeyState(VK_SHIFT)<0 then
showmessage('capslock is on and shift key is down too')
else
showmessage('capslock is on but shift key is NOT down');
getkeystate is a windows api function you can read more about it here

You can capture them in the OnKeyDown event
procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
if Key = VK_SHIFT then
ShowMessage('Shift Pressed');
if Key = VK_CAPITAL then
ShowMessage('Caps Lock Pressed');
end;

if Key = VK_SHIFT then
ShowMessage('Shift Pressed');
there should have been:
if shift = [ssShift] then ...
The program tests Shift: TShiftState for SHIFT key hold down,
not key :word; it is checked for other ordinal characters
It doesn't work for CAPSLOCK - I couldn't find the code.
There are only: ssShift, ssAlt, ssCtrl, ssLeft, ssRight, ssMiddle, ssDouble, so it seems have to use in this case:
if Odd(GetKeyState(VK_CAPITAL)) then ...

Related

USB barcode scanner inputs are ignored in FMX projects

i am trying to upgrade a program from Delphi7 to DelphiXE8.
In the programm there are some TEdit fields. You could input data in that fields via keyboard or a usb scanner. The usb scanner emulates a keyboard and works fine in all other programs. (Same program in Delphi7, Firefox, Editor, etc.......)
If I use the scanner in Delphi XE8 the TEdit field doesn't get correct data. If I trigger a KeyDown event I see that there are many Key 16/17/18 coming in but KeyChar is always #0.
Same Problem with TMemo.
I just tried something different:
In a VCL project the scanner works fine.
In a FMX project the scanner fails.
The scanner is a Birch BF-481BU/N.
Any ideas what could solve that problem?
My scanner has a Caps Lock setting.
Auto
Alt+Keypad
Caps Lock Off
Caps Lock On
With "Auto","Off","On" the scanner works fine with FMX.
With "Alt+Keypad" the scan fails in FMX.
I tried with a normal USB Scanner (Keyboard wedge) with this code that works fine.
procedure TForm2.Edit1KeyDown(Sender: TObject; var Key: Word; var KeyChar: Char;
Shift: TShiftState);
begin
if KeyChar = #13 then ShowMessage('Your code is ' + Edit1.Text);
end;
Alt + Keypad is used to enter particular chars typing the ascii code.
As an example if you press ALT + 126 the result will be "~"
So probably you have to remove the Alt+Keypad settings on your scanner.
I had this problem with a game I'm building. It seems that the keycode is getting "handled" and set to null before it ever even gets to the form. It looks like they are only passing things through when the ALT or CTRL keys are pressed.
To solve this particular problem, I commented out a line in FMX.Platform.Win
procedure CurrentChar(Msg: tagMsg; var Key: Word; var Ch: WideChar; var Shift: TShiftState);
begin
Key := wParam;
Ch := WideChar(Msg.wParam);
Shift := KeyDataToShiftState(lParam);
if (Ch >= ' ') then
begin
if ((Shift * [ssAlt, ssCtrl]) = [ssAlt, ssCtrl]) then
begin
// AltGr + Char (in German keyboard)
Shift := Shift - [ssAlt, ssCtrl];
end;
//WHYYYY?!?!?!?!?!?!?
//if (([ssAlt, ssCtrl, ssCommand] * Shift) = []) then
// Key := 0;
end;
if ((([ssAlt, ssCtrl, ssCommand] * Shift) <> []) or (Ch < ' ')) and (Key > 0) then
Ch := #0;
end;
What I did FIRST though is I copied FMX source folder into your own private source tree and built it from there. Then your apps will build with any minor patches you put into it, but it is simpler than installing hacked design-time packages.
Once you have your own private FMX source, then you can start hacking it (which you will have to do from time to time).
The first thing you should do when starting a new project, is copy the FMX source folder into your own private source tree and build from there.
FMX still has a way to go before it is a comprehensive cross platform solution, but it is getting closer, so you'll have to mess with it occasionally. Using similar approaches, I added Android pen support, fixed some BLE issues... you'll probably come across your own things.

detect enter on a memo in Delphi

hi i am doing a command shell using a memo in Delphi , the problem is to detect the last line written and read the command I need to know how to detect the enter key on a memo.
as I can detect the enter key on a memo ?
In the OnKeypress event you can check for certain keys and handle them as you wish yourself. The enter key is one of these keys.
procedure TForm1.Memo1KeyPress(Sender: TObject; var Key: Char);
const
ENTER = #13;
begin
case Key of
ENTER : begin
// Do something
end;
end;
end;
By default a TMemo has the WantReturns property set to TRUE. This means that as well as any response to the key press that you might implement in your code, the TMemo will still also receive the key event and add a new line to the content of the memo.
If you do not want this then you can either:
Set WantReturns = FALSE. you will still get the KeyPress
event for the enter key, but the memo will not add new lines in
response (they can still be added if the user presses Ctrl +
Enter)
OR
Keep WantReturns = TRUE but set Key to the value #0 for any key
events which you want to suppress (i.e. prevent from reaching the
memo control).
An example of this latter approach might look something like this:
const
NO_KEY = #0;
ENTER = #13;
begin
case Key of
ENTER : begin
// Do something
if NOT AddNewLine then
Key := NO_KEY;
end;
end;
end;
NOTE: The OnKeyPress event only allows you to respond to a subset of key events, specifically those that correspond to CHAR type values (although this does include some non-printing characters such as Tab and Backspace, for example).
If you want or need to detect the state of a wider range of non-character keys or to reliably handle key combinations such as Ctrl+Key or Shift+Key then you will need to query the state of those modifier keys. However, by the time you are responding to the key event, the state of the modifier keys may have changed and a better approach in that case is to use an alternate event which provides a greater range of key events, including the state of the Shift keys (and Control keys) at the time of the key event itself, such as OnKeyDown.
You can use OnKeyDown event, for example:
procedure TForm.Memo1KeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
if Key = VK_Return then
begin
// Your code here ...
// set Key to 0 if you do not want the key
// to be default-processed by the control...
Key := 0 ;
end;
end;
Detecting the enter key in a TMemo control is easy. Just add an OnKeyPress event:
procedure TForm1.Memo1KeyPress(Sender: TObject; var Key: Char);
begin
if Key = #13 then
begin
// Do something
end;
end;

Delphi monitoring status of CapsLock key

My program runs in the background, and uses a timer to regularly check if Capslock is ON or OFF.
My question is if there exists a better solution than using a timer?
procedure TForm1.Timer2Timer(Sender: TObject);
var KeyState: TKeyboardState;
begin
GetKeyboardState(KeyState) ;
if (KeyState[VK_CAPITAL] = 0) then
CheckBox1.Checked:=False //Capslock is OFF
else
CheckBox1.Checked:=True; //Capslock is ON
end;
Do this with a low level keyboard hook, WH_KEYBOARD_LL. Install the hook with SetWindowHookEx. You'll get notified of every keyboard event in the hook proc. Detect the original state by calling GetKeyboardState.
Note that you must read the documentation more carefully. For GetKeyboardState it says:
If the key is a toggle key, for example CAPS LOCK, then the low-order bit is 1 when the key is toggled and is 0 if the key is untoggled.
Therefore it is erroneous to test the entire byte against zero. Test just the low-order bit. Use and $1 to pick out that bit.
KeyPreview:= True
FormKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
if GetKeyState(VK_CAPITAL) > 0 then
hdrSts.Sections[0]:= 'CAPS LOCK'
else
hdrSts.Sections[0]:= '';
end;

How to get rid of windows sounds when capturing CTRL + S?

In my application, when I press CTRL + S, my form (with Key Preview enabled) captures this and saves the document. But when the focus is in, for example, an edit control, I get an annoying "Ding" sound, or in general, windows sounds. How do I avoid this sound?
Here's my form's capture of this key event...
procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
var
C: String;
begin
if not fChanging then
Modified;
if ssCtrl in Shift then begin
C:= LowerCase(Char(Key));
if C = 's' then begin
DoSave;
Key:= 0; //Tried this but didn't work
end else
if C = 'c' then begin
//Copy selected item(s)
end;
end;
end;
PS - Is there a more standard way of capturing these events? Because I'm sure I'm doing something wrong, and I'm sure there's another way I should be getting these key events without sounds.
A couple of things:
Try putting your code into FormKeyPress instead of FormKeyDown. This will make the Key := 0; code work... You will need to handle the CTRL checking manually though, by using something like GetKeyState() (I originally had GetAsyncKeyState() here, but as Rob Kennedy points out, GetKeyState() is a much better option).
Use an Action instead. Plop a TActionList on your form, double click on it, add an action and set it's hot key to CTRL-S. Add your save code to it's OnExecute event handler. This is the "proper" way to do it I believe.
Hope this helps.
Why don't you use Actions? That is the best way to handle shortcuts.
Here is some Delphi code to disable the system beep.

Pause/Break as a Keyboard ShortCut (Win32, *Possibly* Delphi-specific)

Is it not possible to use the Pause/Break key in keyboard shortcuts?
I know I can respond to the Pause/Break key, e.g.
procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
if Key = VK_PAUSE then
ShowMessage('VK_PAUSE pressed');
end;
However, I am unable to respond to the Pause/Break key using the TShortCut properties, e.g. in menu items and action lists. The Object Inspector lets me enter Pause, Shift + Pause, Ctrl + Pause, Alt + Pause, Ctrl + Shift + Pause etc. so it clearly recognizes the Pause key. But when I run the application, the menu item/action is not triggered on the specified shortcut. Is there a known workaround?
With D2007, a quick workaround seems to be assigning it in the run-time;
Action1.ShortCut := VK_PAUSE;
For some reason if assigned in design-time, VK_PAUSE (19) seems to mutate to VK_NUMLOCK (144).
When a shortcut is assigned in design time the IDE has to convert a string to a shortcut. The problem is TextToShortCut('Pause'); returns 144 instead of 19. Though I'm not sure I believe the error is with Delphi; With 'Pause', retrieving a shortcut finds its way to menus.GetSpecialName, I think it should not.
On another note, while the workaround mentioned above works with 'Alt' and 'Shift' modifiers, it doesn't work with the 'Ctrl' modifier. The reason is, the OS assings 'Ctrl+Break' processing a special code: VK_CANCEL. To use 'Ctrl+Pause' as a shortcut one have to code;
Action1.ShortCut := menus.Shortcut(VK_CANCEL, [ssCtrl]);

Resources