3.3. The anatomy of bindings

A binding is basically a pairing of two things: an event, and an action. Together, they form a coherent instruction to the window manager, along the lines of: "when this happens, do that."

3.3.1. Events

The event is generally triggered by something outside of the window manager, usually the mouse or keyboard. Some examples are DoubleClick (the familiar double-clicking of a mouse button), KeyPress (the pressing of a key on the keyboard), and EnterNotify (when the mouse pointer enters a window's area).

An event generally excepts one or two arguments. If the event involves a mouse or key press, then it will accept two arguments: the modifier, and the key/mouse button. If an event does not involve a key/mouse (e.g. EnterNotify), then it accepts only one argument; the modifier. In the former case, the modifier comes before the key/mouse button, and in either case, the modifier may be omitted. To use more than one modifier, simply add them together. To specifically require a modifier to not be part of a binding, subtract it. Here are some examples of valid events, in Python:

3.3.1.1. Keys

Keyboard keys are specified in a string, using the standard X name for that key. To find the name of a key, use the xev(1) utility that comes with XFree86. Just start up xev, start typing in keys, and it'll tell you all sorts of information about them.

Key names are about as straightforward as you'd expect; most keys go by the name that you call them. The backspace key is BackSpace, the tab key is Tab, the letter "a" is a, and the number one is 1. Some keys, such as keypad keys, and "multimedia" keys, will have somewhat less predictable names. Note that key names are case-insensitive!

Alternatively, you can use keycodes for keys that don't have a name. Many times, you may have a multimedia keyboard where some or all of the multimedia keys do not have names -- but every key has a keycode (well, as long as X actually realizes the key is there :). To specify a keycode, just enter the keycode itself where the key name goes, without quotes (i.e. as an integer, not a string). The keycode is obtained the same way as the key name: with xev.

Example 3-1. Example uses of keynames

KeyPress(Alt, "A")             # letter A
KeyPress(Alt, "a")             # same thing
KeyPress(Alt, "KP_Add")        # plus sign on keypad
KeyPress(Alt, "F1")            # F1 key
KeyPress(Alt, "XF86AudioPlay") # multimedia "play" key
KeyPress(Alt, 142)             # whatever key has keycode 142
KeyPress(Alt, "Control_L")     # works!

3.3.1.2. Mouse Buttons

Mouse buttons are even simpler, since there are relatively so few of them. The names of the buttons are in the form of ButtonX, where X is the number of the button. Your left (usually) mouse button is Button1, and your right mouse button is Button3. If you have a wheel, then pressing it should be Button2, scrolling it up should be Button4, and scrolling down should be Button5. Buttons are given in strings, just like key names. Thus, when you actually type in a button name, you would put quotes around it.

3.3.1.3. Modifiers

A modifier is different from a normal key in that it is designed to be held in conjunction with normal keys, to modify their meaning. The modifier that you use all of the time is the Shift key. Others include the Control key, the Alt key, and the Super AKA Windows key. These four main modifiers also have shorthand counterparts, C, S, A, and W, representing Control, Shift, Alt, and Super, respectively.

As previously mentioned, you can use normal addition and subtraction operations to specify arbitrary combinations of modifiers; thus -Ctrl+Alt-Super is perfectly valid (it would mean "alt and not control and not super"). Unlike key and mouse button names, modifiers are predefined objects, and you can use them as such, instead of quoting modifier keynames. However -- you can also use the keynames in strings, if you need or want to. "Control_L" is just as good as Ctrl, if it's by itself. You can't, however, use the addition and subtraction operators like you would with modifier objects, since you'd just be concatenating/subtracting strings, and it would basically just bomb on you at one point or another, since it wouldn't make any sense. But if you really do need to use a string name for a modifier, and want to be able to add/subtract it with other modifiers, there is a way. If you want to add "Control_L" with "BobsModifier_L", you would do Modifiers.fromString("Control_L") + Modifiers.fromString("BobsModifier_L"). However, that's digging deeper at the scripting interface, so no guarantees that that will always work out of the box (although at present, it should work just fine).

3.3.2. Actions

The action is the actual course of action to take when the corresponding event happens. An action can be a few different things:

3.3.2.1. Builtin Actions With No Arguments

This is simply an action in the Kahakai core, that has no need for extra information to be passed to it. This action would simply be a string with the name of the (case sensitive!) action in it. An example would be "Close" (to close the focused window), or "{xterm}" (to execute an xterm - shell commands are put in curly braces).

3.3.2.2. Builtin Actions With Arguments

To call a builtin action with an argument, you enclose the two in a sequence (in Python - a tuple or list). The first item is the action name in a string, and the second item is the argument, also in a string. However, if the argument is an integer, it can be a normal integer, not enclosed in quotes. Some examples:

  • ("GoToDesktop", 0)

  • ("ViewportRelativeMove", "+100+200")


3.3.2.3. Functions within the scripts

You can create your own functions within your scripts, and register them to events, to be called when those events occur. The only difference is that instead of putting the action name into a string, you actually pass a reference to your function. For example, if you created a function called growByTwentyPx(), and wanted to assign it to Super+G, you would create a binding like this:

(KeyPress(Super, "G"), growByTwentyPx)

You can also pass arbitrary arguments to your own callback functions, in basically the same way you would do so with builtin actions. Say you had the following function:

def printSomething(window, event, action):
print action.param

And now you want to Ctrl+H to print "Hello, World!", and Ctrl+F to say "F U!". You would use the following bindings:

(KeyPress(Ctrl, "H"), (printSomething, "Hello, World!")),
(KeyPress(Ctrl, "F"), (printSomething, "F U!"))

Notice the arguments to printSomething(); window, event, and action: These arguments are passed to all functions that you call from bindings. The window argument is either the currently focused window, or the window involved in the event. For example, if you assigned a binding to a double click event on the root window, then the window argument would be the root window. You can do a lot of useful things with the window. Some useful examples:

def halfScreenWidth(win, ev, act):
  """Sets the window's width to ((screen width / 2) - 1)"""

  # the internal names of the parameters are irrelevant, you
  # could name them poop, pizza, and lightning, if you wanted.
  # in fact, i think you do...

  win.SetFrameWidth((screen.width/2)-1)
  win.RedrawWindow()

def moveToLeft(poop, pizza, lightning):
  """Moves windo^H^H^H^H^Hpoop to left screen edge"""
  poop.SetFrameLeft(0)
  poop.RedrawWindow()

def grow(win, ev, act):
  """Grows a window by 20 pixels in all four directions"""
  win.SetFrameLeft(win.GetFrameLeft()-20)
  win.SetFrameTop(win.GetFrameTop()-20)
  win.SetFrameWidth(win.GetFrameWidth()+40)
  win.SetFrameHeight(win.GetFrameHeight()+40)
  win.RedrawWindow()

The event argument is the event object representing the event which triggered the binding to be called. Generally you won't have much use for this (if you can think of one, let us know :). The action argument is the action part of the binding used to call your function, and its param attribute is the argument you (may have) passed to your function. In my examples above, the "Hello, World!" and "F U!" strings are contained in action.param.

There are some additional things to note about user-defined functions:

  • Your functions should not block! If they do, then the entire window manager will be blocked, including redraws and the whole deal. Not purty.

  • Threads don't work with Kahakai! Don't try to be clever and spawn a thread to do something, because it basically just won't work. Swig is probably the culprit, but no one has really looked into it. If you can figure any more out regarding threads, please let us know!