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);
//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.