XKB:

The X Keyboard Extension provides capabilities for the keyboard and the
mouse that were lacking or cumbersome in the core implementation. The
extension is designed to replace the core functionality entirely, but it
provides backwards compatability with old clients programmed before the
advent of the extension. While the extension is running, it is important
to distinguish three types of clients running on the server. There are
"xkb aware" clients that use the xkb specific library routines in the
application. There are "xkb capable" clients that do not use special
routines but were linked with the extension library on compilation.
Finally, there are "xkb unaware" clients that neither call the routines
nor were linked with any special libraries.

For a full explaination of the XKB extension can be found in the
documentation menu. This is documented in a 200+ page postscript file.


   AccessX:

The X Keyboard Extension includes a set of routines that activate
handicapped accessibility features on the X Server. These features can
be used by clients that are either "xkb-aware" or "xkb-capable".
"xkb-unaware" clients cannot take advantage of these special features.
AccessX features that were used in our project include sticky keys,
mouse keys, slow keys, bounce keys, repeat keys, and an idle timeout
feature.


     Programming with AccessX of the X Keyboard Extension:

Activating AccessX features in the X Keyboard Extension is a simple
process of several steps:

  1. * Open a connection to the X Server calling the X Keyboard
     Extension's special connection routine, XkbOpenDisplay.* The call
     takes 6 parameters and returns a Display object. The first
     parameter is a string denoting the hardware display name. The next
     two are pointers to integers that will be filled with information
     by the server denoting event and error codes. The next two are
     pointers to integers that denote the compile time library major
     and minor versions. These are constants defined in XKBlib.h:
     XkbMajorVersion and XkbMinorVersion. The pointers are backfilled
     by the server with server version information. The final parameter
     is a pointer to an integer backfilled by the server containing the
     status code of the operation.
  2. * Get a keyboard description object from the server by calling the
     X Keyboard Extension's special routine, XkbGetMap.* The call takes
     3 parameters and returns a pointer to a* XkbDescRec* structure.
     The first parameter is a pointer to the *Display* object retrieved
     in step one. The second is a *bitmask* describing what information
     to return. The final parameter is an *integer* describing which
     device to fetch information on the keyboard. The constant
     *XkbUseCoreKbd* can be used to denote the default keyboard.
  3. Fill the keyboard description object with information on the
     boolean controls. Call XKB's special routine XkbGetControls. It
     takes three parameters and returns a status code. The first
     parameter is a pointer to the *Display object* retrieved in step
     one. The second parameter is a *mask* denoting which controls to
     get information on. The final parameter is the *keyboard
     description object* retrieved in step two.
  4. *Modify the XkbControlsRec structure to change the settings to
     what you desire.* The XkbControlsRec structure is a pointer in the
     XkbDescRec structure call ctrls. So... if you called your keyboard
     description object xkb, and it was a pointer, you can access the
     XkbControlsRec structure via: xkb->ctrls. The XkbControlsRec
     structure contains all the fields necessary to activate and change
     the settings of the various AccessX features. These fields are
     described in detail in the X keyboard extension documentation. The
     ones used in the project are described below:
         * *enabled_ctrls* - this field is a bitmask denoting which
           boolean controls have been turned on. This is where sticky
           keys, mouse keys, bounce keys, slow keys, repeat keys, and
           the idle timeout feature are all turned on. There are
           constants in XKBlib.h that define the bitmasks for each of
           these features. enabled_ctrls must have a bit for each
           feature that is to be turned on.
         * *repeat_delay* - this field is the initial delay in
           milliseconds before the first repeated key is recognized by
           the server for repeat keys.
         * *repeat_interval* - this field is the time interval in
           milliseconds between repeated key events for repeat keys.
         * *slow_keys_delay* - this field is the time delay in
           milliseconds that a key must be held before an event is
           triggered for slow keys.
         * *debounce_delay* - this field is the time delay in
           milliseconds that must be waited between successive
           identical key events for bounce keys.
         * *mk_delay* - this field is the time in milliseconds between
           the initial key press and the first repeated motion event
           for mouse key acceleration.
         * *mk_interval* - this field is the time in milliseconds
           between repeated motion events for mouse key acceleration.
         * *mk_time_to_max* - this field is the number of key events
           before the pointer reaches a maximum speed for mouse key
           acceleration
         * *mk_max_speed* - this field is the maximum speed in pixels
           per key event the pointer can reach for mouse key acceleration
         * *mk_curve* - this field is the slope of the acceleration
           curve for mouse key acceleration.
         * *ax_options* - this field is a bitmask that denotes which
           features of sticky keys are currently activated. The two
           features available are latch-to-lock and two-key-disable.
           latch-to-lock allows for a qualifier key to be pressed twice
           to lock it. two-key-disable allows for two qualifier keys to
           be pressed simultaneously to turn off sticky keys.
         * *ax_timeout* - this field is the the time in seconds that a
           keyboard must be idle before all of the AccessX features are
           turned off.
  5. Notify the server that changes have been made by calling XKB's
     special routine XkbSetControls. The routine takes three parameters
     and returns a status code. The first parameter is a pointer to the
     display retrieved in step one. The second is a bitmask of the
     controls to turn on. This bitmask should be bitwise "or'd" to the
     mask XkbControlsEnabledMask to denote that the controls are on.
     The final parameter is a pointer to the keyboard description
     object obtained in step two.
  6. Flush the request to the server. Even though a request has been
     made, it still may be lying in a buffer on the client side. To
     make sure it got sent over, call any routine that requires
     information from the server. This will force the request buffer
     sent to the server. An example routine to do this would be
     XkbGetControls.
  7. Free up the memory for the keyboard description object. Call XKB's
     special routine XkbFreeKeyboard. It takes three parameters. The
     first is a pointer to the keyboard description object. The second
     is mask denoting which information resides in the object. The
     final parameter is a boolean that, if true, frees all non-null
     components of the object and the object itself.

Below is some sample source code that turns sticky keys on at the
command prompt. It demonstrates the steps described above:

//
//  This program activates sticky keys in the X keyboard extension.
//  It can be called with or without command line arguments.  The
//  arguments specify whether or not sticky keys are turned on (-y, -n),
//  whether modifier keys pressed twice lock (-yl), whether two
//  simultaneously pressed modifier keys disable sticky keys (-yt),
//  or whether both features are active (-ylt, -ytl).
//  Compiled with -- g++ -L/usr/X11R6/lib -lXext sticky.C
//
//  Oct 16 1998 -- Written by Aaron Klish
//
//  Modified Nov 12th by Aaron Klish -- changed program structure.
//
//////////////////////////////////////////////////////////////////////


#include
#include
#include
#include

void main(int argc, char *argv[]) {

 //mask of features to turn off.
 unsigned int options_off_mask = 0;
 //mask of features to turn on.
 unsigned int options_on_mask = 0;
 //whether sticky keys are on or off.
 Bool on = false;

 //parse the command line arguments...
 if (argc < 2 || strcmp(argv[1],"-y")==0)
   {
     on = true;
     options_off_mask = (XkbAX_TwoKeysMask | XkbAX_LatchToLockMask);
   }
 else if (strcmp(argv[1],"-yt")==0)
   {
     on = true;
     options_off_mask = XkbAX_LatchToLockMask;
     options_on_mask = XkbAX_TwoKeysMask;
   }
 else if (strcmp(argv[1], "-yl")==0)
   {
     on = true;
     options_off_mask = XkbAX_TwoKeysMask;
     options_on_mask = XkbAX_LatchToLockMask;
   }
 else if (strcmp(argv[1], "-ytl")==0 || strcmp(argv[1], "-ylt")==0)
   {
     on = true;
     options_on_mask = (XkbAX_TwoKeysMask | XkbAX_LatchToLockMask);
   }
 else if (strcmp(argv[1], "-n")==0)
   {
     options_off_mask = (XkbAX_TwoKeysMask | XkbAX_LatchToLockMask);
   }
 else
   {
     cerr << "USAGE: sticky [-n,-y,-yt,-yl,-ytl]" << endl;
     exit(0);
   }


 //compile time lib major version in, server major version out
 int major_in_out = XkbMajorVersion;

 //compile time lib minor version in, server minor version out
 int minor_in_out = XkbMinorVersion;

 //backfilled with the extension base event code
 int event_rtrn = 0;

 //backfilled with the extension base error code
 int error_rtrn = 0;

 //backfilled with a status code
 int reason_rtrn = 0;

 //connection to XServer
 Display *display;

 //Open a connection with an X server, check for a compatible version of the XKB extension
 //in both the library and the server, and initialize the extension for use.
 display = XkbOpenDisplay(NULL,&event_rtrn,&error_rtrn,&major_in_out,&minor_in_out,&reason_rtrn);
 if (reason_rtrn == XkbOD_BadLibraryVersion)
   {
     cerr << "ERROR: library and server have incompatible extension versions." << endl;
     exit(0);
   }
 else if (reason_rtrn == XkbOD_ConnectionRefused)
   {
     cerr << "ERROR: could not establish a connection with the X server." << endl;
     exit(0);
   }
 else if (reason_rtrn == XkbOD_BadServerVersion)
   {
     cerr << "ERROR: library and server have incompatible extension versions." << endl;
     exit(0);
   }
 else if (reason_rtrn == XkbOD_NonXkbServer)
   {
     cerr << "ERROR: XKB extension not present in the X server." << endl;
   exit(0);
   }
 else {

   //Get the state of the keyboard.
   XkbDescPtr xkb = XkbGetMap(display, 0, XkbUseCoreKbd);
   if ((int)xkb == BadAlloc || xkb == NULL)
     {
       cerr << "ERROR: Unable to allocate storage." << endl;
       exit(0);
     }


   //Get the current state of the keyboard controls.
    if (Status S = XkbGetControls(display,XkbAllControlsMask,xkb) != Success)
     {
       if (S == BadAlloc)
         cerr << "ERROR: Unable to allocate storage." << endl;
       else if (S == BadMatch)
         cerr << "ERROR: Unable to query state of keyboard." << endl;
       else cerr << "ERROR: Unable to get state of keyboard controls." << endl;
       exit(0);
     }

   //If the controls are valid, modify the sticky keys control options governed
   //by the masks determined above.
   if (xkb->ctrls)
     xkb->ctrls->ax_options = ((xkb->ctrls->ax_options | options_on_mask) & ~options_off_mask);
   else
     {
       cerr << "ERROR: Unable to get state of keyboard controls." << endl;
       exit(0);
     }

   //Turn stickykeys on or off...
   unsigned int enabled = 0;
   enabled |= (on ? XkbStickyKeysMask : 0);

   xkb->ctrls->enabled_ctrls &= ~XkbStickyKeysMask;
   xkb->ctrls->enabled_ctrls |= (XkbStickyKeysMask & enabled);


   //Set the modified sticky key controls at the X server.
   if (XkbSetControls(display,enabled | XkbControlsEnabledMask,xkb) != Success)
     {
       cerr << "ERROR: Unable to set state of keyboard controls." << endl;
       exit(0);
     }

   //Flush the Xlib request buffer.
   XkbGetControls(display,XkbAllControlsMask,xkb);

   //Free the local copy of the keyboard state
   XkbFreeKeyboard(xkb,XkbAllControlsMask,True);
 }
}


   Bugs found during implementation:


     Slow Keys Delay:

When slow keys is activated, setting the delay to some value over 200 ms
locks up the keyboard. Pressing and holding down keys results in beeps
from the computer speaker. I believe the bug is in the X keyboard
extension. To repeat the error, get a pointer to an XkbControlsRec
structure. Turn slow keys on by setting the appropriate bitmask in
enabled_ctrls field. Set the delay value via the slow_keys_delay field
to a value larger than 200ms.


     Mouse Keys Acceleration:

When mouse keys acceleration is turned on, either nothing happens or the
mouse pointer seems to jump a few pixels in the direction of motion. The
pointer then returns to the original unaccelerated speed. When both slow
keys and mouse keys are activated, the keyboard repeatedly beeps, but
acceleration oddly seems to work as it should. A wierd observation is
that setting different values for slow keys delay will result in
different accelerations for mouse keys. Again I believe the bug is in
the X keyboard extension. To repeat the error, get a pointer to an
XkbControlsRec structure. Turn mouse keys and mouse keys acceleration on
by setting the appropriate bitmask in the enabled_ctrls field. Set the
five parameters that govern mouse key acceleration in the XkbControlsRec
structure.