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. .