mxk-tutorial(8)

MXK tutorial(8) Operator Manual MXK tutorial(8)

Overview

This tutorial shows you how to write simple mxk scripts. It assumes that you have managed to install mxk successfully.

First script

As root run mxk without any arguments. You should see output looking something like


~# mxk

mxk: starting


mxk: starting console


mxk: starting unix monitor on /var/run/mxk


mxk: starting keyboard server


mxk: ready to serve requests


console: ready

The line console: ready is significant. It means that the console is ready to accept commands. Ignore the fact that there is no prompt. Just type in a command and then press enter. A good command would be help. This command should have lots of text scrolling past. Lets ignore that for the time being. Type list-sources instead. You should see output like this


list-sources


/dev/input/event0: unused: PC Speaker


/dev/input/event1: unused: AT Translated Set 2 keyboard


/dev/input/event2: unused: ImExPS/2 Generic Explorer Mouse


3 entries

This listing shows you what input devices the kernel thinks are connected to your system. If the listing is empty or you get an error, you either don't have any input devices or the kernel hasn't been built with evdev support. Remember you need a 2.6 series Linux kernel which knows about evdev. Try loading the evdev module explicitly with modprobe evdev or try creating the device files manually with mknod(1) in case of problems.

In all likelihood you will have some odd input devices such as power buttons and PC speakers. We will ignore these and focus on the important devices such as keyboards and mice. In the example the third device ( /dev/input/event2) is a mouse (ImExPS/2 Generic Explorer Mouse). We can tell mxk to open this device. Use the command file-source 0 /dev/input/event2 to open the device as source 0. If the open succeeds we can use the command used-sources to view more information about the device. You should see something like this:


file-source 0 /dev/input/event2


used-sources


0: ImExPS/2 Generic Explorer Mouse: bypass [sync key relative]


1 entry

The fields in the square parenthesis tell us that this device has keys (actually buttons) and can report relative movements. Good. Note the word bypass, it means the device is opened, but won't generate events just yet. We'll change that right at the end of our script, once we have set up everything else.

Now, once we have a source we want to connect something to it. Lets try a translation array. It matches a single key event and replaces it with an array of other events. First create the array, with identifier 0, using the command start-array 0. Then tell the array that for each middle click (middlebutton) we want to generate the word hi but with the i only being printed when the middlebutton is released. To achieve this enter the following two commands


press-array 0 middlebutton h/press sync: h/release sync:


release-array 0 middlebutton i/press sync: i/release sync:

So now we have a piece of logic which does the substitution. We can inspect it with the list-arrays command. You should see the following output


list-arrays


array[0]: record=<disabled>, play=<disabled>, state=play, permit=any, unknown=ignore


release middlebutton: i/press sync: i/release sync:


press middlebutton: h/press sync: h/release sync:


2 entries

Sofar we have something which collects the input (a source), and something which does a substitution (the array). Now we only need a sink or target. This one is easy to create. Just type start-target 0. The kernel should report a new device, called MXK/version (PID). If this doesn't work you don't have uinput support in your kernel or are missing the device node /dev/input/uinput. To check that this has been created, run the list-sources command again. You should see an extra entry


list-sources


/dev/input/event0: unused: PC Speaker


/dev/input/event1: unused: AT Translated Set 2 keyboard


/dev/input/event2: bypass: ImExPS/2 Generic Explorer Mouse


/dev/input/event3: target: MXK/0.1 (29378:0)

Admittedly, it is a bit confusing to have the target show up in the input list too, but the target is a special input device, one which mxk generates.

So we have generated the three nodes, a source, a processing node and a target. Now they need to be connected to each other in the correct way. This is done using the connect-node command. Type connect-node source:0 array:0 to connect the output of the source to the array and connect-node array:0 target:0 to connect the array output to the virtual input device. The target node then sends events back to the kernel and from there to all your applications.

This only leaves one more command - turning on the bypassed source node. Lets do it with activate-source 0. You can run used-sources or list-sources after that to see the status of the source changing from bypass to active.

Now if everything has worked out, middle clicks should also generate the word hi, try it. If it works congratulations, you have written your first mxk script.

Cleaning up the first script

Suspend disbelief and pretend that the above mxk function is really useful and you need to run it occasionally.

To avoid typing out all the commands every time, save them to a file, say hi.mxk and then run it as follows mxk -p hi.mxk.

In case you want to run this in the background, use mxk -dzp hi.mxk.

If you prepend the line #/usr/sbin/mxk -dzp to the file hi.mxk and make it executable, then you can run the script directly, similar to a shell script.

Now before you send the script to your friend who also needs to have his middle mouse click say hi, you need to remove the hardcoded path to your mouse - currently this is set to /dev/input/event2. On your friends system the mouse is likely to show up as a different file.

There are two other ways of opening a source, by capability and by name.

Use caps-source to open a source by capability. Here a capability is the ability of a source to generate a certain event type, such as a key/button press or a relative motion event. Mice tend to have both these capabilities so use caps-source 0 key relative to open the first unused source which has these. Like in the case of the file-source and start-array commands, the 0 is the instance identifier. If you wish to list the types of events which mxk knows about, use the list-events command, while the used-sources command will show you the capabilities of already opened devices.

Alternatively you can use name-source to open the mouse by name, like this


name-source 0 ImExPS/2 Generic Explorer Mouse

Note that there are no quotes surrounding the name and that the match is case-sensitive. However, the name will match a substring, so name-source 0 ImExPS/2 would also have worked.

You may encounter hardware which registers multiple /dev/input/event entries, all having the same name. In such situations you may wish to try running a name match several times, as mxk will skip already opened sources. The below example illustrates this


list-sources


/dev/input/event0: unused: USBPS2


/dev/input/event1: unused: USBPS2


/dev/input/event2: unused: AT Translated Set 2 keyboard


3 entries


name-source 0 USBPS2


source: added source matching <USBPS2>


name-source 0 USBPS2


source: skipping /dev/input/event0: already in use


source: added source matching <USBPS2>


list-sources


/dev/input/event0: bypass: USBPS2


/dev/input/event1: bypass: USBPS2


/dev/input/event2: unused: AT Translated Set 2 keyboard


3 entries

So now you have several options to detect input devices on your friends PC. But what if your friend decides he doesn't need the script anymore and wants it stopped ?

When your friend is running mxk interactively, this is easy, just tell him to type halt.

However, when it is running as a daemon, he needs to reattach to the running instance using the command mxk -a and then type halt. This can be made noninteractive with a command such as echo halt | mxk -a.

hi.mxk listing


#/usr/sbin/mxk -dzp


caps-source 0 key relative


caps-source 0 key relative


start-array 0


press-array 0 middlebutton h/press sync: h/release sync:


release-array 0 middlebutton i/press sync: i/release sync:


start-target 0


connect-node source:0 array:0


connect-node array:0 target:0


activate-source 0

Event Streams

The hi.mxk script sent events from a source to an array and from the array to the target. So what are these events ?

Linux evdev devices report an event as a triple of event type, code and value. For example, the enter keypress would belong to the key type, its code would be 28 (enter) and its value would be 1 (press) while the releasing the key would have a value of 0 (release).

mxk represents this triple as type:code/value. For example, relative:x/1 is an event reporting a small horizontal relative movement. Mice are examples of devices which generate relative movement events, while tablets tend to generate absolute movement events.

There are two shortcuts to be aware of:

Where the type is left out, mxk assumes a key event, so enter/press is sufficient to describe the enter keypress.

Thusfar there is only one event in the sync type, so sync: is sufficient to describe it.

The sync: event exists to group related events together. For example, relative:x/1 sync: relative:y/1 sync: moves the mouse right and then up, while relative:x/1 relative:y/1 sync: moves the mouse diagonally.

The commands


list-events


list-codes


list-relative


list-absolute

will list the known event types, key codes, relative axes and absolute axes respectively. For other event types, mxk has no symbolic names.

Note that most mxk processing nodes operate on certain event types only and tend to discard other types. In particular, many mxk processing nodes operate on keypress events only. As a matter of fact, mxk allocates an additional 256 virtual keys (virt1 to virt256) - these keys are not generated by real devices and silently discarded at the target device. They are however useful at intermediate stages. Examples of processing nodes emitting them are the timer and generic brailler nodes.

Conditional Processing

The first example script was linear - all events which were not discarded took the same path (source to array to target).

Often it is useful to have events take different paths through the processing pipeline, or rather processing graph.

mxk provides several mechanisms to split the event stream. One such facility is the branch node. It splits the input stream into two - the uporhigh branch and the downorlow branch. The node maintains a global state variable which decides if the branch is up or down (like a railway switch). In addition, the branch node also maintains a map for each key event indicating if it may be relayed in any of the states and if it changes the state of the branch.

Lets create a branch, which toggles state whenever the left alt key is pressed. When in the up state, only relay the keys h,j,k and l. In the down state relays everything


start-branch 0


key-branch 0 h pass


key-branch 0 j pass


key-branch 0 k pass


key-branch 0 l pass


key-branch 0 leftalt toggle


default-branch 0 lopass

Now check its content


list-branches


branch[0]: state=down pattern[up]=<not set>, pattern[down]=<not set> pressed=0


default action: lopass


key h: hipass lopass


key j: hipass lopass


key k: hipass lopass


key l: hipass lopass


key leftalt: up down


5 entries

This branch could be used to enter a special mode where the h,j,k and l keys are mapped to mouse movements or arrow presses and all other keys do nothing.

So how does one connect this node to its downstream nodes ? The syntax to connect-node remains unchanged, but before connecting downstream nodes, force the branch node into the desired state. The below will connect the down branch directly to the target node, while the up branch will be connected to a remapping array


down-branch 0


connect-node branch:0 target:0


up-branch 0


connect-node branch:0 target:0


down-branch 0

Note the final down branch. It ensures that the system starts in a state where all keys are relayed.

Now imagine this node being connected to a source. What happens if the alt key is struck briefly while the h key is held down ? The simple approach of sending h/press down one branch and h/release down another is almost always likely to cause problems downstream. For this reason a branch node only changes state when all keys are released. Unfortunately not all keyboard (drivers) report the release of all keys reliably, especially after holding down several keys. This bug will then cause the branch node to stay stuck in one state. To confirm this problem the used-sources command to check if your keyboard thinks keys are depressed which are not.

The branch node is a useful facility to split a stream of events into two. But what if you wish to group events into many substreams ? While it is possible to connect several branch nodes together, there exists a node which can perform this in one go. This is the sorter node. The sorter node allows you to define a set of bins, numbered from zero and assign keypress events to a particular bin. Each bin can then be connected to a different downstream node. Do note that a particular keypress always ends up at a particular bin. This behaviour differs from a branch node where a keypress can potentially be sent down different paths, depending on the internal state of the branch node. So while it is a useful analogy to think of a branch node as being a railway switch, the sorter node functions more like a post office, where a letter with the same address should always go to the same postbox.

Lets create a sorter node with three bins, where the last bin is the default one - the one which receives all events not matched elsewhere


start-sorter 0 2


match-sorter 0 a 0


match-sorter 0 b 1

This node will categorise a into bin 0, b into bin 1 and all other keypresses, including c to bin 2.

Like the branch node, downstream nodes are connected using a plain connect-node command, but only before the bin to be connected has been selected using the bin-sorter command. For example, the following will connect bin 2 to an array.


bin-sorter 0 2


connect-node sorter:0 array

There exist a few other ways of classifying input streams, however these are not covered in the tutorial.

Conclusion

This tutorial has provided a brief introduction to the commands which mxk uses to set up it processing graph. There exist several more processing nodes which have not yet been described here.

See Also

mxk(8).

DECEMBER 2008 Linux