Scena.Keyboard.pas 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. //
  2. // The graphics platform GLScene https://github.com/glscene
  3. //
  4. unit Scena.Keyboard;
  5. (*
  6. Provides on demand state of any key on the keyboard as well as a set of
  7. utility functions for working with virtual key codes.
  8. Note that windows maps the mouse buttons to virtual key codes too, and you
  9. can use the functions/classes in this unit to check mouse buttons too.
  10. See "Virtual-Key Codes" in the Win32 programmers references for a list of
  11. key code constants (VK_* constants are declared in the "Windows" unit).
  12. *)
  13. interface
  14. {$I Scena.inc}
  15. uses
  16. Winapi.Windows,
  17. System.SysUtils;
  18. type
  19. TVirtualKeyCode = Integer;
  20. const
  21. // pseudo wheel keys (we squat F23/F24), see KeyboardNotifyWheelMoved
  22. VK_MOUSEWHEELUP = VK_F23;
  23. VK_MOUSEWHEELDOWN = VK_F24;
  24. (* Check if the key corresponding to the given Char is down.
  25. The character is mapped to the <i>main keyboard</i> only, and not to the
  26. numeric keypad.
  27. The Shift/Ctrl/Alt state combinations that may be required to type the
  28. character are ignored (ie. 'a' is equivalent to 'A', and on my french
  29. keyboard, '5' = '(' = '[' since they all share the same physical key). *)
  30. function IsKeyDown(c: Char): Boolean; overload;
  31. (* Check if the given virtual key is down.
  32. This function is just a wrapper for GetAsyncKeyState. *)
  33. function IsKeyDown(vk: TVirtualKeyCode): Boolean; overload;
  34. (* Returns the first pressed key whose virtual key code is >= to minVkCode.
  35. If no key is pressed, the return value is -1, this function does NOT
  36. wait for user input.
  37. If you don't care about multiple key presses, just don't use the parameter. *)
  38. function KeyPressed(minVkCode: TVirtualKeyCode = 0): TVirtualKeyCode;
  39. (* Converts a virtual key code to its name.
  40. The name is expressed using the locale windows options. *)
  41. function VirtualKeyCodeToKeyName(vk: TVirtualKeyCode): String;
  42. (* Converts a key name to its virtual key code.
  43. The comparison is **NOT** case-sensitive, if no match is found, returns -1.
  44. The name is expressed using the locale windows options, except for mouse
  45. buttons which are translated to 'LBUTTON', 'MBUTTON' and 'RBUTTON'. *)
  46. function KeyNameToVirtualKeyCode(const keyName: String): TVirtualKeyCode;
  47. (* Returns the virtual keycode corresponding to the given char.
  48. The returned code is untranslated, f.i. 'a' and 'A' will give the same
  49. result. A return value of -1 means that the characted cannot be entered
  50. using the keyboard. *)
  51. function CharToVirtualKeyCode(c: Char): TVirtualKeyCode;
  52. (* Use this procedure to notify a wheel movement and have it resurfaced as key stroke.
  53. Honoured by IsKeyDown and KeyPressed *)
  54. procedure KeyboardNotifyWheelMoved(wheelDelta: Integer);
  55. var
  56. vLastWheelDelta: Integer;
  57. // ---------------------------------------------------------------------
  58. implementation
  59. // ---------------------------------------------------------------------
  60. const
  61. cLBUTTON = 'Left Mouse Button';
  62. cMBUTTON = 'Middle Mouse Button';
  63. cRBUTTON = 'Right Mouse Button';
  64. cUP = 'Up';
  65. cDOWN = 'Down';
  66. cRIGHT = 'Right';
  67. cLEFT = 'Left';
  68. cPAGEUP = 'Page up';
  69. cPAGEDOWN = 'Page down';
  70. cHOME = 'Home';
  71. cEND = 'End';
  72. cMOUSEWHEELUP = 'Mouse Wheel Up';
  73. cMOUSEWHEELDOWN = 'Mouse Wheel Down';
  74. cPAUSE = 'Pause';
  75. cSNAPSHOT = 'Print Screen';
  76. cNUMLOCK = 'Num Lock';
  77. cINSERT = 'Insert';
  78. cDELETE = 'Delete';
  79. cDIVIDE = 'Num /';
  80. cLWIN = 'Left Win';
  81. cRWIN = 'Right Win';
  82. cAPPS = 'Application Key';
  83. c0 = '~';
  84. c1 = '[';
  85. c2 = ']';
  86. c3 = ';';
  87. c4 = '''';
  88. c5 = '<';
  89. c6 = '>';
  90. c7 = '/';
  91. c8 = '\';
  92. function IsKeyDown(c: Char): Boolean;
  93. var
  94. vk: Integer;
  95. begin
  96. // '$FF' filters out translators like Shift, Ctrl, Alt
  97. vk := VkKeyScan(c) and $FF;
  98. if vk <> $FF then
  99. Result := (GetAsyncKeyState(vk) < 0)
  100. else
  101. Result := False;
  102. end;
  103. function IsKeyDown(vk: TVirtualKeyCode): Boolean;
  104. begin
  105. case vk of
  106. VK_MOUSEWHEELUP:
  107. begin
  108. Result := vLastWheelDelta > 0;
  109. if Result then
  110. vLastWheelDelta := 0;
  111. end;
  112. VK_MOUSEWHEELDOWN:
  113. begin
  114. Result := vLastWheelDelta < 0;
  115. if Result then
  116. vLastWheelDelta := 0;
  117. end;
  118. else
  119. Result := (GetAsyncKeyState(vk) < 0);
  120. end;
  121. end;
  122. function KeyPressed(minVkCode: TVirtualKeyCode = 0): TVirtualKeyCode;
  123. var
  124. i: Integer;
  125. buf: TKeyboardState;
  126. begin
  127. Assert(minVkCode >= 0);
  128. Result := -1;
  129. if GetKeyboardState(buf) then
  130. begin
  131. for i := minVkCode to High(buf) do
  132. begin
  133. if (buf[i] and $80) <> 0 then
  134. begin
  135. Result := i;
  136. Exit;
  137. end;
  138. end;
  139. end;
  140. if vLastWheelDelta <> 0 then
  141. begin
  142. if vLastWheelDelta > 0 then
  143. Result := VK_MOUSEWHEELUP
  144. else
  145. Result := VK_MOUSEWHEELDOWN;
  146. vLastWheelDelta := 0;
  147. end;
  148. end;
  149. function VirtualKeyCodeToKeyName(vk: TVirtualKeyCode): String;
  150. var
  151. nSize: Integer;
  152. begin
  153. // Win32 API can't translate mouse button virtual keys to string
  154. case vk of
  155. VK_LBUTTON:
  156. Result := cLBUTTON;
  157. VK_MBUTTON:
  158. Result := cMBUTTON;
  159. VK_RBUTTON:
  160. Result := cRBUTTON;
  161. VK_UP:
  162. Result := cUP;
  163. VK_DOWN:
  164. Result := cDOWN;
  165. VK_LEFT:
  166. Result := cLEFT;
  167. VK_RIGHT:
  168. Result := cRIGHT;
  169. VK_PRIOR:
  170. Result := cPAGEUP;
  171. VK_NEXT:
  172. Result := cPAGEDOWN;
  173. VK_HOME:
  174. Result := cHOME;
  175. VK_END:
  176. Result := cEND;
  177. VK_MOUSEWHEELUP:
  178. Result := cMOUSEWHEELUP;
  179. VK_MOUSEWHEELDOWN:
  180. Result := cMOUSEWHEELDOWN;
  181. VK_PAUSE: Result := cPAUSE;
  182. VK_SNAPSHOT: Result := cSNAPSHOT;
  183. VK_NUMLOCK: Result := cNUMLOCK;
  184. VK_INSERT: Result := cINSERT;
  185. VK_DELETE: Result := cDELETE;
  186. VK_DIVIDE: Result := cDIVIDE;
  187. VK_LWIN: Result := cLWIN;
  188. VK_RWIN: Result := cRWIN;
  189. VK_APPS: Result := cAPPS;
  190. 192: Result := c0;
  191. 219: Result := c1;
  192. 221: Result := c2;
  193. 186: Result := c3;
  194. 222: Result := c4;
  195. 188: Result := c5;
  196. 190: Result := c6;
  197. 191: Result := c7;
  198. 220: Result := c8;
  199. else
  200. nSize := 32; // should be enough
  201. SetLength(Result, nSize);
  202. vk := MapVirtualKey(vk, 0);
  203. nSize := GetKeyNameText((vk and $FF) shl 16, PChar(Result), nSize);
  204. SetLength(Result, nSize);
  205. end;
  206. end;
  207. function KeyNameToVirtualKeyCode(const keyName: String): TVirtualKeyCode;
  208. var
  209. i: Integer;
  210. begin
  211. // ok, I admit this is plain ugly. 8)
  212. Result := -1;
  213. for i := 0 to 255 do
  214. begin
  215. if SameText(VirtualKeyCodeToKeyName(i), keyName) then
  216. begin
  217. Result := i;
  218. Break;
  219. end;
  220. end;
  221. end;
  222. function CharToVirtualKeyCode(c: Char): TVirtualKeyCode;
  223. begin
  224. Result := VkKeyScan(c) and $FF;
  225. if Result = $FF then
  226. Result := -1;
  227. end;
  228. procedure KeyboardNotifyWheelMoved(wheelDelta: Integer);
  229. begin
  230. vLastWheelDelta := wheelDelta;
  231. end;
  232. end.