Recent

Author Topic: GetKeyState(VK_SHIFT)  (Read 27497 times)

Pascal

  • Hero Member
  • *****
  • Posts: 932
GetKeyState(VK_SHIFT)
« on: January 19, 2017, 11:19:23 am »
During startup of my program i would like to check the state of the SHIFT key (left or right)
to bypass some parts of the programm.

I used GetKeyState(VK_SHIFT) <> 0 to determine if one of the keys is pressed. But this
does not seem to be the right way to do it as this does not work reliable.

Any suggestions?
laz trunk x64 - fpc trunk i386 (cross x64) - Windows 10 Pro x64 (21H2)

balazsszekely

  • Guest
Re: GetKeyState(VK_SHIFT)
« Reply #1 on: January 19, 2017, 11:32:10 am »
Try this:
Code: Pascal  [Select][+][-]
  1. procedure TForm1.FormCreate(Sender: TObject);
  2. var
  3.   ShiftState: TShiftState;
  4. begin
  5.   ShiftState := GetKeyShiftState;
  6.   if ssShift in ShiftState then
  7.     //...
  8.   else
  9.     //...
  10. end;
« Last Edit: January 19, 2017, 11:34:13 am by GetMem »

Fungus

  • Sr. Member
  • ****
  • Posts: 354
Re: GetKeyState(VK_SHIFT)
« Reply #2 on: January 19, 2017, 01:25:19 pm »
I'm pretty sure you need to check the high order bit, like: (GetKeyState(VK_SHIFT) And $80) <> 0 in order to figure out if the key is pressed or not. The low order bit will toggle on/off for each keypress, so checking for non-zero is not reliable :)

Pascal

  • Hero Member
  • *****
  • Posts: 932
Re: GetKeyState(VK_SHIFT)
« Reply #3 on: January 19, 2017, 02:46:57 pm »
I'm pretty sure you need to check the high order bit, like: (GetKeyState(VK_SHIFT) And $80) <> 0 in order to figure out if the key is pressed or not. The low order bit will toggle on/off for each keypress, so checking for non-zero is not reliable :)
I found both implementations: with mask $8000 and without mask. Both do not work. Without mask i've never seen a value other than 0 or 1.
Toggling of bit 0 corresponds to my values seen during runtime.
laz trunk x64 - fpc trunk i386 (cross x64) - Windows 10 Pro x64 (21H2)

Pascal

  • Hero Member
  • *****
  • Posts: 932
Re: GetKeyState(VK_SHIFT)
« Reply #4 on: January 19, 2017, 02:55:21 pm »
Try this:
Code: Pascal  [Select][+][-]
  1. procedure TForm1.FormCreate(Sender: TObject);
  2. var
  3.   ShiftState: TShiftState;
  4. begin
  5.   ShiftState := GetKeyShiftState;
  6.   if ssShift in ShiftState then
  7.     //...
  8.   else
  9.     //...
  10. end;

Does not work, as GetKeyShiftState looks like this:
Code: Pascal  [Select][+][-]
  1. function GetKeyShiftState: TShiftState;
  2. begin
  3.   Result := [];
  4.   if (GetKeyState(VK_CONTROL) and $8000) <> 0 then
  5.     Include(Result, ssCtrl);
  6.   if (GetKeyState(VK_SHIFT) and $8000) <> 0 then
  7.     Include(Result, ssShift);
  8.   if (GetKeyState(VK_MENU) and $8000) <> 0 then
  9.     Include(Result, ssAlt);
  10.   if ((GetKeyState(VK_LWIN) and $8000) <> 0) or ((GetKeyState(VK_RWIN) and $8000) <> 0) then
  11.     Include(Result, ssMeta);
  12.   if (GetKeyState(VK_LWIN) < 0) or (GetKeyState(VK_RWIN) < 0) then
  13.     Include(Result, ssMeta);
  14. end;

GetKeyState(VK_SHIFT) never delivered any values other than 0 or 1 to me.
laz trunk x64 - fpc trunk i386 (cross x64) - Windows 10 Pro x64 (21H2)

Fungus

  • Sr. Member
  • ****
  • Posts: 354
Re: GetKeyState(VK_SHIFT)
« Reply #5 on: January 19, 2017, 03:02:52 pm »
Since GetKeyState return a one byte value (shortint) the bitmap $8000 must be invalid, so this must be a bug or a mistake.

Code: Pascal  [Select][+][-]
  1. Function IsKeyDown(Const VirtualKeyCode: Integer): Boolean; Inline;
  2. Begin
  3.   Result:= (GetKeyState(VirtualKeyCode) And $80) <> 0;
  4. End;

https://msdn.microsoft.com/en-us/library/windows/desktop/ms646301(v=vs.85).aspx

EDIT: Short or Shortint may be 2-bytes on 64bit systems, though. But on Linux Mint 64bit the method above works.
« Last Edit: January 19, 2017, 03:08:18 pm by Fungus »

rvk

  • Hero Member
  • *****
  • Posts: 6948
Re: GetKeyState(VK_SHIFT)
« Reply #6 on: January 19, 2017, 03:15:08 pm »
GetKeyState is defined as returning a Smallint in FPC/Windows.
Code: Pascal  [Select][+][-]
  1. function GetKeyState(nVirtKey: Integer): Smallint; {$IFDEF IF_BASE_MEMBER}virtual;{$ENDIF}
And a SmallInt is 2 bytes:
http://freepascal.org/docs-html/ref/refsu5.html

"SHORTs are 16-bit integers"
Look at the data-definitions of SHORT from Microsoft:
https://msdn.microsoft.com/en-us/library/windows/desktop/aa383751(v=vs.85).aspx

On my Windows machine the GetKeyShiftState with ShiftState works.
« Last Edit: January 19, 2017, 03:19:10 pm by rvk »

Pascal

  • Hero Member
  • *****
  • Posts: 932
Re: GetKeyState(VK_SHIFT)
« Reply #7 on: January 19, 2017, 03:21:51 pm »
Since GetKeyState return a one byte value (shortint) the bitmap $8000 must be invalid, so this must be a bug or a mistake.

Code: Pascal  [Select][+][-]
  1. Function IsKeyDown(Const VirtualKeyCode: Integer): Boolean; Inline;
  2. Begin
  3.   Result:= (GetKeyState(VirtualKeyCode) And $80) <> 0;
  4. End;

https://msdn.microsoft.com/en-us/library/windows/desktop/ms646301(v=vs.85).aspx

EDIT: Short or Shortint may be 2-bytes on 64bit systems, though. But on Linux Mint 64bit the method above works.
Here on Windows x64 SHORT = smallint = 2 byte.
But anyway i just tested the output of GetKeyState(VK_SHIFT) without mask, but i do not get any other value than 0 and 1 without any correlation to the real state of the shift key.
laz trunk x64 - fpc trunk i386 (cross x64) - Windows 10 Pro x64 (21H2)

Handoko

  • Hero Member
  • *****
  • Posts: 5524
  • My goal: build my own game engine using Lazarus
Re: GetKeyState(VK_SHIFT)
« Reply #8 on: January 19, 2017, 03:32:03 pm »
I tested both GetMem and Fungus suggestions on Linux 64-bit.

Here is the result:
- Both won't work on TForm.OnCreate
- Both work correctly on TForm.OnActivate

Pascal

  • Hero Member
  • *****
  • Posts: 932
Re: GetKeyState(VK_SHIFT)
« Reply #9 on: January 19, 2017, 03:39:14 pm »
I tested both GetMem and Fungus suggestions on Linux 64-bit.

Here is the result:
- Both won't work on TForm.OnCreate
- Both work correctly on TForm.OnActivate

I have to use it in OnCreate / OnShow.
Both places do not work here.
laz trunk x64 - fpc trunk i386 (cross x64) - Windows 10 Pro x64 (21H2)

rvk

  • Hero Member
  • *****
  • Posts: 6948
Re: GetKeyState(VK_SHIFT)
« Reply #10 on: January 19, 2017, 03:50:06 pm »
Weird... You are on Windows? What Lazarus version do you have?
(O, I already see, latest trunk)

What do you get with the following?
A TTimer and TMemo on your form and this as TimerEvent:
Code: Pascal  [Select][+][-]
  1. procedure TForm1.Timer1Timer(Sender: TObject);
  2. begin
  3.   Memo1.Lines.Add(IntToStr(GetKeyState(VK_SHIFT) and $8000));
  4. end;
When holding shift I get 32768 and releasing it I get 0 in the Memo1.

Handoko

  • Hero Member
  • *****
  • Posts: 5524
  • My goal: build my own game engine using Lazarus
Re: GetKeyState(VK_SHIFT)
« Reply #11 on: January 19, 2017, 03:58:48 pm »
@Pascal
Try to start a new project, drop a TLabel and put this code below to see if it work or not.

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, StdCtrls, LCLIntf, LCLType;
  9.  
  10. type
  11.  
  12.   { TForm1 }
  13.  
  14.   TForm1 = class(TForm)
  15.     Label1: TLabel;
  16.     procedure FormActivate(Sender: TObject);
  17.   private
  18.     { private declarations }
  19.   public
  20.     { public declarations }
  21.   end;
  22.  
  23. var
  24.   Form1: TForm1;
  25.  
  26. implementation
  27.  
  28. {$R *.lfm}
  29.  
  30. { TForm1 }
  31.  
  32. procedure TForm1.FormActivate(Sender: TObject);
  33. begin
  34.   if (GetKeyState(VK_SHIFT) And $80) <> 0 then
  35.     Label1.Caption := 'It works!';
  36. end;
  37.  
  38. end.

Fungus

  • Sr. Member
  • ****
  • Posts: 354
Re: GetKeyState(VK_SHIFT)
« Reply #12 on: January 19, 2017, 04:25:26 pm »
Sorry, shortint / short is of course a signed word of 2 bytes :-[

On a form drop a TEdit and a TButton, create an OnClick handler for the button and try this code:

Code: Pascal  [Select][+][-]
  1. uses ..., LCLType, LCLIntf;
  2.  
  3. Function GetKeyMask(Key: Integer): String;
  4. Var K, I: Smallint; //Changed from Shortint to Smallint
  5. Begin
  6.   K:= LCLIntf.GetKeyState(Key);
  7.   Result:= '';
  8.   For I:= 15 DownTo 0 Do
  9.     If (K And (1 Shl I)) <> 0 Then Result:= Result + '1'
  10.     Else Result:= Result + '0';
  11. End;
  12.  
  13. procedure Button1Click(Sender: TObject);
  14. begin
  15.   Edit1.Text:= GetKeyMask(VK_SHIFT);
  16. end;

I'm not 100% sure, but GetKeyState may be declared both in Windows and LCLIntf and this may be what's causing variation. Stick with the one in LCLIntf :)

On Linux Mint 64bit the output is "1111111110000001" when shift is down, and "0000000000000001" when it is up. The low order bit toggles for each key press registred and this seems to be the case for all keys, not only the ones who actually can toggle (like CAPS, NUMLOCK etc.). If this is a bug or a workaround, I do not know.
« Last Edit: January 19, 2017, 05:33:39 pm by Fungus »

rvk

  • Hero Member
  • *****
  • Posts: 6948
Re: GetKeyState(VK_SHIFT)
« Reply #13 on: January 19, 2017, 04:36:14 pm »
Sorry, shortint / short is of course a signed word of 2 bytes :-[
Well, yes and no, and that's where the confusion is.
A Shortint in FPC is actually a signed byte.
But @Microsoft they are talking about SHORT and THAT is a 2 byte signed word.
In FPC that translates to a Smallint. Neat, huh :D

In Windows the GetKeyState does need to be masked with $8000 because they are talking about the high-bit of a SHORT.
But I noticed in GTK2 sources of FPC it is somehow masked(?) with 127 (so leaving the 128 ($80) for held down).
I think that's an incompatibility (or bug) between GTK and Windows implementation.

Fungus

  • Sr. Member
  • ****
  • Posts: 354
Re: GetKeyState(VK_SHIFT)
« Reply #14 on: January 19, 2017, 05:36:07 pm »
@rvk: You are right about the short small stuff. I've changed the shortint to smallint in my previous example, output remains the same though. Probably because memory aligment expands a single byte type to 4/8 bytes :)

 

TinyPortal © 2005-2018