Copyright © 2002 Andrey Volkov
Revision date 11/30/2002. You can always get the latest version from
mkeys.sourceforge.net
mkeys is a daemon that, once it is started, sits in the background and listens for user input. It does this absolutely independently from other programs that are running and handling user input. You may switch virtual consoles, start or stop X server - mkeys will behave as usual.
To run mkeys, simply type:
mkeys
To unload mkeys, type:
mkeys -u
When mkeys is started, it look at a lock file, $HOME/.mkeys/locks/lock. If it does exist, mkeys sends SIGHUP to the process that created it (another copy of mkeys), thus making shure that only one copy per user is run.
Then, if -u or --unload is specified in the command line, mkeys return control to the shell. Otherwise, it creates its own lock file and daemonize.
After it has daemonized, it begins to read config directives (which somebody can call programs). It looks for the file $HOME/.mkeys/INCLUDE. If it is found, read it line by line, assuming that each line contain name of a file that should be processed. It processes each file mentioned in INCLUDE. After all, even if INCLUDE was not found, it processes $HOME/.mkeys/config.
Then it starts serving users needs until it receives SIGHUP, when it removes a lock and terminates.
The basis of mkeys is its hierarchical tree where it contains all data and all actions available to user. It is in some way similar to UNIX filesystem, but uses dots instead slashes. For example, you can have somewhat like this (tree on the left, names on the right):
+ |-bin bin | |-OSS bin.OSS | | |-name bin.OSS.name | | |-device bin.OSS.device | | |-volume bin.OSS.volume | | | |-step bin.OSS.volume.step | | | |-up bin.OSS.volume.up | | | |-down bin.OSS.volume.down | | |-balance bin.OSS.balance | | |-step bin.OSS.balance.step | | |-left bin.OSS.balance.left | | |-right bin.OSS.balance.right | |-LED bin.LED | |-name bin.LED.name | |-device bin.LED.device | |-NumLock bin.LED.NumLock | |-ScrollLock bin.LED.ScrollLock | . . . |-keys keys | |-Keypad_Plus keys.Keypad_Plus | |-KeyPad_Minus keys.Keypad_Minus | |-VolumeUp keys.VolumeUp | |-VolumeDown keys.VolumeDown . . . . .
These names are used everywhere when programming mkeys.
Each node contains some value (or action, or both), and it can be read, written or executed. Each of these operations is permitted for every node of a tree, though may not be implemebted by a particular node.
Each node is responsible for all its subnodes. It has to maintain the activity of its subnodes. This means that, for example, node bin.OSScan fully control the behaviour of nodes bin.OSS.volume, bin.OSS.volume.step and so on.
Values are used when making assignments, comparing things, running shell commands, loading modules, etc. Value is always a string. Values can be presented as:
Since every value in mkeys is a string, "false" and false are exactly the same. And "true" is exactly the same as true.
Scripts that make things running, consist of a very simple set of commands.
set some.node.to.set to value
This will set some.node.to.set to a value value. If node some.node.to.set does not exist, it will be created.
If some.node.to.set is presented by some specific loadable module, for example, mounted on some.node, it may not behave as you wish.
set bin.OSS.device to "/dev/dsp" set bin.OSS.volume.step to "2"
run command
run some.node.to.run
The first will run command using default shell. For this, command should be a correct value.
The second will run action contained within some.node.to.run
run "mozilla" run bin.OSS.volume.up
if condition :
operators
endif condition :
operators
else :
operators
end
It is a conditional operator, you know what it's like? The only condition available for now is:
some.node.to.compare is value
It refurns true if some.node.to.compare contents is exactly the same as value. If some.node.to.compare does not exist, it returns false.
if bin.OSS.name is "OSS": set bin.OSS.device to "/dev/dsp" else: mount "OSSNode.m" to bin.OSS set bin.OSS.device to "/dev/dsp" end;
link some.node.to.become.a.link to some.node.to.point.to
This will create a link some.node.to.become.a.link to a point to some.node.to.point.to. It is similar to hard links in the filesystem, the only difference is that when you delete some.node.to.point.to, it would be very bad to refer to it using still existing link. On the other hand, when you move some.node.to.point.to to another location, some.node.to.become.a.link will still point to it.
operation opr.print1: run "echo Print1 >> /tmp/testout" end; operation opr.print2: run "echo Print2 >> /tmp/testout" end; link prints.p1 to opr.print1 link prints.p2 to opr.print2 run prints.p1 /* prints Print1 */ run prints.p2 /* prints Print2 */ link print to prints.p1 run print /* prints Print1 */ link prints.p1 to opr.print2 run prints.p1 /* prints Print2 */ run print /* still prints Print1 */
operation some.name :
operators
end
key somekey :
operators
end
operation is somewhat like procedure in Pascal. A bunch of operators are given a name some.name and placed in a tree. You can run them using a run operator.
key is a shortcut for creating keyboard actions, it creates operation with a name keys.somekey.
operation internet: run "mozilla" run "kmail" run "/etc/ppp/dial" end; key Homepage: run internet end;
mount filename to some.point.to.mount.to
This will load a dynamically loadable (DL) module filename (filename should be a value, i.e. in almost all cases it should be quoted or doublequoted). A DL module contains a special node, that is then mounted (placed) to the some.point.to.mount.to. If filename starts with '/', it is considered to be an absolute path to the module in a local filesystem, else filename is treated as relative to $HOME/.mkeys/modules/. After mounting, you can refer to loaded node as some.point.to.mount.to.
mount "OSSNode.m" to bin.OSS if bin.OSS.name is "OSS": link bin.sound to bin.OSS set bin.OSS.device to "/dev/dsp" set bin.OSS.volume.step to "1" set bin.OSS.balance.step to "2" end;
unset some.unwanted.node
This will delete some.unwanted.node from nodetree. This is deprecated for now, is fairly untested and is still basically an unwanted feature. Do not use it. If you do need it, contact me and tell a reason why, and I will not call it deprecated and unwanted. It would be nice if you test it then.
None, since it is deprecated
Modules are loaded by the mount command. Usually they are represented by one "real" node that acts for all its subnodes. But it is not a rule, it is only a current state.
This module acts as an input driver for devices that support event interface. It reads device files (usually /dev/input/event* ) and issues some commands. I will write more later...
This module acts is for changing volume and balance (and muting sound, too). As you can guess, it is for OSS (Open Sound System). I did not test it with ALSA, but they say ALSA to be OSS-compatible, and in such an easy case I think it can. If it does work with ALSA, please, let me know. And if you can write a module for ALSA - it would be wonderful. I will write more on this later...
This module acts is by all means a toy-like module. However, I find it very useful for user interface style mkeys provide. LEDNode.m can turn LEDs on, off, it can even make them blink. I will write more on this later...
Here I will show an example of a simple $HOME/.mkeys/config file. It will configure four keys on my multimedia keyboard to control xmms. Here it is:
mount "HIDNode.m" to bin.mkeys /* This will load an input driver for */ set bin.mkeys.device to "/dev/input/event1" /* multimedia keys on my keyboard, which */ /* are represented as /dev/input/event1 */ set xmms_playing to false key PlayPause: /* This will be called when I press a */ if xmms_playing is true: /* play/pause key ( >/|| ) */ run "xmms --pause" set xmms_playing to false else: run "xmms --play" set xmms_playing to true end; end; key StopCD: /* A stop key is called StopCD on my kb */ run "xmms --stop" set xmms_playing to false end; key NextSong: run "xmms --fwd" end; key PreviousSong: run "xmms --prev" end;
That's it! Run mkeys from X, so that DISPLAY variable will be set when you first call xmms, or it will not initialize. Or run mkeys from console and load xmms manually when X starts. You will be able to handle it by pressing keys.
Two main difficulties in listing are the first two lines. First line loads a module "HIDNode.m" and places a node provided by it to bin.mkeys. This module provides an input driver for all this USB devices, that are controlled by event interface. The node it provides is an interface to this driver. It is used to contol the behaviour of the driver and set its parameters. The only required parameter is device, which is a filename of event device. After this parameter is set, driver tries to open this file, and then starts to poll it. Once a key press event is read, it runs keys.KeyName. Once a key release event is read, it runs keys.KeyName.release. The root for events may differ from keys, it is controlled by driver parameter key. Like this:
set bin.mkeys.key to "multimedia"
Note, that once you change root for keyboard events, you should specify actions like this:
operation multimedia.NextSong: run "xmms --fwd" end;
Now I would like to add volume-changing capabilities to my config, and it looks like this now:
mount "HIDNode.m" to bin.mkeys /* This will load an input driver for */ set bin.mkeys.device to "/dev/input/event1" /* multimedia keys on my keyboard, which */ /* are represented as /dev/input/event1 */ mount "OSSNode.m" to bin.OSS /* This will load Open Sound System */ set bin.OSS.device to "/dev/dsp" /* driver and set some parameters */ set bin.OSS.volume.step to "4" set xmms_playing to false key PlayPause: /* This will be called when I press a */ if xmms_playing is true: /* play/pause key ( >/|| ) */ run "xmms --pause" set xmms_playing to false else: run "xmms --play" set xmms_playing to true end; end; key StopCD: /* A stop key is called StopCD on my kb */ run "xmms --stop" set xmms_playing to false end; key NextSong: run "xmms --fwd" end; key PreviousSong: run "xmms --prev" end; key VolumeUp: run bin.OSS.volume.up end; key VolumeDown: run bin.OSS.volume.down end; key Mute: run bin.OSS.volume.mute end;
The only difference from previous example is in loading an additional driver for OSS (Open Sound System). I think that no comments are needed here.
Now I would like to change my volume using LeftAlt+MouseWheel. Let's see what I change:
mount "HIDNode.m" to bin.mkeys /* This will load an input driver for */ set bin.mkeys.device to "/dev/input/event1" /* multimedia keys on my keyboard, which */ /* are represented as /dev/input/event1 */ mount "HIDNode.m" to bin.keyboard /* This will load an input driver for */ set bin.keyboard.device to "/dev/input/event0" /* the conventonal keys on keyboard, */ /* represented as /dev/input/event0 */ mount "HIDNode.m" to bin.mouse /* This will load an input driver for */ set bin.mouse.device to "/dev/input/event2" /* my wheel mouse, whish is */ set bin.mouse.rel to mouse /* represented as /dev/input/event2 */ mount "OSSNode.m" to bin.OSS /* This will load Open Sound System */ set bin.OSS.device to "/dev/dsp" /* driver and set some parameters */ set bin.OSS.volume.step to "1" /* Change: more precise volume steps */ set xmms_playing to false key PlayPause: /* This will be called when I press a */ if xmms_playing is true: /* play/pause key ( >/|| ) */ run "xmms --pause" set xmms_playing to false else: run "xmms --play" set xmms_playing to true end; end; key StopCD: /* A stop key is called StopCD on my kb */ run "xmms --stop" set xmms_playing to false end; key NextSong: run "xmms --fwd" end; key PreviousSong: run "xmms --prev" end; key VolumeUp: run bin.OSS.volume.up /* Change: yes, that silly! The less */ run bin.OSS.volume.up /* volume step gets... */ run bin.OSS.volume.up run bin.OSS.volume.up end; key VolumeDown: run bin.OSS.volume.down run bin.OSS.volume.down run bin.OSS.volume.down run bin.OSS.volume.down end; key Mute: run bin.OSS.volume.mute end; set AltPressed to false key LeftAlt: set AltPressed to true end; key LeftAlt.release: set AltPressed to false end; operation mouse.WheelUp: if AltPressed is true: run bin.OSS.volume.up end; end; operation mouse.WheelDown: if AltPressed is true: run bin.OSS.volume.down end; end;
The only difference from previous example is in loading more instances of a HIDNode driver. One is loaded for keyboard, one for multimedia keys, one for mouse. Note that a root for mouse events (exactly speaking, relative movements) is controlled by driver parameter rel, and I set it to mouse. All the mouse buttons (even side buttons and clicks on a wheel) still have keys as their root. The only interesting relative events from mouse are wheel movements.