FreeBASIC  0.91.0
io_input.c
Go to the documentation of this file.
1 /* console input helpers */
2 
3 #include "../fb.h"
4 #include "fb_private_console.h"
5 #include <ctype.h>
6 
7 #define KEY_BUFFER_LEN 512
9 static size_t key_head = 0, key_tail = 0;
10 static INPUT_RECORD input_events[KEY_BUFFER_LEN];
11 static unsigned key_scratch_pad = 0;
13 
14 typedef struct _FB_KEY_CODES {
15  unsigned short value_normal;
16  unsigned short value_shift;
17  unsigned short value_ctrl;
18  unsigned short value_alt;
19 } FB_KEY_CODES;
20 
21 typedef struct _FB_KEY_LIST_ENTRY {
22  unsigned short scan_code;
25 
27  { 0x001C, { 0x000D, 0x000D, 0x000A, 0xA600 } },
28  { 0x0035, { 0x002F, 0x003F, 0x9500, 0xA400 } },
29  { 0x0047, { 0x4700, 0x4700, 0x7700, 0x9700 } },
30  { 0x0048, { 0x4800, 0x4800, 0x8D00, 0x9800 } },
31  { 0x0049, { 0x4900, 0x4900, 0x8400, 0x9900 } },
32  { 0x004B, { 0x4B00, 0x4B00, 0x7300, 0x9B00 } },
33  { 0x004D, { 0x4D00, 0x4D00, 0x7400, 0x9D00 } },
34  { 0x004F, { 0x4F00, 0x4F00, 0x7500, 0x9F00 } },
35  { 0x0050, { 0x5000, 0x5000, 0x9100, 0xA000 } },
36  { 0x0051, { 0x5100, 0x5100, 0x7600, 0xA100 } },
37  { 0x0052, { 0x5200, 0x5200, 0x9200, 0xA200 } },
38  { 0x0053, { 0x5300, 0x5300, 0x9300, 0xA300 } }
39 };
40 
41 #define FB_KEY_LIST_SIZE (sizeof(fb_ext_key_entries)/sizeof(FB_KEY_LIST_ENTRY))
42 
43 static const FB_KEY_CODES fb_asc_key_codes[] = {
44  { 0x0000, 0x0000, 0x0000, 0x0000 },
45  { 0x001B, 0x001B, 0x001B, 0x0100 },
46  { 0x0031, 0x0021, 0x0000, 0x7800 },
47  { 0x0032, 0x0040, 0x0300, 0x7900 },
48 
49  { 0x0033, 0x0023, 0x0000, 0x7A00 },
50  { 0x0034, 0x0024, 0x0000, 0x7B00 },
51  { 0x0035, 0x0025, 0x0000, 0x7C00 },
52  { 0x0036, 0x005E, 0x001E, 0x7D00 },
53 
54  { 0x0037, 0x0026, 0x001F, 0x7E00 },
55  { 0x0038, 0x002B, 0x0000, 0x7F00 },
56  { 0x0039, 0x0028, 0x0000, 0x8000 },
57  { 0x0030, 0x0029, 0x0000, 0x8100 },
58 
59  { 0x002D, 0x005F, 0x001F, 0x8200 },
60  { 0x003D, 0x002B, 0x0000, 0x8300 },
61  { 0x0008, 0x0008, 0x007F, 0xE000 },
62  { 0x0009, 0x0F00, 0x9400, 0x0F00 },
63 
64  { 0x0071, 0x0051, 0x0011, 0x1000 }, /* 16 */
65  { 0x0077, 0x0057, 0x0017, 0x1100 },
66  { 0x0065, 0x0045, 0x0005, 0x1200 },
67  { 0x0072, 0x0052, 0x0012, 0x1300 },
68 
69  { 0x0074, 0x0054, 0x0014, 0x1400 },
70  { 0x0079, 0x0059, 0x0019, 0x1500 },
71  { 0x0075, 0x0055, 0x0015, 0x1600 },
72  { 0x0069, 0x0049, 0x0009, 0x1700 },
73 
74  { 0x006F, 0x004F, 0x000F, 0x1800 },
75  { 0x0070, 0x0050, 0x0010, 0x1900 },
76  { 0x005B, 0x007B, 0x001B, 0x1A00 },
77  { 0x005D, 0x007D, 0x001D, 0x1B00 },
78 
79  { 0x000D, 0x000D, 0x000A, 0x1C00 },
80  { 0x0000, 0x0000, 0x0000, 0x0000 },
81  { 0x0061, 0x0041, 0x0001, 0x1E00 },
82  { 0x0073, 0x0053, 0x0013, 0x1F00 },
83 
84  { 0x0064, 0x0044, 0x0004, 0x2000 }, /* 32 */
85  { 0x0066, 0x0046, 0x0006, 0x2100 },
86  { 0x0067, 0x0047, 0x0007, 0x2200 },
87  { 0x0068, 0x0048, 0x0008, 0x2300 },
88 
89  { 0x006A, 0x004A, 0x000A, 0x2400 },
90  { 0x006B, 0x004B, 0x000B, 0x2500 },
91  { 0x006C, 0x004C, 0x000C, 0x2600 },
92  { 0x003B, 0x003A, 0x0000, 0x2700 },
93 
94  { 0x0027, 0x0022, 0x0000, 0x2800 },
95  { 0x0060, 0x007E, 0x0000, 0x2900 },
96  { 0x0000, 0x0000, 0x0000, 0x0000 },
97  { 0x005C, 0x007C, 0x001C, 0x0000 },
98 
99  { 0x007A, 0x005A, 0x001A, 0x2C00 },
100  { 0x0078, 0x0058, 0x0018, 0x2D00 },
101  { 0x0063, 0x0043, 0x0003, 0x2E00 },
102  { 0x0076, 0x0056, 0x0016, 0x2F00 },
103 
104  { 0x0062, 0x0042, 0x0002, 0x3000 }, /* 48 */
105  { 0x006E, 0x004E, 0x000E, 0x3100 },
106  { 0x006D, 0x004D, 0x000D, 0x3200 },
107  { 0x002C, 0x003C, 0x0000, 0x3300 },
108 
109  { 0x002E, 0x003E, 0x0000, 0x3400 },
110  { 0x002F, 0x003F, 0x0000, 0x3500 },
111  { 0x0000, 0x0000, 0x0000, 0x0000 },
112  { 0x002A, 0x0000, 0x0072, 0x0000 },
113 
114  { 0x0000, 0x0000, 0x0000, 0x0000 },
115  { 0x0020, 0x0020, 0x0020, 0x0020 },
116  { 0x0000, 0x0000, 0x0000, 0x0000 },
117  { 0x3B00, 0x5400, 0x5E00, 0x6800 },
118 
119  { 0x3C00, 0x5500, 0x5F00, 0x6900 },
120  { 0x3D00, 0x5600, 0x6000, 0x6A00 },
121  { 0x3E00, 0x5700, 0x6100, 0x6B00 },
122  { 0x3F00, 0x5800, 0x6200, 0x6C00 },
123 
124  { 0x4000, 0x5900, 0x6300, 0x6D00 }, /* 64 */
125  { 0x4100, 0x5A00, 0x6400, 0x6E00 },
126  { 0x4200, 0x5B00, 0x6500, 0x6F00 },
127  { 0x4300, 0x5C00, 0x6600, 0x7000 },
128 
129  { 0x4400, 0x5D00, 0x6700, 0x7100 },
130  { 0x0000, 0x0000, 0x0000, 0x0000 },
131  { 0x0000, 0x0000, 0x0000, 0x0000 },
132  { 0x4700, 0x0037, 0x7700, 0x0000 },
133 
134  { 0x4800, 0x0038, 0x8D00, 0x0000 },
135  { 0x4900, 0x0039, 0x8400, 0x0000 },
136  { 0x0000, 0x002D, 0x0000, 0x0000 },
137  { 0x4B00, 0x0034, 0x7300, 0x0000 },
138 
139  { 0x4C00, 0x0035, 0x8F00, 0x4C00 },
140  { 0x4D00, 0x0036, 0x7400, 0x0000 },
141  { 0x0000, 0x002B, 0x0000, 0x0000 },
142  { 0x4F00, 0x0031, 0x7500, 0x0000 },
143 
144  { 0x5000, 0x0032, 0x9100, 0x0000 }, /* 80 */
145  { 0x5100, 0x0033, 0x7600, 0x0000 },
146  { 0x5200, 0x0030, 0x9200, 0x0000 },
147  { 0x5300, 0x002E, 0x9300, 0x0000 },
148 
149  { 0x0000, 0x0000, 0x0000, 0x0000 },
150  { 0x0000, 0x0000, 0x0000, 0x0000 },
151  { 0x0000, 0x0000, 0x0000, 0x0000 },
152  { 0x8500, 0x8700, 0x8900, 0x8B00 },
153 
154  { 0x8600, 0x8800, 0x8A00, 0x8C00 }
155 };
156 
157 #define FB_KEY_CODES_SIZE (sizeof(fb_asc_key_codes)/sizeof(FB_KEY_CODES))
158 
159 static void fb_hConsolePostKey( int key, const KEY_EVENT_RECORD *key_event )
160 {
161  INPUT_RECORD *record;
162 
163  FB_LOCK();
164 
166 
167  DBG_ASSERT( key_event!=NULL );
168 
169  record = input_events + key_tail;
170  memcpy( &record->Event.KeyEvent,
171  key_event,
172  sizeof( KEY_EVENT_RECORD ) );
173  record->EventType = KEY_EVENT;
174 
175  if (((key_tail + 1) & (KEY_BUFFER_LEN - 1)) == key_head)
176  key_head = (key_head + 1) & (KEY_BUFFER_LEN - 1);
177  key_tail = (key_tail + 1) & (KEY_BUFFER_LEN - 1);
178 
180 
181  FB_UNLOCK();
182 }
183 
185 {
186  int result;
187 
189 
190  FB_LOCK();
191  result = key_buffer_changed;
193  FB_UNLOCK();
194 
195  return result;
196 }
197 
198 static int fb_hConsoleGetKeyEx( int full, int allow_remove )
199 {
200  int key = -1;
201 
203 
204  FB_LOCK();
205 
206  if (key_head != key_tail) {
207  int do_remove = allow_remove;
208  key = key_buffer[key_head];
209  if( key > 255 ) {
210  if( !full ) {
211  key_buffer[key_head] = (key >> 8);
212  key = (unsigned) (unsigned char) FB_EXT_CHAR;
213  do_remove = FALSE;
214  }
215  }
216  if( do_remove ) {
217  key_head = (key_head + 1) & (KEY_BUFFER_LEN - 1);
218  /* Reset the status for "key buffer changed" when a key
219  * was removed from the input queue. */
221  }
222  }
223 
224  FB_UNLOCK();
225 
226  return key;
227 }
228 
229 int fb_hConsoleGetKey(int full)
230 {
231  return fb_hConsoleGetKeyEx( full, TRUE );
232 }
233 
234 int fb_hConsolePeekKey(int full)
235 {
236  return fb_hConsoleGetKeyEx( full, FALSE );
237 }
238 
240 {
241  size_t key_idx;
242 
243  FB_LOCK();
244 
245  while( fb_ConsoleProcessEvents( ) )
246  ;
247 
248  key_idx = key_head;
249  while( key_idx != key_tail ) {
250  DWORD dwEventsWritten = 0;
251  size_t count = (key_idx > key_tail) ? (KEY_BUFFER_LEN - key_idx) : (key_tail - key_idx);
252 
253  WriteConsoleInput( __fb_in_handle,
254  input_events + key_idx,
255  count,
256  &dwEventsWritten );
257 
258  key_idx += count;
259  if( key_idx==KEY_BUFFER_LEN )
260  key_idx = 0;
261  }
262 
263  FB_UNLOCK();
264 }
265 
266 static void fb_hConsoleProcessKeyEvent( KEY_EVENT_RECORD *event )
267 {
268  int KeyCode;
269  int ValidKeyStatus, ValidKeys, AddScratchPadKey = FALSE;
270  if( event->bKeyDown ) {
271  KeyCode =
272  fb_hConsoleTranslateKey( event->uChar.AsciiChar,
273  event->wVirtualScanCode,
274  event->wVirtualKeyCode,
275  event->dwControlKeyState,
276  FALSE );
277  } else {
278  KeyCode = -1;
279  }
280 
281  ValidKeyStatus =
282  ((event->dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED | SHIFT_PRESSED))==0)
283  && ((event->dwControlKeyState & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED))!=0);
284 #if 0
285  ValidKeys =
286  (event->wVirtualScanCode >= 0x47 && event->wVirtualScanCode <= 0x49)
287  || (event->wVirtualScanCode >= 0x4b && event->wVirtualScanCode <= 0x4d)
288  || (event->wVirtualScanCode >= 0x4f && event->wVirtualScanCode <= 0x52);
289 #else
290  ValidKeys =
291  (event->wVirtualKeyCode >= VK_NUMPAD0
292  && event->wVirtualKeyCode <= VK_NUMPAD9);
293 #endif
294 
295  if( ValidKeys && ValidKeyStatus ) {
296  if( event->bKeyDown ) {
297  int number;
298 #if 0
299  if( event->wVirtualScanCode <= 0x49 ) {
300  number = event->wVirtualScanCode - 0x40;
301  } else if( event->wVirtualScanCode <= 0x4d ) {
302  number = event->wVirtualScanCode - 0x47;
303  } else if( event->wVirtualScanCode <= 0x51 ) {
304  number = event->wVirtualScanCode - 0x4e;
305  } else {
306  number = 0;
307  }
308 #else
309  number = event->wVirtualKeyCode - VK_NUMPAD0;
310 #endif
311  key_scratch_pad *= 10;
312  key_scratch_pad += number;
313  }
314  } else if( KeyCode!=-1 ) {
315  key_scratch_pad = 0;
316  } else if( !ValidKeyStatus ) {
317  AddScratchPadKey = key_scratch_pad!=0;
318  }
319 
320 #if 0
321  printf("%04hx\n", event->wVirtualScanCode);
322  printf("%04hx, %08x\n", event->wVirtualKeyCode, MapVirtualKey( event->wVirtualScanCode, 1));
323  printf("%02x\n", (unsigned) (unsigned char) event->uChar.AsciiChar);
324  printf("%08x, %d\n", key_scratch_pad, ValidKeyStatus);
325 #endif
326 
327  if( AddScratchPadKey ) {
328  char chAsciiCode= (char) (key_scratch_pad & 0xFF);
329  KEY_EVENT_RECORD rec;
330  SHORT wVkCode = VkKeyScan(chAsciiCode);
331  memset( &rec, 0, sizeof(KEY_EVENT_RECORD) );
332  rec.uChar.AsciiChar = chAsciiCode;
333  rec.wVirtualKeyCode = wVkCode & 0xFF;
334  rec.dwControlKeyState |= (((wVkCode & 0x100)!=0) ? SHIFT_PRESSED : 0);
335  rec.dwControlKeyState |= (((wVkCode & 0x200)!=0) ? LEFT_CTRL_PRESSED : 0);
336  rec.dwControlKeyState |= (((wVkCode & 0x400)!=0) ? LEFT_ALT_PRESSED : 0);
337  rec.wVirtualScanCode = MapVirtualKey( rec.wVirtualKeyCode, 0 );
338  fb_hConsolePostKey( key_scratch_pad & 0xFF, &rec );
339  key_scratch_pad = 0;
340  }
341 
342  if( KeyCode!=-1 ) {
343  fb_hConsolePostKey(KeyCode, event);
344  }
345 }
346 
347 static BOOL WINAPI fb_hConsoleHandlerRoutine( DWORD dwCtrlType )
348 {
349  switch( dwCtrlType ) {
350  case CTRL_CLOSE_EVENT:
351  case CTRL_LOGOFF_EVENT:
352  case CTRL_SHUTDOWN_EVENT:
353  {
354  KEY_EVENT_RECORD rec;
355  memset( &rec, 0, sizeof(KEY_EVENT_RECORD) );
356  rec.wVirtualKeyCode = VK_F4;
357  rec.dwControlKeyState = LEFT_ALT_PRESSED;
358  rec.wVirtualScanCode = MapVirtualKey( rec.wVirtualKeyCode, 0 );
359  fb_hConsolePostKey( KEY_QUIT, &rec );
360  }
361  return TRUE;
362  }
363  return FALSE;
364 }
365 
367 
368 static void fb_hExitControlHandler( void )
369 {
370  if( control_handler_inited ) {
371  SetConsoleCtrlHandler( fb_hConsoleHandlerRoutine, FALSE );
372  }
373 }
374 
375 static void fb_hInitControlHandler( void )
376 {
377  FB_LOCK();
378  if( !control_handler_inited ) {
380  atexit( fb_hExitControlHandler );
381  SetConsoleCtrlHandler( fb_hConsoleHandlerRoutine, TRUE );
382  }
383  FB_UNLOCK();
384 }
385 
387 {
388  int got_event = FALSE;
389  INPUT_RECORD ir;
390  DWORD dwRead;
391 
393 
394  do {
395  if( !PeekConsoleInput( __fb_in_handle, &ir, 1, &dwRead ) )
396  dwRead = 0;
397 
398  if( dwRead > 0 ) {
399  ReadConsoleInput( __fb_in_handle, &ir, 1, &dwRead );
400 
401  FB_LOCK();
402 
403  switch ( ir.EventType ) {
404  case KEY_EVENT:
405  if( ir.Event.KeyEvent.bKeyDown && ir.Event.KeyEvent.wRepeatCount != 0 ) {
406  fb_hConsoleProcessKeyEvent( &ir.Event.KeyEvent );
407  } else if( !ir.Event.KeyEvent.bKeyDown ) {
408  fb_hConsoleProcessKeyEvent( &ir.Event.KeyEvent );
409  }
410  break;
411 
412  case MOUSE_EVENT:
414  __fb_con.mouseEventHook( &ir.Event.MouseEvent );
415  got_event = TRUE;
416  }
417  break;
418  }
419 
420  FB_UNLOCK();
421  }
422 
423  } while( dwRead != 0 );
424 
425  return got_event;
426 }
427 
434  (
435  char AsciiChar,
436  WORD wVsCode,
437  WORD wVkCode,
438  DWORD dwControlKeyState,
439  int bEnhancedKeysOnly
440  )
441 {
442  int KeyCode = 0, AddKeyCode = FALSE;
443  int is_ext_code = AsciiChar==0;
444 
445  /* Process ENHANCED_KEY's in a different way */
446  if( (dwControlKeyState & ENHANCED_KEY)!=0 && is_ext_code) {
447  size_t i;
448  for( i=0; i!=FB_KEY_LIST_SIZE; ++i ) {
449  const FB_KEY_LIST_ENTRY *entry =
450  fb_ext_key_entries + i;
451  if(entry->scan_code==wVsCode) {
452  const FB_KEY_CODES *codes = &entry->codes;
453  if( dwControlKeyState & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED) ) {
454  KeyCode = codes->value_alt;
455  AddKeyCode = KeyCode!=0;
456  } else if( dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED) ) {
457  KeyCode = codes->value_ctrl;
458  AddKeyCode = KeyCode!=0;
459  } else if( dwControlKeyState & SHIFT_PRESSED ) {
460  KeyCode = codes->value_shift;
461  AddKeyCode = KeyCode!=0;
462  } else {
463  KeyCode = codes->value_normal;
464  AddKeyCode = TRUE;
465  }
466  break;
467  }
468  }
469  } else {
470  unsigned uiAsciiChar = (unsigned) (unsigned char) AsciiChar;
471  unsigned uiNormalKey, uiNormalKeyOtherCase;
472  /* Test if we must translate a "normal" key into an enhanced key */
473  if( wVsCode < FB_KEY_CODES_SIZE ) {
474  const FB_KEY_CODES *codes = fb_asc_key_codes + wVsCode;
475 
476  uiNormalKey = MapVirtualKey( wVkCode, 2 ) & 0xFFFF;
477  if( isupper( (int) uiNormalKey ) ) {
478  uiNormalKeyOtherCase = tolower( (int) uiNormalKey );
479  } else if( islower( (int) uiNormalKey ) ) {
480  uiNormalKeyOtherCase = toupper( (int) uiNormalKey );
481  } else {
482  uiNormalKeyOtherCase = uiNormalKey;
483  }
484 
485  if( dwControlKeyState & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED) ) {
486  KeyCode = codes->value_alt;
487  } else if( dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED) ) {
488  KeyCode = codes->value_ctrl;
489  } else if( dwControlKeyState & SHIFT_PRESSED ) {
490  KeyCode = codes->value_shift;
491  } else {
492  if( uiAsciiChar==0 ) {
493  KeyCode = codes->value_normal;
494  } else {
495  KeyCode = uiNormalKey;
496  }
497  }
498  /* Add the found key code only when the following conditions are
499  * met:
500  * 1. KeyCode must be > 255 (enhanced)
501  * 2. The ASCII character provided must be different from the
502  * "normal" character - this test is required to allow
503  * AltGr+character combinations that are language-specific
504  * and therefore quite hard to detect ... */
505  AddKeyCode = (KeyCode > 255)
506  && ((uiAsciiChar==uiNormalKey) || (uiAsciiChar==uiNormalKeyOtherCase));
507  }
508 
509  if( !AddKeyCode && !bEnhancedKeysOnly) {
510  if( !is_ext_code ) {
511  /* The key code is simply the returned ASCII character */
512  KeyCode = uiAsciiChar;
513  AddKeyCode = TRUE;
514  }
515  }
516  }
517 
518  if( AddKeyCode ) {
519  if( KeyCode > 255 )
520  KeyCode = FB_MAKE_EXT_KEY((char) (KeyCode >> 8));
521  return KeyCode;
522  }
523  return -1;
524 }