Keyboard Input

Basics

   Using FB's built-in functionality, there are four ways of getting 
   keyboard input:

      * Inkey() returns a string containing an ASCII char corresponding to 
        the key pressed by the user, or a 2-byte FB extended keycode for 
        some special keys, such as the Arrow keys or Page Up/Down. It works 
        pretty much like it did in QB.

            * Getkey() returns the same information as inkey(), but in 
              form of an integer instead of a string. inkey() and getkey() 
              belong together: They use the same code and they are located 
              in the same modules.

            * Multikey() takes an FB scancode (SC_*) and checks whether 
              that key is pressed at this moment.

            * Screenevent() returns key presses in form of EVENT_KEY_PRESS 
              events (and others for key release or repeat). It returns the 
              FB scancode in the Event.Scancode Field, and the ASCII char 
              value or 0 in the EVENT.ascii field. EVENT.ascii does not use 
              FB extended keycodes; the EVENT.scancode field can be checked 
              instead in order to handle extended keys.

   "scancode" refers to the SC_* #defines which are more or less matching 
   the DOS keyboard scancodes. The values are not made up, they themselves 
   correspond to certain ASCII chars, for example: SC_HOME = asc( "G" ) = 
   &h47. They're also the same values that you get under DOS/DJGPP or from 
   the Linux kernel as part of extended key code sequences. Besides their 
   use in multikey() or screenevent(), scancodes are used in various places 
   internally, for example when translating between different kinds of key 
   codes, as an easy-to-use and portable representation of keycodes.

   "key" refers to an ASCII char, or a 2-byte extended keycode string for 
   other keys as returned by inkey(). The rtlib has several KEY_* #defines 
   for the available 2-byte extended keycodes, in form of integers. These 
   are used internally and also match the values returned by getkey().

   FB's 2-byte extended keycodes consist of a &hFF byte followed by a byte 
   containing the SC_* scancode value corresponding to the keypress. 
   Checking for SC_HOME returned by inkey() could look like:
      if( inkey( ) = chr( 255 ) + "G" ) then ...
   Checking for SC_HOME returned by getkey():
      if( getkey() = &h47FF ) then ...
      if( getkey() = ((SC_HOME shl 8) or &hFF) ) then ...

   inkey(), getkey() and multikey() use wrapper functions that call ...
      * the console-mode versions fb_ConsoleInkey(), fb_ConsoleGetkey(), 
        fb_ConsoleMultikey() by default,
      * or the gfxlib versions fb_GfxInkey(), fb_GfxGetkey(), 
        fb_GfxMultikey() if a graphics SCREEN is active,
   by using function pointer hooks.

rtlib

   The rtlib has separate console-mode implementations of the above 
   functions, for each platform:

   * DOS
      fb_ConsoleInkey() and fb_ConsoleGetkey() use DJGPP's getch() function 
      to retrieve input characters anytime they're called. getch() returns 
      ASCII chars, but also 2-byte sequences for special keys, which are 
      easy to handle because they match the SC_* scancodes.

      fb_ConsoleMultikey() installs an interrupt handler that uses port I/O 
      to read keyboard information and updates a key state table which is 
      checked by multikey().

   * Win32
      fb_ConsoleInkey() and fb_ConsoleGetkey() (indirectly) use the Win32 
      API functions PeekConsoleInput() and ReadConsoleInput() to get queued 
      key press/release events whenever needed. All currently pending 
      events are handled during a call, and after very complex internal 
      translation involving MapVirtualKey(), the keys are put into a 
      buffer, from where fb_ConsoleInkey() and fb_ConsoleGetkey() read the 
      keys they return.

      SetConsoleCtrlHandler() is used to listen for console close/system 
      shutdown events to provide SC_CLOSE events for console-mode (the 
      win32 port of the rtlib might be the only one going this far).

      fb_ConsoleMultikey() uses a FindWindow()/GetForegroundWindow() hack 
      to determine whether the console window is focused, and if yes, 
      simply uses GetAsyncKeyState().

   * Linux, *BSD
      The Unix port of the rtlib runs a console keyboard handler (and a 
      console mouse handler) in a background thread, in order to provide 
      input for multikey() (and getmouse()).

      fb_ConsoleInkey() and fb_ConsoleGetkey() read input bytes through the 
      __fb_con.keyboard_getch() hook. By default, __fb_con.keyboard_getch() 
      points to a simple function that just uses fgetc() on /dev/tty 
      (indirectly; the Unix rtlib initialization code opens the handle, and 
      changes I/O settings etc., not only for the purpose of keyboard 
      input, but mostly).

      The terminal returns ASCII chars for simple key presses, and special 
      escape sequences for extended keys. On the first call, various 
      termcap lookups (via tgetstr()) are done to determine these 
      terminal-specific escape sequences for certain key press events, and 
      they are put into a lookup tree to allow easy & fast translation to 
      the corresponding FB extended keycodes. By doing the termcap query 
      the Unix rtlib can support all the different terminals (e.g. xterm 
      vs. linux) quite well, although there still are some keys not working 
      here and there.

      Only one "event" (ASCII char or escape sequence) is read at a time, 
      the resulting key is added to a key buffer, from where 
      fb_ConsoleInkey() and fb_ConsoleGetkey() can read it.

      fb_ConsoleMultikey() is currently implemented for the Linux port 
      only, not under *BSD though. In console-input mode (used under 
      'console'/'linux' terminals), it dup()licates the rtlib's /dev/tty 
      handle, and switches it over into medium raw mode. Then it overrides 
      the background thread's __fb_con.keyboard_handler() hook to a 
      function that read()s kernel key codes from the duplicated /dev/tty 
      handle.

      Called from the background thread, it reads a fixed amount of input 
      at once, whenever it arrives. After somewhat complex translation, a 
      key state table is updated to reflect the state of pressed/released 
      keys, to be checked by fb_ConsoleMultikey() at any time, and the keys 
      are added to a key buffer from where an overridden 
      __fb_con.keyboard_getch() reads them, whenever called by 
      fb_ConsoleInkey() or fb_ConsoleGetkey() [why is this done?]. 
      Furthermore, the keys are sent to the Linux fbdev gfxlib2 driver, if 
      it's active.

      In X11 mode (used under 'xterm' terminal), fb_ConsoleMultikey() sets 
      the background thread's __fb_con.keyboard_handler() to a function 
      that checks whether the xterm has input focus (XGetInputFocus()) and 
      if yes, simply uses XQueryKeymap() to update the key state table for 
      fb_ConsoleMultikey().

gfxlib2

   In the gfxlib, fb_GfxInkey() and fb_GfxGetkey() use one key buffer (same 
   code on all platforms), to which the different/platform-specific gfx 
   drivers post keys to. Similar to that, there is a single key state table 
   for fb_GfxMultikey(), and it is also updated by the gfx drivers. Whether 
   or not the gfx drivers actually do post keys or update key states is up 
   to them though.

   * DOS
      The DOS gfxlib2 port (for all DOS gfx drivers) sets a hook/callback 
      that's called by the same keyboard interrupt handler used by the DOS 
      fb_ConsoleMultikey().

   * Win32 driver
      The gfx window thread listens to WM_KEYDOWN, WM_CHAR and WM_CLOSE, 
      translates the keys, and then updates the key state table, posts them 
      to the fb_GfxInkey()/fb_GfxGetkey() buffer, and fills in & posts the 
      corresponding EVENT for screenevent().

   * X11 driver
      The gfx window thread listens to KeyPress and other XEvent's, 
      translates the keys, then posts them etc., just like the Win32 
      driver.

   * Linux fbdev driver
      As mentioned above, the fbdev driver gets its input from the same 
      keyboard handler code that's used by the Linux fb_ConsoleMultikey().

