* * *

Author Topic: How to return special keys (e.g., Alt+H)  (Read 2743 times)

dculp

  • Jr. Member
  • **
  • Posts: 79
How to return special keys (e.g., Alt+H)
« on: April 20, 2017, 11:34:59 pm »
I need something like the following to detect special keys. The pseudo-code is for pressing the Alt and H keys together but should also apply to other key combinations (Crtl+1, etc.)

This is a console program running only under Windows.

Lazarus 1.6, FPC 3.0.0

Thanks,
Don C.

Code: Pascal  [Select]
  1. const
  2.    Alt_H = some integer; // probably
  3. var
  4.    Key: integer;
  5.  
  6. begin
  7. Key:= detect Alt_H key; <== need this
  8. case Key of
  9.    do something
  10.    end;
  11. end.
  12.  

Handoko

  • Hero Member
  • *****
  • Posts: 1514
  • My goal: build my own game engine using Lazarus
Re: How to return special keys (e.g., Alt+H)
« Reply #1 on: April 21, 2017, 03:23:38 am »
For console programming, maybe you can try GetKeyEventShiftState:
http://www.freepascal.org/docs-html/current/rtl/keyboard/getkeyeventshiftstate.html

I do not write console program. If you want GUI version, it should be easy:

Code: Pascal  [Select]
  1. procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState
  2.   );
  3. begin
  4.   if (Shift = [ssAlt]) and (Key = VK_H) then ShowMessage('You''ve done it!');
  5. end;
  6.  
  7. procedure TForm1.FormCreate(Sender: TObject);
  8. begin
  9.   Label1.Caption := 'Please press Alt + H.';
  10. end;

dculp

  • Jr. Member
  • **
  • Posts: 79
Re: How to return special keys (e.g., Alt+H)
« Reply #2 on: April 21, 2017, 02:36:14 pm »
For console programming, maybe you can try GetKeyEventShiftState:
http://www.freepascal.org/docs-html/current/rtl/keyboard/getkeyeventshiftstate.html

The above code is similar to the following (http://www.freepascal.org/docs-html/rtl/keyboard/getkeyevent.html). The returned string from KeyEventToString(K) works for many keys. For example, pressing the F10 key returns 'F10', Ctrl+F10 returns 'CTRL F10', Alt+F10 returns 'ALT F10'. However, pressing Alt+H returns 'ALT Key with scancode 8960' which isn't particularly usable.

Perhaps using KeyEventToString(K) is not the best solution. Other suggestions?

Code: Pascal  [Select]
  1. program example1;
  2.  
  3. { This program demonstrates the GetKeyEvent function }
  4.  
  5. uses keyboard;
  6.  
  7. Var
  8.   K : TKeyEvent;
  9.  
  10. begin
  11.   InitKeyBoard;
  12.   Writeln('Press keys, press "q" to end.');
  13.   Repeat
  14.     K:=GetKeyEvent;
  15.     K:=TranslateKeyEvent(K);
  16.     Write('Got key event with ');
  17.     Case GetKeyEventFlags(K) of
  18.       kbASCII    : Writeln('ASCII key');
  19.       kbUniCode  : Writeln('Unicode key');
  20.       kbFnKey    : Writeln('Function key');
  21.       kbPhys     : Writeln('Physical key');
  22.       kbReleased : Writeln('Released key event');
  23.     end;
  24.     Writeln('Got key : ',KeyEventToString(K));
  25.   Until (GetKeyEventChar(K)='q');
  26.   DoneKeyBoard;
  27. end.

Bart

  • Hero Member
  • *****
  • Posts: 2718
    • Bart en Mariska's Webstek
Re: How to return special keys (e.g., Alt+H)
« Reply #3 on: April 21, 2017, 06:24:13 pm »
From the old TP6 days:

Code: Pascal  [Select]
  1. Unit wordkeys;
  2.  
  3. Interface
  4.  
  5. {Keyboard extended key codes}
  6. const
  7.   kbEsc       = $011B;  kbAltSpace  = $0200;  kbCtrlIns   = $0400;
  8.   kbShiftIns  = $0500;  kbCtrlDel   = $0600;  kbShiftDel  = $0700;
  9.   kbBack      = $0E08;  kbCtrlBack  = $0E7F;  kbShiftTab  = $0F00;
  10.   kbTab       = $0F09;  kbAltQ      = $1000;  kbAltW      = $1100;
  11.   kbAltE      = $1200;  kbAltR      = $1300;  kbAltT      = $1400;
  12.   kbAltY      = $1500;  kbAltU      = $1600;  kbAltI      = $1700;
  13.   kbAltO      = $1800;  kbAltP      = $1900;  kbCtrlEnter = $1C0A;
  14.   kbEnter     = $1C0D;  kbAltA      = $1E00;  kbAltS      = $1F00;
  15.   kbAltD      = $2000;  kbAltF      = $2100;  kbAltG      = $2200;
  16.   kbAltH      = $2300;  kbAltJ      = $2400;  kbAltK      = $2500;
  17.   kbAltL      = $2600;  kbAltZ      = $2C00;  kbAltX      = $2D00;
  18.   kbAltC      = $2E00;  kbAltV      = $2F00;  kbAltB      = $3000;
  19.   kbAltN      = $3100;  kbAltM      = $3200;  kbF1        = $3B00;
  20.   kbF2        = $3C00;  kbF3        = $3D00;  kbF4        = $3E00;
  21.   kbF5        = $3F00;  kbF6        = $4000;  kbF7        = $4100;
  22.   kbF8        = $4200;  kbF9        = $4300;  kbF10       = $4400;
  23.   kbHome      = $4700;  kbUp        = $4800;  kbPgUp      = $4900;
  24.   kbGrayMinus = $4A2D;  kbLeft      = $4B00;  kbRight     = $4D00;
  25.   kbGrayPlus  = $4E2B;  kbEnd       = $4F00;  kbDown      = $5000;
  26.   kbPgDn      = $5100;  kbIns       = $5200;  kbDel       = $5300;
  27.   kbShiftF1   = $5400;  kbShiftF2   = $5500;  kbShiftF3   = $5600;
  28.   kbShiftF4   = $5700;  kbShiftF5   = $5800;  kbShiftF6   = $5900;
  29.   kbShiftF7   = $5A00;  kbShiftF8   = $5B00;  kbShiftF9   = $5C00;
  30.   kbShiftF10  = $5D00;  kbCtrlF1    = $5E00;  kbCtrlF2    = $5F00;
  31.   kbCtrlF3    = $6000;  kbCtrlF4    = $6100;  kbCtrlF5    = $6200;
  32.   kbCtrlF6    = $6300;  kbCtrlF7    = $6400;  kbCtrlF8    = $6500;
  33.   kbCtrlF9    = $6600;  kbCtrlF10   = $6700;  kbAltF1     = $6800;
  34.   kbAltF2     = $6900;  kbAltF3     = $6A00;  kbAltF4     = $6B00;
  35.   kbAltF5     = $6C00;  kbAltF6     = $6D00;  kbAltF7     = $6E00;
  36.   kbAltF8     = $6F00;  kbAltF9     = $7000;  kbAltF10    = $7100;
  37.   kbCtrlPrtSc = $7200;  kbCtrlLeft  = $7300;  kbCtrlRight = $7400;
  38.   kbCtrlEnd   = $7500;  kbCtrlPgDn  = $7600;  kbCtrlHome  = $7700;
  39.   kbAlt1      = $7800;  kbAlt2      = $7900;  kbAlt3      = $7A00;
  40.   kbAlt4      = $7B00;  kbAlt5      = $7C00;  kbAlt6      = $7D00;
  41.   kbAlt7      = $7E00;  kbAlt8      = $7F00;  kbAlt9      = $8000;
  42.   kbAlt0      = $8100;  kbAltMinus  = $8200;  kbAltEqual  = $8300;
  43.   kbCtrlPgUp  = $8400;  kbNoKey     = $0000;
  44.  
  45. { Keyboard state and shift masks }
  46.  
  47.   kbRightShift  = $0001;
  48.   kbLeftShift   = $0002;
  49.   kbCtrlShift   = $0004;
  50.   kbAltShift    = $0008;
  51.   kbScrollState = $0010;
  52.   kbNumState    = $0020;
  53.   kbCapsState   = $0040;
  54.   kbInsState    = $0080;
  55.  
  56. function GetKey: Word;
  57.  
  58. Implementation
  59.  
  60. function GetKey: Word; Assembler;
  61. Asm
  62.   mov ax, 00h
  63.   int 16h
  64. end;
  65.  
  66.  
  67. end.
  68.  

The assembler code can easily be replaced by some pascal code.
I also have some code that returns strings instead of words.

I don't think this is cross platform though.

Bart

dculp

  • Jr. Member
  • **
  • Posts: 79
Re: How to return special keys (e.g., Alt+H)
« Reply #4 on: April 21, 2017, 08:54:21 pm »
Bart --

These codes might be useful. However, what accompanying Pascal code do I need to translate a physical keypress into one of these codes (e.g., press the Esc key to return $011B). Also, I would need F11 and F12 together with associated Shift, Ctrl, & Alt.

I have been using something like the following but the return Keycode for special keys is nonstandard. (Note the required Offset. If Offset = 0 (ignored) then some Keycode overlap -- for instance, the Keycode for "0" is 48 and the Keycode for Alt+B would also be 48.) I think there should be a generic way to do this in console mode so that some kind of standard codes would be returned which could then be queried with a case statement.

Code: Pascal  [Select]
  1. program Keyboard_scan_codes;
  2. // see http://www.freepascal.org/docs-html/rtl/crt/readkey.html
  3.  
  4. uses
  5.    crt, strutils;
  6.  
  7. var
  8.   K: integer;
  9.  
  10.    function Keycode: integer;
  11.  
  12.    const
  13.       Offset = 256; {arbitrary}
  14.  
  15.    var
  16.       ch: char;
  17.       Special_char: boolean;
  18.  
  19.    begin
  20.    Special_char:= false;
  21.    ch:=ReadKey;
  22.    writeln('ch = "', ch, '"');
  23. //   writeln('ord(Key) = ', ord(ch));
  24.    case ch of
  25.        #0: begin
  26.            ch:=ReadKey; {special key}
  27.            Special_char:= true;
  28.            end;
  29.        end;
  30.  
  31.    if Special_char then Keycode:= ord(ch) + Offset
  32.    else Keycode:= ord(ch);
  33.    end;
  34.  
  35. begin
  36.   writeln('Press a key. q=Quit');
  37.   repeat
  38.     K:= Keycode;
  39.     writeln('K = ', K);
  40.     writeln;
  41.   until K = 113 {'q'};
  42. end.
  43.  

Bart

  • Hero Member
  • *****
  • Posts: 2718
    • Bart en Mariska's Webstek
Re: How to return special keys (e.g., Alt+H)
« Reply #5 on: April 21, 2017, 10:52:30 pm »
I don't think you can detect combo's like Ctrl+Shift, Ctrl+Alt using crt.readkey.
You will need to use some functions of the keyboard unit.

FWIW: my implementation of GetKey function:

Code: Pascal  [Select]
  1. function GetKey: Word;
  2. begin
  3.   Result := Ord(ReadKey);
  4.   if (Result = 0) then
  5.     Result := (Ord(ReadKey) shl 8) or Result;
  6. end;
  7.  

It will distinguish between H, Alt+H, Shift+A and Ctrl+A.

Bart

molly

  • Hero Member
  • *****
  • Posts: 1787
Re: How to return special keys (e.g., Alt+H)
« Reply #6 on: April 21, 2017, 11:09:10 pm »
@Bart/TS:
should be possible using default kb function. One exception though: you seem to need to implement your own conversion for physical keys.

For all (special modifier keys) combinations the keyboard unit seems to produce the same scan-code (keycode), e.g. for h that is #$23
Code: [Select]
((KeyCode and $0000FF00) shr 8).

The shift-state seems to differs for all possible modifier combinations.

(incomplete) table + char retrieve function for alt keys
Code: [Select]
Var
  Physicals : array[0..255] of char =
  (
    #$00, #$01, #$02, #$03, #$04, #$05, #$06, #$07, #$08, #$09, #$0A, #$0B, #$0C, #$0D, #$0E, #$0F,
     'q',  'w',  'e',  'r',  't',  'y',  'u',  'i',  'o',  'p', #$1A, #$1B, #$1C, #$1D,  'a',  's',
     'd',  'f',  'g',  'h',  'j',  'k',  'l', #$27, #$28, #$29, #$2A, #$2B, #$2C, #$2D, #$2E, #$2F,
    #$30, #$31, #$32, #$33, #$34, #$35, #$36, #$37, #$38, #$39, #$3A, #$3B, #$3C, #$3D, #$3E, #$3F,
    #$40, #$41, #$42, #$43, #$44, #$45, #$46, #$47, #$48, #$49, #$4A, #$4B, #$4C, #$4D, #$4E, #$4F,
    #$50, #$51, #$52, #$53, #$54, #$55, #$56, #$57, #$58, #$59, #$5A, #$5B, #$5C, #$5D, #$5E, #$5F,
    #$60, #$61, #$62, #$63, #$64, #$65, #$66, #$67, #$68, #$69, #$6A, #$6B, #$6C, #$6D, #$6E, #$6F,
    #$70, #$71, #$72, #$73, #$74, #$75, #$76, #$77, #$78, #$79, #$7A, #$7B, #$7C, #$7D, #$7E, #$7F,
    #$80, #$81, #$82, #$83, #$84, #$85, #$86, #$87, #$88, #$89, #$8A, #$8B, #$8C, #$8D, #$8E, #$8F,
    #$90, #$91, #$92, #$93, #$94, #$95, #$96, #$97, #$98, #$99, #$9A, #$9B, #$9C, #$9D, #$9E, #$9F,
    #$A0, #$A1, #$A2, #$A3, #$A4, #$A5, #$A6, #$A7, #$A8, #$A9, #$AA, #$AB, #$AC, #$AD, #$AE, #$AF,
    #$B0, #$B1, #$B2, #$B3, #$B4, #$B5, #$B6, #$B7, #$B8, #$B9, #$BA, #$BB, #$BC, #$BD, #$BE, #$BF,
    #$C0, #$C1, #$C2, #$C3, #$C4, #$C5, #$C6, #$C7, #$C8, #$C9, #$CA, #$CB, #$CC, #$CD, #$CE, #$CF,
    #$D0, #$D1, #$D2, #$D3, #$D4, #$D5, #$D6, #$D7, #$D8, #$D9, #$DA, #$DB, #$DC, #$DD, #$DE, #$DF,
    #$E0, #$E1, #$E2, #$E3, #$E4, #$E5, #$E6, #$E7, #$E8, #$E9, #$EA, #$EB, #$EC, #$ED, #$EE, #$EF,
    #$F0, #$F1, #$F2, #$F3, #$F4, #$F5, #$F6, #$F7, #$F8, #$F9, #$FA, #$FB, #$FC, #$FD, #$FE, #$FF
  );

function GetPhysicalEventChar(K : TKeyEvent): Char;
begin
  GetPhysicalEventChar := Physicals[(K and $0000FF00) shr 8];
end;

and you can make use of it using:
Code: [Select]
    if GetKeyEventFlags(K) = kbPhys
    then Writeln('Got key : ', KeyEventToString(K), '   ', GetPhysicalEventChar(K))
    else Writeln('Got key : ', KeyEventToString(K), '   ', GetKeyEventChar(K));

Might be easier to mask something off the original keycode/modifier and use default keyboard functions (for translation) instead. The snippets above just serves as example.

PS: do note that default windows (menu) shortcuts are intercepted so that those seem to get missed.
« Last Edit: April 21, 2017, 11:46:09 pm by molly »

molly

  • Hero Member
  • *****
  • Posts: 1787
Re: How to return special keys (e.g., Alt+H)
« Reply #7 on: April 22, 2017, 05:09:16 am »
Quote
dculp wrote:
However, pressing Alt+H returns 'ALT Key with scancode 8960' which isn't particularly usable.
So, translate it so that it becomes usable ;-)

Quote
Perhaps using KeyEventToString(K) is not the best solution. Other suggestions?
Indeed, because not every key combination that is pressed is actually 'translated'.

Quote
These codes might be useful. However, what accompanying Pascal code do I need to translate a physical keypress into one of these codes (e.g., press the Esc key to return $011B). Also, I would need F11 and F12 together with associated Shift, Ctrl, & Alt.
Check out the shiftstate field of record TKeyRecord, which is just a detailed explanation of the TKeyEvent type.

Quote
I think there should be a generic way to do this in console mode so that some kind of standard codes would be returned which could then be queried with a case statement.
I can't see any solution that would actually work in practise.

Quote
Bart wrote:
I don't think you can detect combo's like Ctrl+Shift, Ctrl+Alt using crt.readkey.
I don't think so either.


Something like this (inspired/based on Bart's pasted TP code) should be able to do the job for you.

Code: Pascal  [Select]
  1. program struckbylightning;
  2.  
  3. (*
  4. These codes might be useful. However, what accompanying Pascal code do I
  5. need to translate a physical keypress into one of these codes (e.g., press
  6. the Esc key to return $011B). Also, I would need F11 and F12 together with
  7. associated Shift, Ctrl, & Alt.
  8. *)
  9.  
  10. uses
  11.   keyboard;
  12.  
  13. Type
  14.   TShiftStateEnum=(stRSHIFT,stLSHIFT,stCTRL,stALT);
  15.   TShiftState=set of TShiftStateEnum;
  16.  
  17. Var
  18.   RKE, // RawKeyEvent
  19.   TKE:TKeyEvent; // TranslatedKeyEvent
  20.   RKR:TKeyRecord absolute RKE; // RawKeyRecord
  21.   RSS:TShiftState absolute RKR.ShiftState; // RawShiftState
  22.   MS, // ModifierString
  23.   KS:String; // keyString
  24.  
  25. begin
  26.   InitKeyBoard;
  27.  
  28.   Writeln('Press Esc,F11,F12 ("q"=quit)');
  29.   repeat
  30.     RKE:=GetKeyEvent;
  31.     TKE:=TranslateKeyEvent(RKE);
  32.     MS:='';KS:='';
  33.  
  34.     if (stCTRL in RSS) then MS:=Concat(MS,'Ctrl-');
  35.     if (stALT  in RSS) then MS:=Concat(MS,'Alt-');
  36.     if ((stRSHIFT in RSS) or (stLSHIFT in RSS)) then MS:=Concat(MS,'Shift-');
  37.  
  38.     case RKR.KeyCode of
  39.       $011b: KS:='Escape';
  40.       $8500,$8700,$8900,$8B00:KS:='F11';
  41.       $8600,$8800,$8A00,$8C00:KS:='F12';
  42.       otherwise KS:='keycode $'+HexStr(RKR.KeyCode, 4);
  43.     end;
  44.     WriteLn('You pressed:',MS,KS);
  45.   until (GetKeyEventChar(TKE)='q');
  46.  
  47.   DoneKeyBoard;
  48. end.
  49.  

Again, do note that certain key combinations are simply 'hijacked' by Windows and will never reach your code.

Bart

  • Hero Member
  • *****
  • Posts: 2718
    • Bart en Mariska's Webstek
Re: How to return special keys (e.g., Alt+H)
« Reply #8 on: April 22, 2017, 02:23:47 pm »
For what it's worth:

Code: Pascal  [Select]
  1. type
  2.   TShiftStateEnum = (ssShift, ssAlt, ssCtrl,
  3.     ssShiftLeft, ssShiftRight);
  4.  
  5.   TShiftState = set of TShiftStateEnum;
  6.  
  7. const
  8.   ShiftStateEnumStrings: array[TShiftStateEnum] of String = (
  9.     'ssShift',
  10.     'ssAlt',
  11.     'ssCtrl',
  12.     'ssShiftLeft',
  13.     'ssShiftRight'
  14.     );
  15.  
  16. function KeyShiftToShiftState(AShiftState : Integer; UseLeftRight : Boolean) : TShiftState;
  17. begin
  18.   Result := [];
  19.   If ((AShiftState and kbShift) <> 0) then
  20.   begin
  21.     Result := Result + [ssShift];
  22.     if UseLeftRight then
  23.     begin
  24.       if ((AShiftState and kbLeftShift) <> 0) then Result := Result + [ssShiftLeft];
  25.       if ((AShiftState and kbRightShift) <> 0) then Result := Result + [ssShiftRight];
  26.     end;
  27.   end;
  28.   If ((AShiftState and kbCtrl) <> 0) then
  29.     Result := Result + [ssCtrl];
  30.   If ((AShiftState and kbAlt) <> 0) then
  31.     Result := Result + [ssAlt];
  32. end;
  33.  
  34. function ShiftStateToString(SS: TShiftState): String;
  35. var
  36.   AState: TShiftStateEnum;
  37. begin
  38.   Result := '[';
  39.   for AState := Low(TShiftStateEnum) to High(TShiftStateEnum) do
  40.     if (AState in SS) then
  41.       Result := Result + ShiftStateEnumStrings[AState] + ',';
  42.   if Length(Result) > 1 then System.Delete(Result, Length(Result), 1);
  43.   Result := Result + ']';
  44. end;
  45.  
  46.  
  47. procedure GetKey(out kbKeyCode: Word; out kbKeyChar: Char; out ShiftState: TShiftState;
  48.                  out KeyName: String; UseLeftRight: Boolean = False);
  49. var
  50.   KE, TKE: TKeyEvent;
  51.   KR, TKR: TKeyRecord;
  52. begin
  53.   KE := GetKeyEvent;
  54.   TKE := TranslateKeyEvent(KE);
  55.   KR := TKeyRecord(KE);
  56.   TKR := TKeyRecord(TKE);
  57.   ShiftState := KeyShiftToShiftState(KR.ShiftState, UseLeftRight);
  58.   kbKeyCode := TKR.KeyCode;
  59.   kbKeyChar := GetKeyEventChar(TKE);
  60.   if (kbKeyChar = #0) then
  61.   begin
  62.     if (kbKeyCode = $0F00) or (kbKeyCode = $9400) then
  63.       KeyName := 'Tab'
  64.     else
  65.       KeyName := FunctionKeyName(kbKeyCode)
  66.   end
  67.   else
  68.   begin
  69.     case kbKeyChar of
  70.       'A'..'Z','a'..'z','0'..'9': KeyName := UpCase(kbKeyChar);
  71.       #8: KeyName := 'Backspace';
  72.       #9: KeyName := 'Tab';
  73.       #10: KeyName := 'LineFeed';
  74.       #13: KeyName := 'Return';
  75.       #1..#7,#11,#12,#14..#26: KeyName := '^' + Chr(Ord(kbKeyChar)-1 + Ord('A'));
  76.       #27: KeyName := 'Escape';
  77.       //#27..#31, : KeyName := '#$' + IntTohex(Ord(kbKeyChar,2));
  78.       #32: KeyName := 'Space';
  79.       otherwise
  80.         KeyName := '#$' + IntTohex(Ord(kbKeyChar),2);
  81.     end
  82.   end;
  83.   if (Copy(KeyName,1,2) = 'F-') then
  84.     KeyName := '';
  85. end;
  86.  

This will give you:

Code: [Select]
Ctr+Alt+Shift+F1 -> KeyCode: FF01 KeyChar: [00] KeyName: F1 Shift: [ssShift,ssAlt,ssCtrl]

Esc -> KeyCode: 011B KeyChar: [1B] KeyName: Escape Shift: []

Bart

molly

  • Hero Member
  • *****
  • Posts: 1787
Re: How to return special keys (e.g., Alt+H)
« Reply #9 on: April 22, 2017, 03:30:05 pm »
Thank you Bart !

That should give TS a lifetime worth of interpreting/handling key-codes  :)

Although i understand, it strikes me as a bit odd that alt was/is not handled at all in the event translation.

But, your GetKey example code allows to implement as such in a more generic and/or completely customizable way. Nice !

Bart

  • Hero Member
  • *****
  • Posts: 2718
    • Bart en Mariska's Webstek
Re: How to return special keys (e.g., Alt+H)
« Reply #10 on: April 22, 2017, 04:34:23 pm »
It probably is not cross-platform, and it lacks support for KeyName for Alt+A .. Alt+Z.

In the old days I use my WordKeys unit to implement a single line edit function.
GetKey returning a word made it much easier to handle Left, Right, Home, Ctlr+Left etc.

Unfortunately it depends on the win.tpu unit, for which fpc does not have a replacement, otherwise it would be rather easy to implement a TEdit like control for a console application.

Bart

dculp

  • Jr. Member
  • **
  • Posts: 79
Re: How to return special keys (e.g., Alt+H)
« Reply #11 on: April 24, 2017, 03:39:42 pm »
Bart --

Quote
procedure GetKey(out kbKeyCode: Word; out kbKeyChar: Char; out ShiftState: TShiftState;
                 out KeyName: String; UseLeftRight: Boolean = False);

What is the purpose of "out"? I couldn't find this in a web search but perhaps you have a link.

Handoko

  • Hero Member
  • *****
  • Posts: 1514
  • My goal: build my own game engine using Lazarus

dculp

  • Jr. Member
  • **
  • Posts: 79
Re: How to return special keys (e.g., Alt+H)
« Reply #13 on: April 25, 2017, 12:58:26 pm »
Bart --

I can't get your code to respond to Ctrl+S (I would need this for file-save). I put in the following debug writelns but no output at all for Ctrl+S. All of the other Ctrl+charkeys respond as expected. I tried this on two computers with the same keyboard and on one of these computers with two different keyboards. Also, your short version of April 21 gives the same result.

Code: Pascal  [Select]
  1.      writeln('kbKeyCode = ', kbKeyCode);
  2.      writeln('hi(kbKeyCode) = ', hi(kbKeyCode));
  3.      writeln('lo(kbKeyCode) = ', lo(kbKeyCode));
  4.      writeln('kbKeyChar = ', kbKeyChar);
  5.      writeln('KeyName = ', KeyName);
  6.      writeln('UseLeftRight = ', UseLeftRight);
  7.  

For what it's worth:

Code: Pascal  [Select]
  1. type
  2.   TShiftStateEnum = (ssShift, ssAlt, ssCtrl,
  3.     ssShiftLeft, ssShiftRight);
  4.  
  5.   TShiftState = set of TShiftStateEnum;
  6.  
  7. const
  8.   ShiftStateEnumStrings: array[TShiftStateEnum] of String = (
  9.     'ssShift',
  10.     'ssAlt',
  11.     'ssCtrl',
  12.     'ssShiftLeft',
  13.     'ssShiftRight'
  14.     );
  15.  
  16. function KeyShiftToShiftState(AShiftState : Integer; UseLeftRight : Boolean) : TShiftState;
  17. begin
  18.   Result := [];
  19.   If ((AShiftState and kbShift) <> 0) then
  20.   begin
  21.     Result := Result + [ssShift];
  22.     if UseLeftRight then
  23.     begin
  24.       if ((AShiftState and kbLeftShift) <> 0) then Result := Result + [ssShiftLeft];
  25.       if ((AShiftState and kbRightShift) <> 0) then Result := Result + [ssShiftRight];
  26.     end;
  27.   end;
  28.   If ((AShiftState and kbCtrl) <> 0) then
  29.     Result := Result + [ssCtrl];
  30.   If ((AShiftState and kbAlt) <> 0) then
  31.     Result := Result + [ssAlt];
  32. end;
  33.  
  34. function ShiftStateToString(SS: TShiftState): String;
  35. var
  36.   AState: TShiftStateEnum;
  37. begin
  38.   Result := '[';
  39.   for AState := Low(TShiftStateEnum) to High(TShiftStateEnum) do
  40.     if (AState in SS) then
  41.       Result := Result + ShiftStateEnumStrings[AState] + ',';
  42.   if Length(Result) > 1 then System.Delete(Result, Length(Result), 1);
  43.   Result := Result + ']';
  44. end;
  45.  
  46.  
  47. procedure GetKey(out kbKeyCode: Word; out kbKeyChar: Char; out ShiftState: TShiftState;
  48.                  out KeyName: String; UseLeftRight: Boolean = False);
  49. var
  50.   KE, TKE: TKeyEvent;
  51.   KR, TKR: TKeyRecord;
  52. begin
  53.   KE := GetKeyEvent;
  54.   TKE := TranslateKeyEvent(KE);
  55.   KR := TKeyRecord(KE);
  56.   TKR := TKeyRecord(TKE);
  57.   ShiftState := KeyShiftToShiftState(KR.ShiftState, UseLeftRight);
  58.   kbKeyCode := TKR.KeyCode;
  59.   kbKeyChar := GetKeyEventChar(TKE);
  60.   if (kbKeyChar = #0) then
  61.   begin
  62.     if (kbKeyCode = $0F00) or (kbKeyCode = $9400) then
  63.       KeyName := 'Tab'
  64.     else
  65.       KeyName := FunctionKeyName(kbKeyCode)
  66.   end
  67.   else
  68.   begin
  69.     case kbKeyChar of
  70.       'A'..'Z','a'..'z','0'..'9': KeyName := UpCase(kbKeyChar);
  71.       #8: KeyName := 'Backspace';
  72.       #9: KeyName := 'Tab';
  73.       #10: KeyName := 'LineFeed';
  74.       #13: KeyName := 'Return';
  75.       #1..#7,#11,#12,#14..#26: KeyName := '^' + Chr(Ord(kbKeyChar)-1 + Ord('A'));
  76.       #27: KeyName := 'Escape';
  77.       //#27..#31, : KeyName := '#$' + IntTohex(Ord(kbKeyChar,2));
  78.       #32: KeyName := 'Space';
  79.       otherwise
  80.         KeyName := '#$' + IntTohex(Ord(kbKeyChar),2);
  81.     end
  82.   end;
  83.   if (Copy(KeyName,1,2) = 'F-') then
  84.     KeyName := '';
  85. end;
  86.  

This will give you:

Code: [Select]
Ctr+Alt+Shift+F1 -> KeyCode: FF01 KeyChar: [00] KeyName: F1 Shift: [ssShift,ssAlt,ssCtrl]

Esc -> KeyCode: 011B KeyChar: [1B] KeyName: Escape Shift: []

Bart

molly

  • Hero Member
  • *****
  • Posts: 1787
Re: How to return special keys (e.g., Alt+H)
« Reply #14 on: April 25, 2017, 01:35:41 pm »
Quote
dculp wrote:
However, pressing Alt+H returns 'ALT Key with scancode 8960' which isn't particularly usable.
So, translate it so that it becomes usable ;-)

Quote
Perhaps using KeyEventToString(K) is not the best solution. Other suggestions?
Indeed, because not every key combination that is pressed is actually 'translated'.

Quote
These codes might be useful. However, what accompanying Pascal code do I need to translate a physical keypress into one of these codes (e.g., press the Esc key to return $011B). Also, I would need F11 and F12 together with associated Shift, Ctrl, & Alt.
Check out the shiftstate field of record TKeyRecord, which is just a detailed explanation of the TKeyEvent type.

Quote
I think there should be a generic way to do this in console mode so that some kind of standard codes would be returned which could then be queried with a case statement.
I can't see any solution that would actually work in practise.

Quote
Bart wrote:
I don't think you can detect combo's like Ctrl+Shift, Ctrl+Alt using crt.readkey.
I don't think so either.


Something like this (inspired/based on Bart's pasted TP code) should be able to do the job for you.

Code: Pascal  [Select]
  1. program struckbylightning;
  2.  
  3. (*
  4. These codes might be useful. However, what accompanying Pascal code do I
  5. need to translate a physical keypress into one of these codes (e.g., press
  6. the Esc key to return $011B). Also, I would need F11 and F12 together with
  7. associated Shift, Ctrl, & Alt.
  8. *)
  9.  
  10. uses
  11.   keyboard;
  12.  
  13. Type
  14.   TShiftStateEnum=(stRSHIFT,stLSHIFT,stCTRL,stALT);
  15.   TShiftState=set of TShiftStateEnum;
  16.  
  17. Var
  18.   RKE, // RawKeyEvent
  19.   TKE:TKeyEvent; // TranslatedKeyEvent
  20.   RKR:TKeyRecord absolute RKE; // RawKeyRecord
  21.   RSS:TShiftState absolute RKR.ShiftState; // RawShiftState
  22.   MS, // ModifierString
  23.   KS:String; // keyString
  24.  
  25. begin
  26.   InitKeyBoard;
  27.  
  28.   Writeln('Press Esc,F11,F12 ("q"=quit)');
  29.   repeat
  30.     RKE:=GetKeyEvent;
  31.     TKE:=TranslateKeyEvent(RKE);
  32.     MS:='';KS:='';
  33.  
  34.     if (stCTRL in RSS) then MS:=Concat(MS,'Ctrl-');
  35.     if (stALT  in RSS) then MS:=Concat(MS,'Alt-');
  36.     if ((stRSHIFT in RSS) or (stLSHIFT in RSS)) then MS:=Concat(MS,'Shift-');
  37.  
  38.     case RKR.KeyCode of
  39.       $011b: KS:='Escape';
  40.       $8500,$8700,$8900,$8B00:KS:='F11';
  41.       $8600,$8800,$8A00,$8C00:KS:='F12';
  42.       otherwise KS:='keycode $'+HexStr(RKR.KeyCode, 4);
  43.     end;
  44.     WriteLn('You pressed:',MS,KS);
  45.   until (GetKeyEventChar(TKE)='q');
  46.  
  47.   DoneKeyBoard;
  48. end.
  49.  

Again, do note that certain key combinations are simply 'hijacked' by Windows and will never reach your code.



Bart --

I can't get your code to respond to Ctrl+S (I would need this for file-save). I put in the following debug writelns but no output at all for Ctrl+S. All of the other Ctrl+charkeys respond as expected. I tried this on two computers with the same keyboard and on one of these computers with two different keyboards. Also, your short version of April 21 gives the same result.

Code: Pascal  [Select]
  1.      writeln('kbKeyCode = ', kbKeyCode);
  2.      writeln('hi(kbKeyCode) = ', hi(kbKeyCode));
  3.      writeln('lo(kbKeyCode) = ', lo(kbKeyCode));
  4.      writeln('kbKeyChar = ', kbKeyChar);
  5.      writeln('KeyName = ', KeyName);
  6.      writeln('UseLeftRight = ', UseLeftRight);
  7.  

For what it's worth:

Code: Pascal  [Select]
  1. type
  2.   TShiftStateEnum = (ssShift, ssAlt, ssCtrl,
  3.     ssShiftLeft, ssShiftRight);
  4.  
  5.   TShiftState = set of TShiftStateEnum;
  6.  
  7. const
  8.   ShiftStateEnumStrings: array[TShiftStateEnum] of String = (
  9.     'ssShift',
  10.     'ssAlt',
  11.     'ssCtrl',
  12.     'ssShiftLeft',
  13.     'ssShiftRight'
  14.     );
  15.  
  16. function KeyShiftToShiftState(AShiftState : Integer; UseLeftRight : Boolean) : TShiftState;
  17. begin
  18.   Result := [];
  19.   If ((AShiftState and kbShift) <> 0) then
  20.   begin
  21.     Result := Result + [ssShift];
  22.     if UseLeftRight then
  23.     begin
  24.       if ((AShiftState and kbLeftShift) <> 0) then Result := Result + [ssShiftLeft];
  25.       if ((AShiftState and kbRightShift) <> 0) then Result := Result + [ssShiftRight];
  26.     end;
  27.   end;
  28.   If ((AShiftState and kbCtrl) <> 0) then
  29.     Result := Result + [ssCtrl];
  30.   If ((AShiftState and kbAlt) <> 0) then
  31.     Result := Result + [ssAlt];
  32. end;
  33.  
  34. function ShiftStateToString(SS: TShiftState): String;
  35. var
  36.   AState: TShiftStateEnum;
  37. begin
  38.   Result := '[';
  39.   for AState := Low(TShiftStateEnum) to High(TShiftStateEnum) do
  40.     if (AState in SS) then
  41.       Result := Result + ShiftStateEnumStrings[AState] + ',';
  42.   if Length(Result) > 1 then System.Delete(Result, Length(Result), 1);
  43.   Result := Result + ']';
  44. end;
  45.  
  46.  
  47. procedure GetKey(out kbKeyCode: Word; out kbKeyChar: Char; out ShiftState: TShiftState;
  48.                  out KeyName: String; UseLeftRight: Boolean = False);
  49. var
  50.   KE, TKE: TKeyEvent;
  51.   KR, TKR: TKeyRecord;
  52. begin
  53.   KE := GetKeyEvent;
  54.   TKE := TranslateKeyEvent(KE);
  55.   KR := TKeyRecord(KE);
  56.   TKR := TKeyRecord(TKE);
  57.   ShiftState := KeyShiftToShiftState(KR.ShiftState, UseLeftRight);
  58.   kbKeyCode := TKR.KeyCode;
  59.   kbKeyChar := GetKeyEventChar(TKE);
  60.   if (kbKeyChar = #0) then
  61.   begin
  62.     if (kbKeyCode = $0F00) or (kbKeyCode = $9400) then
  63.       KeyName := 'Tab'
  64.     else
  65.       KeyName := FunctionKeyName(kbKeyCode)
  66.   end
  67.   else
  68.   begin
  69.     case kbKeyChar of
  70.       'A'..'Z','a'..'z','0'..'9': KeyName := UpCase(kbKeyChar);
  71.       #8: KeyName := 'Backspace';
  72.       #9: KeyName := 'Tab';
  73.       #10: KeyName := 'LineFeed';
  74.       #13: KeyName := 'Return';
  75.       #1..#7,#11,#12,#14..#26: KeyName := '^' + Chr(Ord(kbKeyChar)-1 + Ord('A'));
  76.       #27: KeyName := 'Escape';
  77.       //#27..#31, : KeyName := '#$' + IntTohex(Ord(kbKeyChar,2));
  78.       #32: KeyName := 'Space';
  79.       otherwise
  80.         KeyName := '#$' + IntTohex(Ord(kbKeyChar),2);
  81.     end
  82.   end;
  83.   if (Copy(KeyName,1,2) = 'F-') then
  84.     KeyName := '';
  85. end;
  86.  

This will give you:

Code: [Select]
Ctr+Alt+Shift+F1 -> KeyCode: FF01 KeyChar: [00] KeyName: F1 Shift: [ssShift,ssAlt,ssCtrl]

Esc -> KeyCode: 011B KeyChar: [1B] KeyName: Escape Shift: []

Bart
You might be able to try this but have no clue if that is too late to do inside your code for default windows keyboard driver.
« Last Edit: April 25, 2017, 01:48:25 pm by molly »

 

Recent

Get Lazarus at SourceForge.net. Fast, secure and Free Open Source software downloads Open Hub project report for Lazarus