XHKEYS - a tool for your special keyboard.


User Manual

Version 2.2.0  July 2004


Content



Introduction Installation Using xhkeys Using xhkconf Appendix A.  Configuration file format Appendix B.  Built in operations Appendix C. Plugins

Introduction

Preamble (original)

While I was building my new computer,  I purchased  " Turbo Media keyboard " (KB9810R):  this one was really cheap, and a lot of extra keys looked to be useful.  It came with a software for a dumb "operating system", but  given that I use Linux for most of the time,  those keys remained unused ... until recently.

xhkeys is designed to suit any keyboard  that has some extra keys that otherwise make no use with X:   "Menu" key  on a  105 key keyboard,  extra keys on some keyboard models, or laptops.

The application allows to assign a particular action to any key or key combination (key and modifiers ).  An action can be of one of the following types:

The package includes two programs:
xhkeys   - the main application which processes key presses and implements actions
xhkconf - configuration for xhkeys

A foreword to xhkeys-2.

At last I found time for a complete revision of xhkeys, as you can figure out by checking dates of source files.  I believe, the code is  much neater, and if you still find it hardly readable, I bring my apologies. After all,  efficiency and compactness were always my major criteria :).

Thanks a lot to all comments and bug reports, which I somehow managed to filter out of enormous junk mail. It could probably be impossible without yahoo Bulk mail filter,  thanks to Yahoo developers for their great work.

Multimedia keys (BTW, is there a better name ?) have appeared even at MS keyboard,  which (naturally :)) is going  to provide a de-facto standard for keyboard developers. It isn't so bad after all - now I could attach a configuration file to suit most of recent keyboards, and even install it as a global resource with  xhkeys installation, making xhkeys completely transparent even for novice Linux users ... if I only had a MS keyboard. Unfortunately I still use Turbo-Media keyboard, in expectation that it will break one day, but it doesn't :).  If you use a MS keyboard and believe that your experience in using it might help others, please send me a copy of your configuration.

Now to a probably more relevant stuff ...

Here are the major new features of xhkeys-2:
For recent changes refer to  CHANGES  file. 

WARNING.  If you care about readability of your resource database file ~/.XHkeys is is strongly recommended to add the declaration of standard plugins manually before running xhkeys 2.0.0.  See Plugin Declarations for the details.


Terms explained (the way I understand it, which might be not 100% correct).

ScanCode vs  KeySym

Scan code is a unique code generated  by the keyboard for a particular key.  As opposed to that,  Key Symbol (KeySym for short) is assigned by the X-server(1) ,  depending on the selected layout. KeySyms have  mnemonics starting with XK_,  e.g.  XK_A for capital A,  XK_percent for  %  sign. All KeySyms are listed in  /usr/X11R6/include/X11/keysymdef.h .

While using KeySyms is preferred for portability, this application identifies a key by its scan code,  because:

X reports any scan code as an  8-bit number,  with external keys having  codes in range 128 to 255 (2)
This application however is not limited to range 128-255.

----
(1)  More exactly by KEYBOARD extension, unless disabled.
(2)  In reality all scan codes are below 128, what X reports as  a  key code over 128  is really an e0-sequence, e.g.  '0xc2' corresponds to  '0xe0 0x42'

Modifier and shift state

A modifier is a key  that can be pressed simultaneously with a function key to affect its interpretation. At least 6 modifiers are supported by X on PC:  Shift (1), Caps Lock (2),  Ctrl(4), Alt(8),  Win(0x40), ScrollLock(0x80).

Shift state is a bit mask  corresponding  to all pressed modifiers.  With  6 modifiers you have 64 possible shift states, therefore the  amount of possible key combinations (ExtraKey1,  Shift + ExtraKey1 ,  Ctrl+Shift+ExtraKey1, etc.) is 64 times more than a number of extra keys.

Support

The recent version of xhkeys is available from  http://wmalms.tripod.com  or   http://www.geocities.com/wmalms

The author can be contacted by email:  "Michael Glickman" <wmalms@yahoo.com>
 

Installation

From source tarball

    tar -xzf xhkeys-2.0.0.tar.gz
    cd xhkeys-2.0.0
    ./configure
    make

To install for common use (binaries and documentation), log in as root and enter
        make install
        make install_doc

This will install the binaries in ${prefix}/bin and the manual in ${prefix}/share/doc/xhkeys-1.0.0

Useful configuration options (with ./configure):
 


--prefix=pathinstallation prefix, default  /usr/local
--disable-pluginsdisable support of plugins in xhkeys code
--disable-cdaudioeven if plugins are enabled, exclude  compilation and installation of
CD audio plugin  xhkeys_cdaudio
--disable-mixereven if plugins are enabled, exclude  compilation and installation of
audio mixer plugin  xhkeys_mixer
--disable-osd disable support of on-screen display (OSD) in xhkeys code
--disable-shape disable using SHAPE extension with OSD, so that the background can be always 100% opaque
--disable-syslogexclude syslog support: all messages addressed to syslog will be printed on stout instead

Among most commonly used environment variables are:


CC name of C compiler 
CFLAGScompilation flags

From RPM binary

The rpm binary was built with gcc 2.96,  glibc 2.2.5,  rpm 4.0.2 with CFLAGS="-O4 -march=pentiumpro". If you have problems, try installing from source.

Log in as root and say

rpm -Uv xhkeys-2.0.0-1.i386.rpm

From RPM source

Log in as root and  set  CC and CFLAGS, if necessary. Enter
rpm --rebuild --clean xhkeys-2.0.0-1.src.rpm
This creates the binary  xhkeys-2.0.0-1.i386.rpm usually in /usr/src/redhat/SRPMS. Cchange to this directory and follow the instructions for installation from RPM binary .
 

Using xhkeys

Configuration

xhkeys needs a configuration file.  Its structure is discussed in Appendix A .

 By default xhkeys looks for configuration file in the following  paths:

Global configuration:   /usr/lib/X11/app-defaults/XHkeys
User configuration:      ~/.XHkeys

User settings overwrite the global ones. The location of user configuration file can be changed with --file command line argument.

The package comes with a configuration utility xhkconf ,  that can help you to create and maintain the configuration file. Sorry, I still don't have time (and patience) to implement a nice GUI interface (you will do me a favor by building it), but I will always find time to help you in case you have problems.  xhkconf can configure key bindings,  but not other parameters like device names, time-outs, etc. What really frustrates me about 'xhkconf --edit ' is that it uses a standard Xrm (X Resource Manager) engine, ended up with XrmPutFileDatabase, which doesn't keep the comments, and completely reshuffles the lines, which makes a resource file hardly readable.

If you want to have a nice looking configuration file, you can still find xhkconf useful. The package includes a sample resource file XHkeys.sample ,  which has to be copied to ~/.XHkeys    [i.e. .XHkeys (the name starts with a dot)  in your home directory].   Since the scan codes for extra keys are keyboard model specific, you might need to change the scan codes and maybe shift state in ~/.XHkeys. Use ' xhkconf -k' to see the scan codes and ' xhkconf -m' to reveal the window information needed for configurations.

 

Activation

To activate xhkeys automatically with X server, you can just insert  'xhkeys'  line somewhere near the start of ~/.xinitrc (don't put it at the end, because in some cases the control might not get to the end of ~/.xinitrc).

As an alternative, some Window Managers provide their own way for application autostart, like ~/GNUstep/Library/WindowMaker/autostart for Window Maker, or Autostart directory for kwm .

FYI:  xhkeys fails if X server is inactive, therefore you can't activate it before calling X.
 

Command line arguments

The command line keywords for xhkeys can be used in short or a long format

The  following command line arguments are accepted:

-c
--console
Always log to stdout (suppresses resource settings). Use it if you want to see the feedback but are denied  access to syslog.

-d
--debug
Debug mode: don't fork and,  by default (unless -s is specified) use stdout instead of syslog .

-f name
--file=name
Location of user configuration file, if different from default ~/.XHkeys

-h
--help
Print help and exit

-l level
--log=level
The level of logging
    n- don't  log  messages
    e - log only error messages (default)
    a - log all messages:  start, stop/termination and errors

-o
--noosd
Disable OSD (suppresses resource settings)

-O[display name]
--osd[=display name]
Enable OSD (even if disabled in resource settings). Optionally you can specify display name as Server:ScreenNo (same as DISPLAY environment variable). If display name is used with the short form, it must immediately follow -O, eg   -OotherServer:0, while with the long form =  is used before  display name:  --osd=otherServer:0

-s
--syslog

Enable using syslog (suppresses resource settings).

Using xhkconf

A command line argument can either specify a mode (e.g. edit,  configure plugins, view scan codes) or a parameter for particular modes.
 

Parameters

xhkconf accepts the following parameters:

-f  name
--file  name
Location of user configuration file, if different from default ~/.XHkeys.  Used with  --edit,  --delete, and  --list.

-c  value
--count  value
Number of iterations for --keys, --mouse, --delete and --list

-s  code[.shift]
--scan  code[.shift]
Scan code and optionally shift state separated from the scan code by a dot. The scan code can be specified as a decimal, or hexadecimal number. Shift state 0 is assumed if omitted. Used with  --list, --edit and --delete.

-g
--global
Lookup also global configuration file. Used with --list only.

-a
--all
List all assignments. Used with --list only.
 

N.B.  --count, --scan  and  --all are mutually exclusive.

For details refer to the description of a particular mode
 

Modes

One and only one of the following modes may appear in the command line

-k
--keys

This mode displays key information. After 'xhkconf -k' starts it waits for 10 seconds (the time-out can be changed using  keytimeout parameter  in configuration file).  After you type a key combination, you can see its scan code, shift state and KeySym, if applicable.

If you need to query several key combinations, use --count with a maximum  number of keys. With  0 (zero) as the counter, the number of key combinations is unlimited, so that quit by time-out is the only way to terminate the application.

Examples:
    xhkconf -k -c0               # short keywords
    xhkconf --keys --count=10    # long keywords
 

-m
--mouse

This mode displays information needed for mouse event configuration. Before calling ' xhkconf -m ', you have to start the applications, that will receive mouse events (target applications).

After start, the application waits 8 seconds for a button click  (the time-out can be changed using  clicktimeout resource  in configuration file). After you click a window (of the target application), you get

target window id
class hints (application and class names) if applicable
title if applicable,
mouse button number and shift state,
coordinates (x,y) relative to the target window
If you need to perform several queries, use --count with a maximum  number of button clicks , or  0 for unlimited number of clicks.

-l
--list

Display a command assigned to a particular key combination.

You can specify scan code and shift state with --scan argument, or use --all to show all scan codes.

If neither scan code, nor -all  is  specified, ' xhkeys -l' waits keytimeout seconds for a key combination, and displays the action associated.  To query several key combinations in same run, use --count with a maximum  number of keys (count 0 - unlimited)

Normally --list processes only user configuration file., however if --global argument is specified,  the key assignment will be displayed from the global configuration in case is not found in the user configuration.

Examples:
 


xhkconf -ls 176.1 -f ~/.XHkeys.1# Display  binding for scan code 176, shift state 1
# Configuration file is explicitly specified 
xhkconf --list --scan=176.1 --file=~/.XHkeys.1  # Same with long keywords
xhkconf --list --scan=0xb0 # Scan code is specified in hexadecimal format, 
# shift state is  not specified, 0 is assumed
xhkconf -la# List all assignments from user configuration
xhkconf -lag# List all effective assignments (from user,
# or global configuration)

-e
--edit

Edit a command assigned to a particular key in user configuration (global configuration file cannot be accessed).

This is the default mode.

Accepted parameters:  --scan, --count, --file .  The interpretation is similar to --list  mode

Examples:
 


xhkconf -es 0xb0.1# Add/edit  binding for scan code 176, shift state 1
xhkconf --scan=176.1# Same result with using a long keyword, and decimal scan mode
# edit mode is assumed by default
xhkconf -c0 # Count=0 - edit repeatedly until quit on time-out
xhkconf# No arguments: edit a single (prompted) key combination.

If key and/or mouse event are going to be assigned, you have to activate the application(-s), that will receive the events (target application).

xhkeys will look for a command, assigned to the key combination. If there is an assigned command, you will asked a permission to overwrite it.

To enter a,  you will be asked  to select command type:
 

Internal Function See Appendix B
Applicationany external application with command arguments if needed, 
(e.g.    mozilla -edit     to start mozilla composer)
Key EventKeyPress and/or KeyRelease events are sent to a designated window
Mouse EventButton Press and/or ButtonRelease events are sent to a designated window

Following prompts depend on the command type.

At last the new command is displayed, and if accepted, the key binding is added or replaced.

The changes are written to the user configuration file,  specified with  --file command line argument, or ~/.XHkeys by default.
 

-d
--delete

Delete user assignment for a  particular key. This will activate the global assignment if any.

Accepts same parameters as --edit.

-p
--plugins

View, edit and modify the list of plugins. All other command line arguments are ignored.


-p
name
--plugin=
name

Edit an information for a particular plugin specified by its name or alias.

All other command line arguments are ignored.

-h
--help

Print help page and quit. All other command line arguments are ignored.
 

Appendix A.  Configuration file format

xhkeys uses X11 resource file for configuration, and accesses it using X Resource Manager (Xrm).
Use sample configuration file XHkeys.sample as an example.

The resources are listed below

General resources


maxcodesMinimum number of codes: a code line number must not exceed this value (see Code Lines).
maxplugins
Maximum number of plugins: a plugin line number must not exceed this value (see Plugin Declaration)


logmodexhkeys log mode, if not specified in the command line: none, errors (default) or all
logconsole
true: log to stdout, don't use syslog
false (default): use syslog for messages (errors are also sent to stdout)
Resource is ignored, if logging target is specified in command line


osd true (default): osd is enabled
false : osd is disabled
Resource is ignored, if osd mode is specified in the command line
osdFont
Name of font to be used for OSD text. By default the application will look for the following fonts (in given order): 9x15bold variable fixed
osdColour or
osdColor
Colour to be used for OSD text. While screen colour is taken by default.
osdBkgrColour or
osdBkgrColor
Colour to be used for OSD background, Black screen colour is taken for default
osdBkgrMask
In case osdFrameWidth is zero, this resource defines OSD background transparency as a 4 or 8-digit hexadecimal number (prefixed with 0x).  Specified mask value  results in a (monochrome) bimap of size 8x4 or 8x2 resp., where ones stand for mapped pixels, while zeroes refer to transparent pixels.

For example  osdBkgrMask   0xAA55   produces  a 8x2  bitmap, where first line  is 0x55  (binary 01010101), and second line is 0xAA (binary 10101010), or in rasterized view:
 * * * *
* * * *
which corresponds to 50%  X - hatch

As another example, osdBkgrMask 0x3366CC99 produces a 8x4 bitmap with lines 0x99,  0xCC,  0x66, 0x33 or in rasterized view:
*  **  *
**  ** 
 **  **
  **  **
which is a 50% diagonal hatch

Other bkgrMask examples:
0xFFFF    -  solid  (100% opaque) background
  0     -  transparent background (only text is seen)
0x5555    -  50% vertical hatch
0xFF55    -  75% opacity
0x0055    -  25% opacity

Default value  0x3366CC99. 

If SHAPE extension is unavailable,  osdBkgrMask is ignored so that the background is solid.
osdFrameWidth A number from 0 up to 4, that specifies frame thickness. For a non-zero osdFrameWidth the background is assumed to be solid, and osdBkgrMask value is ignored. By default osdFrameWidth is assumed to be 0.
osdFrameColour or osdFrameColor
Colour of OSD frame (in case osdFrameWidth is non-zero). By default  osdColour  is taken to be osdFrameColour.
osdGeometry
Location of osd window as  width x height + x + y .  Positive x specifies the distance of left OSD border from left of the screen, while negative x specifies the distance of right OSD border from right of the screen.  Similarly positive/negative y values specify distance of top/bottom OSD border from top/bottom of the screen. If a parameter  is omitted, the default value is used.
Default: width 300, height 20,  x=(ScreenWidth-OSDWidth)/2, y =4

Examples:
-10+5 position near top right of the screen, width and height - default
260x12-0-0 
position just at bottom left of the screen, width and height are smaller  than default because of using a smaller text font
osdHAlignment
Horizontal alignment of text in OSD window: left, right or centre (default)
osdTextTop Position of top text line in OSD window. if omitted, OSD text is vertically aligned to  the middle of OSD window.
osdTimeout
OSD message is cleared after specified number of milliseconds. If osdTimeout is 0, the message is not cleared until next message appears, otherwise it must be number from 300 up to 5000. Default: 1000.


pluginTimeout 
A period in milliseconds between consecutive plugin calls (see Running Plugin Continuously). In order OSD to be seen continuously, pluginTimeout value should not exceed osdTimeout


keylattency A period in microseconds between generated KeyPress and KeyRelease (see Key Event below), default 0
keypauseA period in microseconds between consecutively generated key event sequences, default 50000
clicklattencyA period in microseconds between generated ButtonPress and ButtonRelease (see Mouse Button Event below), default 0
clickpauseA period in microseconds between consecutively generated mouse button event sequences, default 50000


keytimeoutA period in seconds for xhkconf to wait for a key combination (see Using xhkconf ), default 10
clicktimeoutA period in seconds for xhkconf to wait for a mouse button click (see Using xhkconf ), default 15


Plugin declaration

A plugin is represented by plugin resource. Its name is pluginNNN, where NNN (plugin line number) is a number between 1 and value of maxplugins (inclusive). The plugin line number can be used as a reference to the plugin (selection by line number) from the code line type 'P' (see Code Lines).

A plugin may be declared more than once with a different initialisation string and alias.

A plugin line has the following structure:

pluginName;pluginAlias;initString

Plugins are discussed in Appendix C

Examples:
xhkeys.plugin1:   xhkeys_cdaudio;cda;devname='/dev/cdrom'
xhkeys.plugin2:   xhkeys_mixer;vol;devname=[/dev/mixer];devgrab;
xhkeys.plugin3:   xhkeys_mixer;volCD;devname=/dev/mixer;channel=8

In the example above xhkeys_mixer is declared twice with different aliases ( vol and volCD) and different init strings.

Warning!  For backward compatibility, the application automatically creates a plugin line for each available standard plugin (xhkeys_cdaudio or xhkeys_mixer)  which is not declared in the resource database with as the following:

xhkeys.plugin1:   xhkeys_cdaudio;cda;devname=" cddevice ";devgrab= cdhold,osd
xhkeys.plugin2:   xhkeys_mixer;vol;devname=" mixerdevice";devgrab= mixerhold ;osd

where cddevice, cdhold, mixerdevice and mixerhold are obtained from obsolete resource settings (see Obsolete Resources ).

Since it might make your resource database file hardly readable, you might wish to add the plugin lines manually.

Code lines

Each key combination assignment  is represented by a string resource, called code line. Its name is codelineNNN, where NNN is a number between  1  and the value of  maxcodes  (inclusive).

A code line has he following structure:

scanCode ;shiftState ;cmdType ;parameters
where:

either scanCode or shiftState is a decimal (e.g. 176) or hexadecimal (e.g. 0xb0) number.

cmdType is one of the  following:

 

IxBuilt-in (internal) command. The following optional character specifies type of OSD message (title):
*   use custom title
#   suppress OSD message
If neither is specified, OSD title is command name.
AxApplication call. The optional character specifies type of OSD message in the same way as for a built-in command. By default command line itself is used as OSD title.
Pxx
Plugin call. Two optional characters specify

Plugin selection:
N - by name (default)
L - by plugin number

Plugin call style
O - call once (default)
C - call continuously

Example:
PC - plugin is specified by name (taken by default), and is called continuously
KxxKey event . Two following characters specify

Target window selection: 
C - Target window is identified by WM. class hints (application name, and class name), 
T - Target window is identified by its title (caption) 
I - Target window is given by the window id 
O - Target window is given by its stacking order, or input focus

Sent key selection: 
Y - Sent key is specified by KeySym as symbolic value, 
U - Sent key is specified by KeySym in numeric (decimal, or hex) form,
S - Sent key is specified by scan code

Example: 
KCY  - target window is given by WM. class hints,  key to send is given as KeySym (symbolic)

MxMouse button event. 
The next character specifies target window selection ( C, T, I , or O) in the same way as for a key event
Hint: target identification by WM class hints appears to be the safest one,  because window title often includes variables (file name, tip of the day, etc.). A problem with WM class hints is that an application might have several active windows with same WM class hints, where specification by title may become useful. Use window id only for permanent windows (e.g. weaker clip), because window id is allocated dynamically and can't be therefore a reliable identifier.

The parameters depend of command type:

For  a built-in command (I):

In case of custom OSD title:
    Title ;Command Arguments

otherwise
    CommandArguments
For  an external application (A):
In case of custom OSD message:
    Title ;ShellCommandLine

otherwise
    ShellCommandLine
For a plugin call (P):

(See About Plugins  in Appendix C)

          In case of selection by name:
PluginName/Alias ; OperationName ;OperationArgument s

In case of selection by plugin line number:
PluginLineNumber ;OperationName ;OperationArguments

In case of selection by name/alias, the aliases have preference over plugin names.

Semicolon before OperationArguments is not needed if arguments are not supplied.

For a key event (K):
T argetWindow ;SentKey ;SentShiftState ; PressRelease ;Count
PressRelease and Count can be omitted with separating semicolons

TargetWindow  (a window receiving the event) format depends on target window selection:
C - AppName-ClassName (hyphen - separated)
T - Window Title
I - Window id (a decimal or hexadecimal number)
O - One of the following characters:
       F - window having the input focus,
       T - topmost window,
       B - bottom window,
       R - root window.

SentKey (key to be sent)  depends on sent key selection
Y - KeySym as a text string without XK_ prefix
U - KeySym as a decimal or hexadecimal number
S - Scan code as a decimal or hexadecimal number

SentShiftState (shift state to send) is a decimal or hexadecimal mask

PressRelease  is:
0 - don't send an event, just bring window to focus and top of the stack
1 - send KeyPress event only
2 - send Key Release event only
3 - send KeyPress and Key Release (default)

For a non-zero PressRelease,  target window gets input focus only temporary for sending events, and the window isn't brought upfront.

An interval  between KeyPress and KeyRelease is given as keylattency

Count:   how many times the event has to be sent,  default 1
A pause between events is specified as keypause

Example: The following code line assigns scan code 254 to Ctlr+Ins , which many editors  process as Copy command:

xhkeys.codeline12:    254;0;KOY;F;Insert;4;3    # Copy

In this example:
K - stands for Key Event,
O - target window is specified by stacking order, or input focus
Y - sent key is specified by KeySym

F - target window is the one having the input focus
Insert  - KeySym (see /usr/include/X11/keysymdef.h )
4 - key modifier (4 for Ctrl)
3 - send KeyPress and KeyRelease events

For a  mouse button event (M):
TargetWindow;SentX,SentY;SentButtonNumber,SentShiftState;PressRelease;Count
PressRelease and Count can be omitted with separating semicolons

TargetWindow  (a window receiving the event) format depends on target window selection:
C - AppName-ClassName (hyphen - separated)
T - Window Title
I - Window id (a decimal or hexadecimal number)
O - One of the following characters:
       F - window having the input focus,
       T - topmost window,
       B - bottom window,
       R - root window.

SentX and SentY are coordinates relative to target window

SentButtonNumber (a button number to sent) is an X button number (1- left button, middle, etc.)

SentShiftState (shift state to send) is a decimal or hexadecimal mask

PressRelease is
0 - don't send an event,  just bring window to focus and top of the stack
1 - send ButtonPress event only
2 - send Button Release event only
3 - send Button Press and Button Release (default)

For a non-zero PressRelease,  target window gets input focus only temporary for sending events, and the window isn't brought upfront.

An interval  between ButtonPress and ButtonRelease is given as clicklattency

Count: how many times the event has to be sent,  default 1
A pause between events is specified as clickpause

Example: The following code assigns scan code 119 to seinding mouse events to Window Maker Clip causing switch to next workspace:

xhkeys.codeline4:  119;0;MI;0x6000e7;57;6;1;0;3  # Next workspace

In this example:
M - stands for Mouse Event,
I - target window is specified by window id

0x6000e7 - target window id (Window Maker Clip) as a hexadecimal number
57;6 - coordinates (x;y) of simulated mouse events relative to target window
1 - button number used with sumulated events
0 - modifier used with sumulated events (no Shift, Ctrl, etc)
3 - send ButtonPress and ButtonRelease events

Obsolete resources

The following resources have become obsolete since CD audio and mixer operations are now implemented  with plugins. For backward compatibility the following resource are used to generate initialisation string for default plugins in case they are not declared, but available.

See Plugin Declarations  and Default Plugins sections for details.

cddevicesave as devname with xhkeys_cdaudio (see CDAudio control (xhkeys_cdaudio), but no delimiters or brackets accepted.
cdhold yes or no - same as devgrab with xhkeys_cdaudio 
mixerdevicesame as devname with xhkeys_mixer (see Mixer control (xhkeys_mixer), but no delimiters or bracket accepted.
mixerhold yes or no - same as devgrab with xhkeys_mixer
mixerchannel same as channel with xhkeys_mixer

Appendix B.  Built in operations

A built-in operation has a operation code, and can accept arguments.

The operation codes are case insensitive .

Supported operations

The following built-in operations are supported:

Quit - terminate xhkeys (no arguments)

KillPlugin - stop a plugin running continuously (no arguments)

WndClose - close a window and terminate the application that owns it.
WNDCLOSE  F
 or no arguments
Close a window having an input focus.
WNDCLOSE S Close selected window. After command is entered, three beeps are sound and an OSD message Click a window to close appears. You need to click the window to be closed within 10 seconds (unless otherwise specified as clicktimeout resource - see General Resources). 

WndRotate - circulate windows
ROTATE +Circulate windows by raising the lowest window
WNDROTATE - Circulate windows by lowering the highest window

Obsolete operations

The  CD Audio and Mixer control  is now implemented in plugins.  For backward compatibility the application will recognise the following operations and implements them by calling the appropriate plugin:

Obsolete operation
 Plugin
Plugin operation
CDPLAY
xhkeys_cdaudio
PLAY
CDPLAYPAUSE same
PLAYPAUSE
CDPAUSE same
PAUSE
CDSTOP sameSTOP
CDLOADEJECT sameLOADEJECT
CDLOAD sameLOAD
CDEJECT sameEJECT
CDSLOT sameSLOT
SNDVOLUME xhkeys_mixer
VOLUME
SNDBALANCE same
BALANCE

Appendix C. Plugins

About plugins

A plugin is a module designed to implement a designated set of functions when called from xhkeys main application. It is implemented as a Linux shared library.

Each plugin has a name related to the file name of corresponding module. The application appends .so to plugin name and expects the module directory ${libdir}/xhkeys,  where libdir is defined during configuration, typically ${prefix}/lib. Assuming default prefix value /usr/local,  for plugin name xhkeys_cdaudio the application will try /usr/local/lib/xhkeys/xhkeys_cdaudio.so

A plugin may accept parameters in the form of initialisation string (init string). An initialisation string is sent to  the plugin  once during its  activation.

A plugin may have a friendly name, or alias which provides a convenient reference to the plugin.

All plugins should be declared in the resource data base, see Plugin Declarations. A plugin may be declared several times with different initialisation string in which case an unique alias is recommended for each declaration.

Each plugin provides a set of operations each identified by operation name. An operation might also accept arguments to modify its functionality. In order to call a plugin you need to bind a key combination to a particular plugin operation and (if needed) operation arguments with a code line type 'P' (see  Code Lines). The plugin can be specified either by plugin name / alias,  or by the line number used to declare the plugin ( see Plugin Declarations).

Note that  aliases, and operation names are case insensitive,  while plugin names are case sensitive simply because ext2 (like any other UN*X file system) is. However I wouldn't risk  having plugins names that differ only in case :)

A key combination can be set up for a plugin continuous call,  in which case the specified function will be called continuously until the plugin is stopped. The period between two consecutive calls of the plugin is specified with pluginTimeout resource (see General Resources). Only one operation at a time can run continuously, therefore each continuous call automatically stops previously running continuous operation if any. To stop a continuous  plugin call, use KillPlugin built-in operation (see Appendix B. Built-in operations). Unless otherwise specified, you can also stop a continuous call by re-entering the key combination used to start that operation. Please note that not all operations can be called continuously. If a continuous call is requested for a "non-continuous" operation, the operation is called only once.

See sample file XHkeys.sample for the example of plugin declaration and call.

Default Plugins

The application comes with two standard plugins: xhkeys_cdaudio and xhkeys_mixer. Each of those can be disabled by using --disable-cdaudio and --disable-mixer  with ./configure  (see Installation ).

CD Audio control (xhkeys_cdaudio)

Initialisation string

General plugin argument are  specified in the initialisation string, e.g

devname=/dev/scd0;devgrab=yes;noosd 
 
devnameCD device name to use for built in CD functions, default  /dev/cdrom
In (a rather imaginary) case of device name containing special characters, or just for readability, you can use custom delimiters or brackets, e,g, devname='dev/hdc',  devname=[/dev/hdc], etc.
devgrab
yes or no 
If yes, the CD device is opened with the first internal command accessing CD, and closes before xhkeys quits.  If no, the CD device is opened and closed for each individual operations.
For a better readability you can code just devgrab (same as devgrab=yes), or nodevgrab (i.e. devgrab=no)
If devgrab is omitted, devgrab=no is assumed. Set devgrab=yes, if you use cdrom auto eject  (e.g. dev.cdrom.autoeject=1 in /etc/sysctl.conf).
osd
yes or no
Enable/disable on-screen display(OSD).  osd value does not affect operations like Status, or  continuously called Play and PlayPause, merely because calling those without OSD wouldn't make sense. However noosd will suppress OSD for called once Play, PlayPause and other operations.
Note that  disabling osd for xhkeys will suppress any plugin osd.

Operations

The following operations are used with  xhkeys_cdaudio

PLAY
: position and play CD

The argument specifies time or track number. When prefixed with + or - it is interpreted as relative to current position,  otherwise it specifies absolute address (track number or time from disc start). D refers to starting track on the disc, * refers to current position.

Examples: 


Play Start from current position
Play *Same
Play DPlay from disc start
Play -1TPlay previous track
Play +4TGo 4 tracks forward
Play 4TPlay track 4 
Play +0TRestart  current track
Play +1M3S2FGo 1 minute 3 sec and 1 frame forward  (frame = 1/75 sec)
Play -10SGo 10 seconds back
When Play is called continuously, the first call performs necessary positioning starts playback and shows status, while following calls just update OSD. Use KillPlugin (see  Supported Operations in Appendix B) to stop continuous run.

PLAYPAUSE - a paused/stopped CD start playing, a playing CD pauses. No arguments

When PlayPause is called continuously, the first call performs actual CD operation and draws status, whereas following calls merely update OSD. Use KillPlugin to stop.

PAUSE - pause a playing CD.  No arguments, run once only

STOP - stop a playing CD.  No arguments, run once only

STATUS - show CD status and position. No arguments Works even with osd=no in init string. Can be called continuously. Press key combination again, or use KillPlugin to stop.

LOADEJECT - load a disk if no medium currently, eject otherwise.   No arguments, run once only

LOAD - attempt load a disk.   No arguments, run once only

EJECT - attempt to eject a disc.   No arguments, run once only

SLOT - change current slot on a multi-changer device. Run once only.
Argument - slot number,  with a sign if relative to current:


Slot +1 Select to next slot
Slot -1Select previous slot
Slot 0Select starting slot (slot number 0)

VOLUME
- control internal CD volume
BALANCE - control internal CD balance
Argument - volume or balance that be relative to current (+value, -value) or absolute (without a sign, or with =).

Volume values are in the range from 0 up to 100, balance values are from -50 up to +50. Use "="  prefix in order to specify an absolute negative balance value (see examples for Audio Mixer).

Don't confuse internal CD volume with CD mixer channel ! Internal CD volume control is provided by CD drive itself and is not related to any sound card. If you plug your headset straight into a CD drive outlet, you will appreciate this functionality. Unfortunately not all CD drives  support volume control.

Call VOLUME or BALANCE without an argument (or with +0 as an argument)  to check current volume and balance. In this case the message will appear even if disabled.

If operation is called continuously, first call will perform volume/balance change, all others will just opdate status. You hardly need it, unless there is something really wierd happens to your volume.  Use KillPlugin to stopp continuos run.


Audio Mixer (volume and balance) control (xhkeys_mixer).

Initialisation string

General plugin argument are  specified in the initialisation string, e.g

devname=/dev/scd0;devgrab=yes;channel=8;osd 
 
devnameMixer device name to use for built in CD functions, default  /dev/mixer
As for CD devname, you can use custom delimiters or brackets, e,g, devname='dev/mixer2',  devname=[/dev/mixer2], etc.
devgrab
yes or no 
If yes, the device is opened with the first internal command accessing mixer, and closes before xhkeys quits.  If no , the mixer device is opened and closed for each individual operations.
For a better readability you can code just devgrab (same as devgrab=yes), or nodevgrab (i.e. devgrab=no)
devgrab=yes seems to be rather safe and probably more efficient choice, however devgrab=no is assumed if parameter is omitted
channel
default mixer channel used for volume/balance control.

The most commonly used mixer devices are:
0
Master Volume
5
Speaker
1
Bass
6
Line
2
Treble
8
CD
3
Synthesizer
10
PCM-2
4
PCM-1
13
Output Gain

The full list of mixer devices is given in /usr/include/linux/soundcard.h

If channel is omitted, or set to (-1),  the plugin takes the first available of the following devices:  Master Volume,  PCM-1,  PCM-2.
osd
yes or no
Enable/disable on-screen display(OSD).  osd value does not affect operations like Status, or  continuously called Play and PlayPause, merely because calling those without OSD wouldn't make sense. However noosd will suppress OSD for called once Play, PlayPause and other operations.
Note that  disabling osd for xhkeys will suppress any plugin osd.

Operations

All mixer operations apart from STATUS can't  be called continuously.

VOLUME

Change volume. The argument is coded as volume;channel

Volume can be relative to current (+value, -value) or absolute (without a sign).
The maximum volume value is 100.

The optional channel parameter allows overwriting the default mixer channel selected during plugin initialisation (see Audio Mixer initialisation string), this value is effective only for the current operation.


Volume +2 Increase volume by 2 on default device
Volume 0Mute
Volume -1Reduce volume by 1 on default device
Volume +1;8 Increase CD volume (device 8) by 1

BALANCE

Change balance. The argument is coded as balance;channel

Balance value can be from -50 up to +50.
You need to use prefix = with an absolute negative balance.

Examples:


Balance +2Increase balance by 2 on default device
Balance -1;6Reduce balance by 1 for Line (device 6)
Balance =-10Set balance to -10 on default device  (= is required to avoid confusion)
Balance =30;8Set balance to 30 for CD (device 8)  (= is not necessary, but used fore readability)
 
MUTE

Save current volume and set volume to zero. Calling Mute again restores saved volume.

Argument: channel,  or omitted

STATUS

Show volume and balance fore selected (or default) device. The information is shown even if osd is disabled for the plugin. SndStatus may be called continuously.o. Press key combination again, or use built in operation KillPlugin (see Appendix B) to stop.

Argument: channel,  or omitted

Plugin interface


This chapter is mainly for myself (indeed, I don't regularly work on this project and often forget what it's all about), or someone else who might wish to develop an xhkeys plugin or improve standard ones.

How to build a plugin ?

A plugin is a Linux shared library. In order to create a shared library, you need to compile each module with -fPIC (position-independent code), and use -shared with linker. To be really pedantic, you  also need to supply -soname parameter to the linker, and call ldconfig for the target directory, which might not be really needed in this case.

Here is a very simple example:

Compile link, and configure myplugin.so

  gcc -c -fPIC -O2 -g -o myplugin.o myplugin.c
  gcc -shared -Wl,-soname,myplugin.so -o myplugin.so myplugin.o

Installation of myplugin.so
   install   -s -m755   myplugin.so  /usr/local/lib/xhkeys
   ldconfig   /usr/local/lib/xhkeys
  
(Here I really wanted to make it easy. See Makefile.in for a much better way to do that)

How the main application operates a plugin ?

To get the plugin work you need to implement the functions listed below (with exact prototypes).  Some of the functions are optional, meaning that their absence does not upset the main application. Still it would be a good idea to have all optional functions, because an optional function might become mandatory in a future release.

Of course  you are free to use other functions which are local to your plugin, but for readability I recommend using static attribute for each local function (unless the function should be accessible from another source file).

C++ users need to code external "C" for each exported function. xhkeys comes with a header file xhkeys_plugin.h. You needed to copy this header to your project and include it in your plugin code, with  XHKEYS_PLUGIN_CODE definition before include:

#include <X11/Xlib.h>
............................  
#define XHKEYS_PLUGIN_CODE
#include "xhkeys_plugin.h"
..........................

This enables custom types and ensures that all exported functions have correct prototype. In particular it provides extern "C" when required.

A file named plugin_common.c contains some parsing functions used with standard plugins, but the functions that might be useful for you as well. File plugin_common.h contains function prototypes and type definitions for those functions.

Since there could be several instances of same plugin, keeping variables (i.e. non-constant data) statically in the plugin is a bad practice. A correct way is using data buffer. A data buffer of length sufficient to hold all variables is created in init and a pointer to it is returned to the main application. Other functions get this pointer as an argument and can read or modify the data. The data buffer is released by the main application before it quits. Don't release data buffer in term , otherwise the application will crash !

The following section contains a detailed description of the functions.
Name
Type
Description
init
Optional
Called once  after opening the plugin. The function processes init string, and (if necessary) allocates and initialises data buffer. If the function is missing, init string is discarded and no data buffer is assumed.
getopcount
Required
Returns number of operations supported by the plugin.
getopdata
Required
Returns properties of requested operation.
process
Required
Process operation.
property
Optional
Get or set a global plugin property (e.g. get plugin version and description, pass xhkeys settings to the plugin). Currently this function is not really crucial, but it is a good idea to have it.
term
Optional
A deinitialisation routine (e.g. close files, free arrays) called before closing the plugin. As opposed to standard plugin destructor, term provides you an access to data buffer.

Plugin functions

Initialisation

Bool  init(void **bufferReturn, const char *initString, char *message, int msgLen);

Arguments:
bufferReturna pointer to allocated buffer or NULL (passed by reference)
initStringinitialisation string
messagea pointer to error message
msgLenmaximum length of error message (to be used with strncpy, or snprintf)

Return:  True on success, False on failure

  Here is the typical scenario for init
  1. Allocate the data buffer to hold arguments and variables. In fails, copy string "<plugin name> init memory allocation error" to message;
  2. If no errors so far, process init string and fill buffer with arguments. In case of an error copy error message to message;
  3. If no errors so far, initialise variables and generate error message in case of failure
  4. If no errors,  assign *bufferReturn to the data buffer pointer, set message to an empty string and return True.  In case of an error, free data buffer if necessary, set *bufferReturn to NULL and return False,

Query operations

The following functions are required, because it doesn't make sense to have a plugin without any operation supported :). The number of operations provided by the plugin  (OPCOUNT for short) is returned by getopcount.

Each operation is characterised by :
Functions:

int getopcount(void);
Returns OPCOUNT

Bool getopdata(int opCode,char opName[XHKEYS_PLUGIN_OPNAME_LENGTH], int *opFlags);
Get characteristics of an operation specified by operation code.

Arguments:
opCode
code of requested operation
opName
If not NULL, a string of size is at least XHKEYS_PLUGIN_OPNAME_LENGTHto be filled with a zero-terminated operation name.
opFlags
If not NULL, a pointer int variable to get operation flags

Make sure that the function does not crash if opName and/or opFlags is NULL !

Returns True on success,  False is opCode is invalid (i.e < 0, or  >= OPCOUNT).

Operation flags contain (the constants are defined in xhkeys_plugin.h) argument type that might be OR-ed with some modifiers.

Argument type (select one):

XHKEYS_PLUGOPTYPE_PARMNONE operation doesn't take arguments (default)
XHKEYS_PLUGOPTYPE_PARMOPTIONAL operation takes  an optional  argument
XHKEYS_PLUGOPTYPE_PARMREQUIRED operation requires an argument

Modifiers (used in combination with a selected argument type)

XHKEYS_PLUGOPTYPE_PARMSPECCHARS Operation argument may contain Space, Tab or #. Normally these characters are used for starting comments in the code line for plugin call (see Code Lines), so that xhkeys main application removes comments and delimiters before passing an argument to the plugin.  If XHKEYS_PLUGOPTYPE_PARMSPECCHARS is specified for the operation, the tail of the code line is passed to the plugin "as is" without pre-processing.
XHKEYS_PLUGOPTYPE_CANCONTINUEThe operation may be called continuously (see About Plugins)
XHKEYS_PLUGOPTYPE_PERSISTENTA continuously running operation does not stop but restarted, when a key combination is re-entered. In this case KillPlugin (see Built-in Operations) is the only way to stop the plugin. See Operation Processing for more information about continuous run.
XHKEYS_PLUGOPTYPE_PERSISTENT assumes XHKEYS_PLUGOPTYPE_CANCONTINUE, so that there is no need for OR-ing: just XHKEYS_PLUGOPTYPE_PERSISTENT will do.

Operation processing

The culmination of a plugin :) called to implement an operation.

Bool process(void *buffer, int opcode, const char *argument, int behaviour, char *message, int msgLen);

Arguments:
buffer
Pointer to data buffer created by  init,  or  NULL
opcode
Operation code (see Query Operations)
argument
Operation argument
behaviour
One of behaviour constants discussed below
message
This string gets error message on error, or OSD text message on success
msgLen
Length of message string

Returns:
True
Processed successfully, message contains OSD text.
Set message to zero-length string if no OSD is needed.
False
Processed with errors, message contains error description 


Behaviour constants (defined in xhkeys_plugin.h):
XHK_BEHAVIOUR_ONCE
The operation is called for a  single run
XHK_BEHAVIOUR_CONTINUOUS
First call in continuous run
XHK_BEHAVIOUR_REPEATED
Repeated call in continuous run
XHK_BEHAVIOUR_DISCONTINUE
Last call in continuous run, the operation is about to stop.
 
If XHKEYS_PLUGOPTYPE_PERSISTENT is specified in operation flags (see Query_Operations), XHK_BEHAVIOUR_DISCONTINUE is call only as a result  KillPlugin, or xhkeys termination, while re-entering key combination results in calling process with  XHK_BEHAVIOUR_CONTINUOUS instead and the operation does not stop, which effectively means restarting it.

Before calling the plugin, image pixmap ( drawPixmap in XHKEYS_PLUGIN_OSD_DATA) , is cleared, while mask bitmap ( maskBitmap), if not None, is set according to background mask. To provide graphics,  the plugin should:

Property exchange

This function is used to exchange general data between main application and plugin.

Bool  property(void *buffer,  XHKEYS_PLUGIN_PROPERTY code, ...);

Arguments:
buffer
Pointer to data buffer created by  init,  or  NULL
code
One of XHKEYS_PLUGIN_PROPERTY constants listed below (declared in xhkeys_plugin.h).
The remaining arguments depend on the property (as specified below).

 Returns: True, if the property has been processed, False - otherwise.

The following properties are currently supported:

Legend:
(fp)  from plugin: the property is obtained by the main application from the plugin,
(tp)  to plugin: the property is passed by main application to the plugin
(u) updated  (in future ?): the default value is passed from main application to the plugin, which might change it and pass back to the main application

(fp) XHK_PLUGIN_VERSION,  int *version       
Plugin version (used with xhkconf -p)

(fp) XHK_PLUGIN_DESCRIPTION,  char *descriptionReturn, int descriptionLength      
Plugin description (used with xhkconf -p)

(tp) XHK_PLUGIN_APPVERSION,  int version       
Application version

(tp) XHK_PLUGIN_LOGMODE,  int logSettings       
Logging settings , as   (logConsole << 8) | logMode , where
logConsole is 0, if syslog in enabled, 1 if all logging goes to stdout
logMode is 0 - log none, 1 - log errors, 2 - log all
More about logging in Using xhkeys and General Resources.

(tp) XHK_PLUGIN_OSDDATA, const XHKEYS_PLUGIN_OSD_DATA *osdData
where XHKEYS_PLUGIN_OSD_DATA is defined as the following:

typedef struct{
   Display *dpy;                 // Display   
    int    screenNo;             // Screen number
    unsigned long textColour;    // text colour
    unsigned long bkgrColour;    // background colour
    unsigned long frameColour;   // frame colour
    unsigned long bkgrMask;      // Background mask (
see osdBkgrMask in Appendix A)
    GC     drawGC;               // Draw Graphic context
    GC     maskGC;               // Mask Graphic context
(None for opaque background)
    Pixmap drawPixmap;           // Draw pixmap
    Pixmap maskBitmap;           // Mask (monochrome) bitmap (None for opaque background)
    XRectangle    rect;          // OSD location and size
    XFontStruct *font;           // Font data used for drawing OSD text
    int textTop;                 // Offset of text top
( see osdTextTop in Appendix A)
    char  alignment;             // OSD text alignment (0-left, 1-right, 2-centre)
    char  frameWidth;            // Frame width (0 to 4)
    Bool enabled;                // OSD enabled
} XHKEYS_PLUGIN_OSD_DATA;



All (tp) properties are passed after all resources have been processed and OSD is initialised.

Termination

This is the last (but definitely not least) function to be called for a plugin:

void term(void *buffer);

Argument
    buffer   Pointer to data buffer created by  init ,  or  NULL

You might wish to use this function for closing files and releasing dynamically allocated arrays. Don't release the buffer !  xhkeys attempts to destroy the buffer just after calling term, so that your "favour"  won't be appreciated this time :)

That's all for now. Cheers !