
                          Mouse & Keyboard Management

                         "The ass loaded with gold still eats thistle"
                                                     German proverb

Introduction

          The GOLDKEY unit contains all the mouse and keyboard
     management routines needed to support sophisticated DOS
     applications. The unit includes the following:

          - Functions which wait for user input by keyboard or mouse.
          - Support for mouse double-clicks, as well as keyboard mouse
            combinations such as Alt-Left Button.
          - Routines to hide, display, confine and move the mouse
            cursor.
          - Routines to get and set the state of the toggle keys, e.g.
            Caps Lock, Num Lock, etc.
          - Procedures to stuff keystrokes and mouse actions in the
            application's keyboard buffer.
          - Support for user-defined call back functions.
          - Routines to change the keyboard repeat rate.

What Did the User Press (or Click)?

          The primary purpose of GOLDKEY is to provide a flexible way to
     pause for user mouse or keyboard activity and make it easy to then
     find out what the user did! Gold uses three basic variables to
     define a single user input: the keystroke or mouse action, the
     mouse X coordinate and the mouse Y coordinate. These three
     variables are defined in the KeyVars record as LastKey, LastX and
     LastY. LastKey is a word-sized variable which is updated with a
     number to indicate the key pressed. For example, if Lastkey has a
     value of 27, the user pressed the Esc key, and if LastKey has a
     value of 520, the user must have clicked the left mouse button. The
     values in LastX and LastY indicate the location of the mouse cursor
     when the button was clicked. Refer to the table on page 5-3 for a
     complete list of the supported keystrokes and mouse clicks. TTT5
     did not support a visible (free floating) mouse and life was much
     simpler! In these good old days, keystrokes were defined in a
     byte-sized variable. In Gold, a single keystroke or mouse action is
     stored in a word-sized variable (KeyVars.LastKey). This change
     allows a much wider range of keystrokes to be reported, and makes
     Gold more flexible for international users.

Pausing for User Input

          At the heart of Gold's input management routines are the
     GetInput and GetInputRel procedures. The GetInput procedure pauses
     program execution and waits for the user to press a key combination
     or press down a mouse button. As soon as the user has pressed a key
     (combination) and pressed down a mouse button, the KeyVars.LastKey,
     KeyVars.LastX and KeyVars.LastY variables are updated with the
     appropriate values, and program execution continues. A important
     point to note is that GetInput will return as soon as a mouse
     button is pressed down. In other words, this function doesn't wait
     for the user to release the mouse button. By responding immediately
     to a mouse down, an application can implement "dragging support"
     where some task has to be performed before the button is released.
     Gold, for example, uses this procedure in the window dragging
     routines. However, if you don't need to support any custom dragging
     operations, call the GetInputRel procedure. This procedure is very
     similar to GetInput, except that the procedure only returns from a
     mouse click when the user has released the mouse button. With
     respect to the keyboard, GetInput and GetInputRel are identical.
     After calling GetInput or GetInputRel, the program should test the
     value of KeyVars.LastKey to see what the user did. If the value is
     mouse-related, the program should check the values of KeyVars.LastX
     and KeyVars.LastY to see where the mouse cursor was when the button
     was clicked.

          The following table details all the codes returned in
     KeyVars.LastKey:


Keyboard codes

     ͻ
      Key   Norm  Shft  Alt  Ctrl  Key   Norm  Shft  Alt  Ctrl 
     ͹
       a     97    65   286    1   F1     315   340  260   350 
       b     98    66   304    2   F2     316   341  361   351 
       c     99    67   302    3   F3     317   342  362   352 
       d     100   68   288    4   F4     318   343  363   353 
       e     101   69   274    5   F5     319   344  364   354 
       f     102   70   289    6   F6     320   345  365   355 
       g     103   71   290    7   F7     321   346  366   356 
       h     104   72   291    8   F8     322   347  367   357 
       i     105   73   279    9   F9     323   348  368   358 
       j     106   74   292   10   F10    324   349  369   359 
       k     107   75   293   11   F11    389   391  395   393 
       l     208   76   294   12   F12    390   392  396   394 
       m     109   77   306   13   Enter   13    13  284    10 
       n     110   78   305   14   BkSp     8     8  270   127 
       o     111   79   280   15   Up     328   428  408   397 
       p     112   80   281   16   Down   336   436  416   401 
       q     113   81   272   17   Left   331   431  411   371 
       r     114   82   275   18   Right  333   433  413   372 
       s     115   83   287   19   End    335   435  415   373 
       t     116   84   276   20   Home   327   427  407   375 
       u     117   85   278   21   PgUp   329   429  409   388 
       v     118   86   303   22   PgDn   337   437  417   374 
       w     119   87   273   23   Ins    338   261  418   260 
       x     120   88   301   24   Del    339   263  419   262 
       y     121   89   277   25   Tab      9   271  421   404 
       z     122   90   300   26   Esc     27    27  257    27 
       1 !   49    33   376        , <     44    60  307       
       2 @   50    64   377   259  . >     46    62  308       
       3 #   51    35   378        / ?     47    63  309       
       4 $   52    36   379        ; :     59    58  295       
       5 %   53    37   380        ' "     39    34  296       
       6 ^   54    94   381   30   [ {     91   123  282    27 
       7 &   55    38   382        ] }     93   125  283    29 
       8 *   56    42   383        \ |     92   124  299    28 
       9 (   57    40   384                                    
       0 )   48    41   385                                    
       - _   45    95   386   31                               
       = +   61    43   387                                    
     ͼ

Mouse Codes

     Mouse Action            Code   Mouse Action           Code

     LeftMouseDown            500   LeftMouseClick          520
     LeftMouseDown+Alt        501   LeftMouseClick+Alt      521
     LeftMouseDown+Ctrl       502   LeftMouseClick+Ctrl     522
     LeftMouseDown+Shift      503   LeftMouseClick+Shift    523
     RightMouseDown           504   RightMouseClick         524
     RightMouseDown+Alt       505   RightMouseClick+Alt     525
     RightMouseDown+Ctrl      506   RightMouseClick+Ctrl    526
     RightMouseDown+Shift     507   RightMouseClick+Shift   527
     CenterMouseDown          508   CenterMouseClick        528
     CenterMouseDown+Alt      509   CenterMouseClick+Alt    529
     CenterMouseDown+Ctrl     510   CenterMouseClick+Ctrl   530
     CenterMouseDown+Shift    511   CenterMouseClick+Shift  531

     LeftMouseDoubleClick     540   LeftDoubleClickonList   560
     RightMouseDoubleClick    544
     CenterMouseDoubleClick   548

Window Codes

     Window Action                       Code

     Close icon selected                  600
     Window moved or dragged              601
     Window zoomed or stretched           602
     Up arrow on V scrollbar selected     610
     Down arrow on V scrollbar selected   611
     Left arrow on H scrollbar selected   612
     Right arrow on H scrollbar selected  613
     Click on body of V scrollbar         614
     Clicked on body of H scrollbar       615

          Refer to Chapter 4 for a full explanation of the window codes,
     and when they are generated.

          Run the demo program DEMKY1.PAS to interactively see the key
     codes used by Gold.

A Basic Input Loop

          The following program fragment illustrates a basic input loop
     which pauses for user input and then calls an appropriate
     subroutine:

     Finished := false
     repeat
        GetInputRel;
        case KeyVars.LastKey of
           301,113,81: Finished := true;  {Alt-X,q,Q}
           315: HelpEm;  {F1}
           337: ScrollDown20;  {PgDn}
           329: ScrollUp20;    {PgUp}
           287: SaveIt;  {Alt-S}
           520: begin    {Left button click}
              if (KeyVars.LastX <= 10)
              and (KeyVars.LastY = HardVars.Depth) then
                 Finished := true;
           end;
        end;
     until Finished;

Ignoring the Mouse

          If you are not interested in mouse activity and want to wait
     for the user to hit a standard key or key combination, you can use
     the function GetKey which is defined as follows:

     GetKey:char;

          Waits for the user to press a key or key combination which has
     a value in the range 13 to 255. The function returns the character
     value of the input. Mouse activity is ignored. Behind the scenes,
     GetKey uses GetInput and so the KeyVars variables LastKey, LastX
     and LastY are updated.

Setting the Maximum Wait Time for Input

          Gold includes the procedure GetInputWait to allow programs to
     run with or without user input, and is defined as follows:

     GetInputWait(WaitTime:longint);

          Waits for user input via the mouse or keyboard. GetInputWait
     is very similar to GetInput, except that a maximum wait time is
     specified in milliseconds. The user input is recorded in the
     KeyVars variables LastKey, LastX and LastY. If WaitTime expires
     before user input is received, the procedure terminates and LastKey
     is set to zero. If a wait time of zero is specified, GetInputWait
     will not time out, i.e. it will wait indefinitely for user input.

Controlling and Managing the Mouse

          Gold makes extensive use of the mouse in everything from menus
     to input forms. As a developer, you gain all this power without
     having to write any mouse-specific code. However, if you want to
     write some custom routines which need to access the mouse, you can
     take advantage of the following mouse routines:

     MouseInstalled:boolean;

          Returns TRUE if a mouse and mouse driver are installed,
     otherwise FALSE is returned.

     MouseHardwareReset;

          Uses a hardware interrupt to reset the mouse to its standard
     defaults. In 99% of hardware setups, a software reset (discussed
     next) is faster and has the same effect.

     MouseSoftwareReset;

          Uses a software interrupt to set the mouse to its default
     configuration. This function is called automatically when a program
     (which uses GOLDKEY) is started.

     MouseShow(On:boolean);

          When passed TRUE, the mouse is made visible, otherwise the
     mouse is hidden.

     MouseMove(X,Y: integer);

          Moves the mouse to the specified (X,Y) coordinate. Note that
     local window or screen coordinates have no effect on this procedure
     -- coordinates are always treated as global.

     MouseConfine(X1,Y1,X2,Y2:integer);

          Defines a rectangular region of the screen where the mouse can
     move -- the user will not be able to move the mouse cursor beyond
     the specified region.

     MousePos(var X,Y: byte);

          Updates the passed parameters with the current location of the
     mouse cursor. Note that local window or screen coordinates have no
     effect on this procedure -- coordinates are always reported as
     globals.

     MouseReleased(Button: integer; var X,Y: byte): byte;

          Returns the number of times the specified button has been
     released since the function was last called. Button values of 0, 1
     and 2 represent the left, right and middle buttons, respectively.
     The variables X and Y are updated with the mouse cursor coordinates
     the last time the button was released.

     MousePressed(Button: integer; var X,Y: byte): byte;

          Returns the number of times the specified button has been
     pressed since the function was last called. Button values of 0, 1
     and 2 represent the left, right and middle buttons, respectively.
     The variables X and Y are updated with the mouse cursor coordinates
     the last time the button was pressed.

     MouseInZone(X1,Y1,X2,Y2: byte):boolean;

          Returns TRUE if the mouse cursor is situated within the
     specified global coordinates.

     MouseStatus(var L,C,R:boolean; var X,Y : byte);

          Updates the L, C and R boolean variables to TRUE if any of the
     left, center or right buttons are depressed, and updates the
     variables X and Y with the current mouse location.

     MouseStatusWin(var L,C,R: boolean; var X,Y: byte);

          Operates like MouseStatus, except that the X and Y coordinates
     are local to the window with focus.

     MouseStyle(OrdChar,Attr: byte);

          Sets the mouse cursor to the character represented by the
     ASCII value OrdChar. The second parameter controls the display
     attribute of the mouse cursor. If a zero is specified, Gold uses
     the standard mouse device driver technique, which changes color
     based on the underlying screen attribute to ensure maximum
     contrast.

     MouseRelease;

          Waits for all mouse buttons to be released.

     Run the demo program DEMKY2.PAS to see the mouse routines in
     action.

Mouse-Related Elements of KeyVars

          The following elements of the global record KeyVars affect the
     mouse functions:

MouseActive

          The variable KeyVars.MouseActive is set to TRUE (at program
     start-up) if a mouse is installed. Gold can be forced to ignore the
     mouse by setting this variable to FALSE.

DoubleDelay

          Gold has to differentiate between two single clicks of the
     mouse and one double click. To use an old gag, the difference is
     "timing". If two single mouse clicks occur within a specified
     (short) time period, a single click and a double click are
     returned. The variable KeyVars.DoubleDelay specifies, in
     milliseconds, the maximum elapsed time allowed between two mouse
     clicks if the second click is to be reported as a double click. The
     default is 350. This variable can be changed as desired.

RightHanded

          The de facto standard in the industry is for the left mouse
     button to be the "do it" or "enter" button, and this is Gold's
     default. By setting the variable KeyVars.RightHanded to TRUE, an
     application will behave as though the right mouse button is the
     do-it button. If your code is written with the left mouse codes as
     the standard, a simple change to this variable is all that's needed
     to give right button support.

InitScrollDelay, ScrollDelay

          When a user clicks on a scroll bar (in a window, list, form,
     etc.) there is a momentary delay before scrolling occurs. The
     variable KeyVars.InitScrollDelay sets the duration of the initial
     delay, in milliseconds, and the variable KeyVars.ScrollDelay
     controls the subsequent scrolling rate during scroll-bar
     operations. The defaults are 350 and 50 respectively. These
     variables can be changed as desired.

Accessing Ctrl, Alt and Shift

          GOLDKEY supports a wide variety of special key combinations
     like Alt-Backspace and Ctrl-PgUp. Sometimes, however, you may want
     to know if one of the shifting keys is being pressed on its own.
     GetInput and GetInputWait will not respond when Ctrl, Alt or either
     Shift key is pressed.

          GOLDKEY does, however, provide the following five functions to
     let you check the status of the special shifting keys:

     KeyShiftPressed: boolean;

          Returns TRUE if either Shift key is being held down.

     KeyRightShiftPressed: boolean;

          Returns TRUE if the right Shift key is being held down.

     KeyLeftShiftPressed: boolean;

          Returns TRUE if the left Shift key is being held down.

     KeyCtrlPressed:boolean;

          Returns TRUE if a Ctrl key is being held down.

     KeyAltPressed:boolean;

          Returns TRUE if an Alt key is being held down.

Accessing Num, Scroll and Caps Lock

          GOLDKEY provides the following routines for determining and
     changing the status of the Num Lock, Scroll Lock and Caps Lock
     keys:

     KeyGetNum:boolean;

          Returns TRUE if the Num Lock key is on.

     KeyGetScroll:boolean;

          Returns TRUE if the Scroll Lock key is on.

     KeyGetCaps:boolean;

          Returns TRUE if the Caps Lock key is on.

     KeySetNum(On:boolean);

          Pass TRUE to turn on the Num Lock key, or FALSE to turn it
     off.

     KeySetScroll(On:boolean);

          Pass TRUE to turn on the Scroll Lock key, or FALSE to turn it
     off.

     KeySetCaps(On:boolean);

          Pass TRUE to turn on the Caps Lock key, or FALSE to turn it
     off.

          Run the demo program DEMKY3.PAS to see how to access the
     shifting and locking keys.

Setting the Keyboard Repeat Rate

          PC keyboards support a typematic effect, i.e. holding a key
     pressed down has the same effect as pressing a key multiple times.
     By default, a key must be held down for about half a second before
     the repetition commences, and thereafter about ten characters per
     second are sent. Most users think this repeat rate is too slow
     (people west of Oklahoma excepted) and Gold provides a way of
     changing it.

          The following three procedures alter the typematic rate on
     systems with BIOS dated December 1985 or later:

     KeySetFast;

          Sets the typematic rate to 30 characters per second. This is
     fast enough even for a New Yorker.

     KeySetSlow;

          Sets the typematic rate to 5 characters per second. This is
     slow enough for the Post Office (just joking).

     KeySetRepeatRate(Delay,Rate:byte);

          This procedure allows you to explicitly set the typematic
     rate. The first parameter accepts a value between 1 and 4 and
     indicates the number of quarter seconds delay before the key
     commences repeating. The second parameter indicates the repeat rate
     and accepts a value between 0 and 31. The following table shows the
     repeat rate parameter along with the resultant repeat rate:

      Val Rate  Val Rate  Val  Rate  Val  Rate  Val  Rate

      0   30.0  7   16.0  14   8.6   21   4.6   28   2.5
      1   26.7  8   15.0  15   8.0   22   4.3   29   2.3
      2   24.0  9   13.3  16   7.5   23   4.0   30   2.1
      3   21.8  10  12.0  17   6.7   24   3.7   31   2.0
      4   20.0  11  10.9  18   6.0   25   3.3
      5   18.5  12  10.0  19   5.5   26   3.0
      6   17.1  13   9.2  20   5.0   27   2.7


Stuffing the Keyboard/Mouse Buffer

          GOLDKEY uses an internal buffer for storing keystrokes and
     mouse clicks. The sole purpose of this buffer is to enable you to
     force characters into the buffer using the KeyStuffBuffer...
     procedures. The GetInput and GetInputWait functions check to see if
     there any characters in the buffer before checking the physical
     mouse and keyboard.

          By default the size of the buffer is 30 characters, but this
     can easily be modified by changing the value of the
     GStuffBufferSize constant at the top of the GOLDKEY.PAS file.

     Listed below is a description of the buffer-related procedures:

     KeyStuffBuffer(W:word);

          Adds a keystroke to the keyboard buffer. The parameter W is
     the word value of the keystroke as detailed in the table starting
     on page 6.3.

     KeyStuffBufferMouse(W:word;X,Y:byte);

          Use this function to add a mouse click to the keyboard buffer.
     The W parameter identifies the mouse action and should be in the
     range 520 to 560. X and Y identify the location of the mouse cursor
     corresponding with the action.

     KeyStuffBufferStr(Str:string);

          Stuffs characters (with ASCII values in the range 0 to 255)
     into the keyboard buffer. This procedure is provided as a
     convenience to save making multiple calls to KeyStuffBuffer (one
     call for every character in the string).

     KeyBufferSpace:word;

          Returns the amount of free space available in the keyboard
     buffer. If the buffer is full, any attempt to stuff additional
     keystrokes will be ignored.

     KeyFlushBuffer;

          Erases all the entries in the application's keyboard buffer.

     KeyFlushDOSBuffer;

          Erases all the entries in the DOS keyboard buffer.

          The keyboard buffer routines form the framework of a simple
     but elegant macro facility. The demo file DEMKY5.PAS implements a
     macro system and is discussed further in the (later) section Using
     Keyboard Hooks.

Goodbye Keypress, Hello GkeyPressed

          The standard Borland Pascal function KeyPressed returns TRUE
     if there is a character waiting in DOS's keyboard buffer. In a Gold
     application, you should use GKeyPressed (for Gold KeyPressed)
     instead. GKeyPressed checks the internal application keyboard
     buffer as well as the DOS buffer. To parody Nike, "Just use it".

          If you want to check and see whether a key has been pressed,
     or whether a mouse button is currently pressed, call the function
     KeyorMousePressed. It functions like GkeyPressed but additionally
     checks the state of the mouse buttons.

Using Keyboard Hooks

          Hooks provide a way for a developer to integrate custom
     routines into the heart of Gold. GOLDKEY supports two primary types
     of keyboard hooks. Gold can call a procedure while the program is
     idle and waiting for the user to press a key; this type of hook is
     known as an idle hook (or a Post Office hook). Similarly, you can
     assign a procedure which will be called every time a character is
     pressed or the mouse is clicked; this type of hook is known as a
     pressed hook.

Using an Idle Hook

          An idle hook is an external procedure which is called
     repeatedly while a program is waiting for the user to input data. A
     good use for an idle hook is to display a clock, or to show the
     status of the shifting keys.

          All you have to do is create a procedure following some
     specific rules, and then call the procedure AssignIdleHook to
     instruct Gold to call the procedure during idle time.

          For a procedure to be eligible as an idle hook it must adhere
     to the following rules:

          The procedure must be declared as a far procedure at the root
          level. Refer to section Understanding Hooks in Chapter 3 for
          further information.

          The procedure must be declared with no (nada/zero/none) passed
          parameters.

     The following procedure declaration follows these rules:

     {$F+}
     procedure MyIdleHook;
     begin
        {some code}
     end; {MyIdleHook}
     {$F-}

          The following procedure is then called to instruct Gold to
     call your procedure while the application is waiting for input:

     AssignIdleHook(Hook:KeyIdleHook);

          Instructs Gold to call the specified procedure while pausing
     for user input.

          For example, the above procedure would be assigned with the
     following statement:

     AssignIdleHook(MyIdleHook);

          The hooked procedure will be continually called while your
     program is waiting for user input, so make sure that the procedure
     is compact and doesn't consume too many clock cycles. If your
     procedure involves too many tasks, the program will slow down
     considerably during input. If you want to call an extended task,
     like a background print program, you must continually checked the
     GkeyPressed function and suspend your procedure when it returns
     TRUE.

          If subsequently, you want to remove the idle hook, execute the
     following:

     AssignIdleHook(NoKeyIdleHook);

          The demo file DEMKY4.PAS illustrates the use of an idle hook
     to display a ticking clock.

Using a Pressed Hook

          A pressed hook is a procedure which is called every time a key
     is pressed or a mouse button is clicked. The hooked procedure is
     called before the key is processed, i.e. before the character is
     returned by GetInput or GetInputWait. The hook is particularly
     useful for trapping special keys like F1 for help, or for building
     keyboard macros.

          All you have to do is create a procedure following some
     specific rules, and then call the AssignPressedHook procedure to
     instruct Gold to call your procedure every time a key is pressed.

          For a procedure to be eligible as a pressed hook it must
     adhere to the following rules:

          The procedure must be declared as a far procedure at the root
          level. Refer to the section Understanding Hooks in Chapter 3
          for further information.

          The procedure must be declared with one variable parameter of
          type word, and two variable parameters of type byte. These
          parameters identify the user input, i.e. the Key, X and Y.


     The following procedure declaration follows these rules:

     {$F+}
     procedure MyPressedHook(var Code:word;var X,Y:byte);
     begin
        {some code}
     end; {MyPressedHook}
     {$F-}

          The following procedure is then called to instruct Gold to
     call your procedure after each input:

     AssignPressedHook(Hook:KeyPressedHook);

          Instructs Gold to call the specified procedure before
     processing each user input.

          If, subsequently, you want to remove the pressed hook, execute
     the following:

     AssignPressedHook(NoKeyPressedHook);

          The demo file DEMKY5.PAS shows how a pressed hook can be used
     to provide macro keyboard support.

Other Neat Stuff

          Here are a few more procedures included in GOLDKEY (we
     couldn't decide where else to put them):

     ExtendedKeyBoard:boolean;

          Returns TRUE if the host system has an extended keyboard, i.e.
     has 101 or more keys.

     KeySetClicking(Clicking : boolean);

          Call this procedure with a TRUE parameter if you want Gold to
     make a tactile clicking noise every time a key is pressed, or pass
     FALSE to turn off clicking.

     WordToChar(W:word):char;

          Gold uses a word-sized variable KeyVars.Lastkey to record user
     input. Use this function translate the word-type keystroke to a
     character. If the passed parameter is greater than 255, a #0 is
     returned.

