Previous Up Next

3  Basic configuration

This chapter should help the reader configure Ion to her/his liking. As the reader probably already knows, Ion uses Lua as a configuration and extension language. If the reader is new to Lua, he might first want to read some Lua documentation as already suggested and pointed to in the Introduction before continuing with this chapter.

In particular, if ''anonymous function'' sounds or the function construct in
kpress("Mod1+1", function(s) screen_switch_nth(s, 0) end)
looks confusing to you, please consider reading some Lua documentation. Read the functions tutorial at
#1

if nothing else. Ion's stock configuration files use anonymous functions quite extensively.

Section 3.1 is an overview of the multiple configuration files Ion uses and as a perhaps more understandable introduction to the general layout of the configuration files, a walk-through of the main configuration file ion.lua is provided in section 3.2. How keys and mouse action are bound to functions is described in detail in 3.3 and in section 3.4 winprops are explained. For a reference on exported functions, see section 6.

3.1  The configuration files

Ion2, to which document applies, stores its stock configuration files in /usr/local/etc/ion/ unless you, OS package maintainer or whoever installed the package has modified the variables PREFIX or ETCDIR in system.mk before compiling Ion. In the first case you probably know where to find the files and in the other case the system administrator or OS package maintainer should have provided documentation to point to the correct location. If these instructions are no help in locating the correct directory, the command locate ion.lua might help provided updatedb has been run recently. User configuration files go in ~/.ion2/.

Ion always searches user configuration file directory before the stock configuration file directory for files. Therefore, if you want to change some setting, it is advised against that you modify the stock configuration files in-place as subsequent installs of Ion will restore the stock configuration files. Instead you should always make a copy of the stock file in ~/.ion2/ and modify this file. When searching for a file, if no extension or path component is given, compiled .lc files are attempted before .lua files.

The ''Ioncore'' window manager core and each module have their own configuration files that should be used to configure that module. The configuration files related to the ioncore main binary are as follows. The files ion.lua and draw.lua are loaded from Ioncore and the rest are included from the former.
File Description
ion.lua The main configuration file
ion-bindings.lua Most of Ion's bindings are configured here. Bindings that are specific to some module are configured in the module's configuration file. For details, see section 3.3.
ion-menus.lua Menu definitions; see section 3.6.
kludges.lua Settings to get some applications behave more nicely have been collected here. See section 3.4 for details on these ''winprops''.
draw.lua This file is a link to or copy of one of the look-*.lua style files. It should load a drawing engine and configure a style for it; for details see chapter 4.

Each (non-drawing engine) module has in addition its own configuration file loaded when that module is loaded:
File Description
ionws.lua Configuration file for the ionws module. Bindings specific to the workspace and frame classes implemented by this module are configured here.
floatws.lua Configuration file for the floatws module. Bindings specific to the workspace and frame classes implemented by this module are configured here.
query.lua Configuration file for the query module. Bindings to edit text in the queries and some other bindings related to queries and messages are defined here.
menu.lua Configuration file for the menu module. Bindings to navigate menus are defined here. Actual menus are (in the stock configuration file setup) defined in ion-menus.lua as mentioned above.

Some of the files contain references to the files querylib.lua and menulib.lua These are installed in SHAREDIR (/usr/local/share/ion/ by default) among some other other .lua files that are an essential part of Ion's code. Users who only want to change a few settings should not need to modify the files in this directory. Nevertheless, it is possible to override the files in SHAREDIR as it is on the search path after ~/.ion2 and ETCDIR.

There is one extra file in SHAREDIR that you may find useful and that is not loaded by default. This is compat.lua and it contains some wrapper functions for backwards compatibility to make the process of updating Ion a little less painful. If you have have modified configuration files that use some features no longer available in the latest Ion, you may just load this file (with include("compat.lua") at the beginning of ion.lua, for example) instead of immediately updating your configuration files. However, the wrappers will be removed eventually (maybe about two months after adding them depending on the rate of new releases) so you should nevertheless update your configuration files before this happens.

3.2  A walk through ion.lua

As already mentioned ion.lua is Ion's main configuration file. Some basic 'feel' settings are usually configured there and the necessary modules and other configuration files configuring some more specific aspects of Ion are loaded there. In this section we take a walk through the stock ion.lua.

The first thing that is done in that file is set
DEFAULT_MOD = "Mod1+"
This causes most of Ion's key bindings to use Mod1 as the modifier key; for details on modifiers and key binding setup in general see section 3.3.

Next there are the commented-out delay settings
-- set_dblclick_delay(250)
-- set_resize_delay(1500)
The first of these settings is the maximum interval in milliseconds between two mouse button presses for the second press to be actually considered a double-click. The latter setting sets the delay, again in milliseconds, after which Ion will automatically terminate one of the keyboard resize modes if nothing has happened.

The setting
enable_opaque_resize(false)
says that a XOR rubber band should be shown when moving or resizing frames. This will, unfortunately, cause Ion to also grab the X server and has some side effects. If your computer is fast enough and you prefer so-called ''opaque resize'' mode where the frame is being resized already during the resize action you may set the parameter to true.

The following settings controls whether Ion will ''warp'' the (mouse) pointer to an object whenever it is focused from the keyboard.
enable_warp(true)
Some people may consider this annoying so setting the parameter to false can be used to disable this feature and have Ion never move the pointer.

Next the stock ion.lua has the include-statements
include("kludges")
include("ion-bindings")
include("ion-menus")
The first of these loads the file kludges.lua that contains some ''winprop'' settings used to make some applications behave a little better under Ion. For details see section 3.4. The second include statement loads file file ion-bindings.lua which contains the statements to configure most of Ion's bindings; modules' bindings are configured in their respective configuration files. The final include statement load menu definitions.

Next we have quite a few statements of the form
add_shortenrule("[^:]+: (.*)(<[0-9]+>)", "$1$2$|$1$<...$2")
These are used to configure how Ion attempts to shorten window titles when they do not fit in a Tab. The first argument is a POSIX regular expression that is used to match against the title and the next is a rule to construct a new title of a match occurs. This particular rule is used to shorten e.g. 'Foo: barbaz<3>' to 'barba...<3>'; for details see the function reference entry for add_shortenrule.

The setting
query_man_path = {
    "/usr/man",
    "/usr/share/man",
    "/usr/X11R6/man",
    "/usr/local/man"
}
is used to configure where the querylib.query_man query (F1) looks for man pages to tab-complete.

Finally we load the modules.
load_module("query")
load_module("menu")
load_module("ionws")
load_module("floatws")
The first of the modules provides the queries, the mini buffer-like line-editor boxes that appear at bottoms of frames and screens and that can be and are used to to do quite a few things including starting programs and navigating to different windows by their name. The 'menu' module provides both pop-up and query-style in-frame menus. The third module loaded provides the normal Ion-style tiled workspaces and frames of that style while the 'floatws' module provides traditional ''free-floating'' WIMP workspaces and frames in the PWM style.

As already mentioned, each of these modules have their own configuration files modulename.lua that configure things that can only be configured if the module is loaded. These settings are mostly just the bindings that are specific to the classes provided by the module.

At the moment there are no other modules than the above three. Any of these modules may be removed; Ion is able to operate without any modules in full-screen only mode, but if you do so, you might first want to reconfigure some of the bindings. Ion will, however, complain of broken workspace save files if starting a module-less configuration over an old session.

3.3  Keys and rodents

In the stock configuration file setup, most key and mouse bindings are set from the file ion-bindings.lua while module-specific bindings are set from the modules' main configuration files (modulename.lua). This, however, does not have to be so as long as the module has been loaded prior to defining any module-specific bindings.

The bindings are defined by calling various functions that add a new set of bindings to a binding group, usually related to some object class. These functions, descriptions of their contexts and parameters passed to ''bindings handlers'' are listed in the following subsection. Each of the functions listed there has a single argument: a table listing the keys and mouse actions to bind functions to. Section 3.3.2 describes how the binding tables can be constructed.

Note that when multiple objects want to handle a binding, the innermost (when the root window is considered the outermost) active object in the parent--child hierarchy (see Figure 2.2) of objects gets to handle the action.

3.3.1  The binding setup functions

This section simply lists out the different binding setup functions Ioncore and the modules provide and does not even attempt to describe how to use these functions; for that refer to 3.3.2 and following sections.

There has been some confusion among users about the need for multiple, partially even overlapping functions to setup bindings so let me try to explain this design decision here.

The thing is that if there was a just a single 'bind this key to this function' method to bind keys, some limitations would have to be made on the available functions and writing custom functions would be more complicated. In addition one may want to bind the same function to different key for different types of objects. Indeed, the workspace and frame tab switching functions are the same both classes being based on WMPlex, and in the stock configuration the switch to n:th workspaces is bound to Mod1+n (using global_bindings explained below) while the switch to n:th tab is bound to the sequence Mod1+k n (using genframe_bindings and submap).

So, in brief this little additional complexity in configuration is the price of flexibility.

Ioncore binding setup functions

The following binding setup functions are defined by Ioncore and in the stock configuration file setup, they are set from the file ion-bindings.lua:
Function Handler parameters and description
global_bindings Parameters to handler: WScreen
Description: Bindings that are available all the time.
mplex_bindings Parameters to handler: WMPlex
Description: Bindings that are common to all WMPlexes (screens and frames). Usually only bindings that affect current client window (if any) are set here.
genframe_bindings Parameters to handler: WGenFrame, [WRegion]
Description: Bindings that are common to all types of frames (but not screens unlike mplex_bindings above. When a tab has been pressed the WRegion corresponding to the tab is passed as the extra parameter.

IonWS module binding setup functions

The following binding setup functions are defined by the IonWS module and in the stock configuration file setup the bindings are set in ionws.lua:
Function Handler parameters and description
ionws_bindings Parameters to handler: WIonWS, WRegion
Description: Bindings that are available on the tiled workspaces implemented by this module. The extra parameter to binding handler is the currently active object on the workspace.
ionframe_bindings Parameters to handler: WIonFrame, [WRegion]
Description: Bindings that are specific to the tiled frames. As above, when a tab has been pressed the WRegion corresponding to the tab is passed as the extra parameter.
ionframe_moveres_bindings Parameters to handler: WIonFrame
Description: Resize mode bindings. Activated by calling WIonFrame.begin_resize. Only certain functions may be called here; see the function reference for details.

FloatWS module binding setup functions

These functions are similar to the ones described in the above section for the FloatWS module. The bindings are defined in the configuration file floatws.lua:
Function Handler parameters and description
floatws_bindings Parameters to handler: WFloatWS, [WRegion]
Description: Bindings that are available on the conventional workspaces implemented by this module. The extra parameter to binding handler is the currently active object on the workspace, if any.
floatframe_bindings Parameters to handler: WFloatFrame, [WRegion]
Description: Bindings that are specific to the the conventional floating frames.
floatframe_moveres_bindings Parameters to handler: WIonFrame
Description: Keyboard move/resize mode bindings. Activated by calling the function WFloatFrame.begin_resize. Only certain functions may be called here; see the function reference for details.

Query module binding setup functions

These functions set the bindings for the query module. The bindings are set from the file query.lua.
Function Handler parameters and description
input_bindings Parameters to handler: WInput
Description: bindings that are common to message and query boxes; stuff to close the box and to scroll message or completions.
query_bindings Parameters to handler: WEdln
Description: Bindings to edit text and finish the query.

Menu module binding setup functions

These functions set the bindings for the menu module. The bindings are set from the file menu.lua.
Function Handler parameters and description
menu_bindings Parameters to handler: WMenu
Description: key bindings available in in-frame menus.

3.3.2  Defining the bindings

Each of the functions listed above has a single argument: a table listing the key presses and other actions to be bound to functions. The descriptions of individual bindings in this table are also tables that can be more conveniently constructed with the following functions:

Key presses: Mouse actions: The actions that most of these functions correspond to should be clear and as explained in the reference, kpress_waitrel is simply kpress with a flag set instructing Ioncore wait for all modifiers to be released before processing any further actions. This is to stop one from accidentally calling e.g. WRegion.close multiple times in a row. The submap function is used to define submaps or ''prefix maps''. The second argument to this function is table listing the key press actions (kpress) in the submap

The parameters keyspec and buttonspec are explained below in detail. The parameter func is the handler for the binding. It is a reference to a function that should expect as parameter(s) objects of the type defined in the above tables depending on which binding setup function this binding definition is being passed to.

The optional string parameter area may be used to specify a more precise location where the mouse action should occur for the binding to take place. Currently only ionframe_bindings and floatframe_bindings support any meaningful values for this parameter. The allowed values are in this case "border", "tab", "empty_tab", "client" and nil (for the whole frame).

3.3.3  An example

For example, to just bind the key Mod1+1 to switch to the first workspace and Mod1+Right to the next workspace, you would make the following call
global_bindings{
    kpress("Mod1+Right", WScreen.switch_nth),
    kpress("Mod1+1", function(scr) scr:switch_nth(1) end),
}
Recall that global_bindings{...} is syntactical sugar for the more cumbersome global_bindings({...}).

The first definition works, because WScreen.switch_next (inherited WMPlex.switch_next) is a function that takes a WScreen as its sole parameter. On the other hand, WScreen.switch_nth (again inherited WMPlex.switch_nth) expects two parameters: the screen and the n giving the number of the workspace to switch to so it could not be directly passed to kpress. Therefore we had to write around it our ''anonymous'' wrapper function around that takes a single parameter and calls switch_nth properly. (Recall that scr:switch_nth(1) is syntactic sugar for WScreen.switch_nth(scr, 1). Alternatively we could have defined function switch_1(scr) scr:switch_nth(1) end before the call to global_bindings and passed switch_1 to kpress instead of the anonymous function.)

3.3.4  Another example

Similarly to the above example, to bind the key sequence Mod1+k n switch to the next managed object within a frame, and Mod1+k 1 to the first, you would issue the following call:
genframe_bindings{
    submap("Mod1+k", {
        kpress("n", WGenFrame.switch_nth),
        kpress("1", function(frame) frame:switch_nth(1) end),
    }),
}
Note that the switching functions being used in this example are exactly the same as in the previous example. Both WScreen and WGenFrame inherit them from WMPlex.

3.3.5  Key and button specifications

As seen above, the functions that create key binding specifications require a keyspec argument. This argument should be a string containing the name of a key as listed in the X header file keysymdef.h1 without the XK_ prefix. Most of the key names are quite intuitive while some are not. For example, the Enter key on the main part of the keyboard has the less common name Return while the one the numpad is called KP_Enter.

The keyspec string may optionally have multiple ''modifier'' names followed by a plus sign (+) as a prefix. X defines the following modifiers:
Shift, Control, Mod1 to Mod5, AnyModifier and Lock.
X allows binding all of these modifiers to almost any key and while this list of modifiers does not explicitly list keys such as Alt that are common on modern keyboards, such keys are bound to one of the ModN. On systems running XFree86 Alt is usually Mod1. On Suns Mod1 is the diamond key and Alt something else. One of the ''flying window'' keys on so called Windows-keyboards is probably mapped to Mod3 if you have such a key. Use the program xmodmap to find out what exactly is bound where. AnyModifier is usually used in submaps to indicate that it doesn't matter which modifier keys are pressed, if any.

Ion ignores the Lock modifier and any ModN modifiers bound to NumLock or ScrollLock by default because such2 locking keys may otherwise cause confusion.

Button specifications are similar to key definitions but now instead of specifying modifiers and a key, you specify modifiers and one of the button names Button1 to Button5.

3.3.6  A further note on the default binding configuration

The variable DEFAULT_MOD in the above listing defaults to "Mod1+" and is set in ion.lua. Changing this variable allows to easily change the the modifier used by all bindings in the default configuration that use modifiers. Quite a few people prefer to use the Windows keys as modifiers because many applications already use Alt. Nevertheless, Mod1 is the default as a key bound to it is available virtually everywhere.

3.3.7  Client window bindings

As client windows do not have function to set their bindings, it is necessary to call client window functions by specifying the bindings somewhere else. In the stock configuration file setup this is done in mplex_bindings by functions that look up the object currently displayed by the WMPlex (WMPlex.current). We then check that it is of type WClientWin to suppress warning and then call the wanted function with the verified client window as argument.

To make it easier to write such bindings, the function make_mplex_clientwin_fn is used to construct this wrapper function. The following two binding definitions are essentially equivalent:
mplex_bindings {
    kpress("Mod1+Return", function(mplex, r)
                              if not r or r==mplex
                                  r=mplex:current()
                              end
                              if obj_is(r, "WClientWin") then
                                  r:toggle_fullscreen()
                              end
                          end),
    kpress("Mod1+Enter",
           make_mplex_clientwin_fn(WClientWin.toggle_fullscreen)),
}

3.4  Winprops

3.4.1  Classes, roles and instances

The so-called ''winprops'' can be used to change how specific windows are handled and to set up some kludges to deal with badly behaving applications. They are defined by calling the function winprop with a table containing the properties to set and the necessary information to identify a window. This identification information is more specifically the class, role and instance name of the window. The name field is a Lua-style regular expression matched against the window's title and the rest are strings that must exactly much the corresponding window information. It is not necessary to specify all of these fields.

Ion looks for a matching winprop in the order listed by the following table. An 'E' indicates that the field must be set in the winprop and it must match the window's corresponding property exactly or, in case of name, the regular expression must match the window title. An asterisk '*' indicates that a winprop where the field is not specified (or is itself an asterisk in case of the first three fields) is tried.
class role instance name
E E E E
E E E *
E E * E
E E * *
E * E E
E * E *
E * * E
: : : etc.

If there are multiple winprops with other identification information the same but different name, the longest match is chosen.

3.4.2  Finding window identification with xprop

To get the identification information required for winprops, in case of normally framed windows you may use the command xprop WM_CLASS and click on the particular window of interest. The class is the latter of the strings while the instance is the former. To get the role -- few windows have this property -- use the command xprop WM_ROLE.

So-called ''transient windows'' are usually short-lived dialogs (although some programs abuse this property) that have a parent window that they are ''transient for''. On tiled workspaces Ion displays these windows simulatenously with the parent window at the bottom of the same frame. Unfortunately xprop is stupid and can't cope with this situation, returning the parent window's properties when the transient is clicked on. For this reason you'll have to do a little extra work to get the properties for that window.3

If you can guess the title of the transient, the simplest solution is to use the Mod1+A query (querylib.query_attachclient) to attach it directly to a frame. Another easy solution is to create a WFloatWS and run the program for this once. A little more complicated solution is to run the following code in the Mod1+F3 (querylib.query_lua) Lua code execution query, assuming there's only one transient (all on one line):
local id=_:current():managed_list()[1]:get_ident();
query_message(_, id.class..'.'..id.instance);
Role and name can be retrieved similarly (see the documentation for WClientWin.get_ident or WRegion.name). Role may not always be set.

Finally, it should be mentioned that too many authors these days ''forget'' to set this vital identification to anything meaningful: everything except name is the same for all of the programs's windows, for example.

3.4.3  Supported winprops

Ion currently knows the following winprops:


Property Type Description
switchto boolean Should a newly mapped client window be switched to within its frame.
jumpto boolean Should a newly created client window always be made active, even if the allocated frame isn't.
transient_mode string "normal": No change in behaviour. "current": The window should be thought of as a transient for the current active client window (if any) even if it is not marked as a transient by the application. "off": The window should be handled as a normal window even if it is marked as a transient by the application.
target string The name of an object (workspace, frame) that should manage windows of this type.
transparent boolean Should frames be made transparent when this window is selected?
acrobatic boolean Set this to true for Acrobat Reader. It has an annoying habit of trying to manage its dialogs instead of setting them as transients and letting the window manager do its job, causing Ion and acrobat go a window-switching loop when a dialog is opened.
max_size table The table should contain the entries w and h that override application-supplied maximum size hint.
aspect table The table should contain the entries w and h that override application-supplied aspect ratio hint.
ignore_resizeinc boolean Should application supplied size increments be ignored?
fullscreen boolean Should the window be initially in full screen mode?
ignore_cfgrq boolean Should configure requests on the window be ignored? Only has effect on windows on floatws:s.
transients_at_top boolean When transients are managed by the client window itself (as it is the case on tiled workspaces), should the transients be placed at the top of the window instead of bottom?

3.4.4  Some common examples

Acrobat Reader

The following is absolutely necessary for Acrobat reader:
winprop{
    class = "AcroRead",
    instance = "documentShell",
    acrobatic = true,
}

Fixing a Mozilla Firebird transient

Mozilla Firebird (0.7) incorrectly does not set the WM_TRANSIENT_FOR property for the dialog that is used to ask the action to take for a file. It, however, sets the the property point to the main window for the save dialog. This can be annoying and confusing, as the first dialog is not closed before the second is displayed.

We'd like the first dialog to be transient to the main window. The closest we can get to that is to consider it transient to the current window (if there's one). Unfortunately Firebird does not set any meaningful classes, instances or roles for the windows, so we'll have to rely on an ugly title match.
winprop{
    class = "MozillaFirebird-bin",
    name = "Opening .*",
    transient_mode = "current",
}

Forcing newly created windows in named frames

The following winprop should place xterm started with command-line parameter '-name sysmon' and running a system monitoring program in a particular frame:
winprop{
    class = "XTerm",
    instance = "sysmon",
    target = "sysmonframe",
}
For this example to work, we have to somehow create a frame with the name sysmonframe. One way to do this is to make the following call in the Mod1+F3 Lua code query:
querylib.query_renameframe(_)
Recall that _ points to the multiplexer (frame or screen) in which the query was opened. Running this code should open a new query prefilled with the current name of the frame. In our example we would change the name to sysmonframe, but we could just as well have used the default name formed from the frame's class name and an instance number.

3.5  The query library

The query module does not implement any queries in itself, but provides the function query_query to execute arbitrary queries. Some standard queries implemented with this interface are available in 'querylib' (SHAREDIR/querylib.lua). Most of these queries support tab-completion and can be directly passed to the binding setup functions for classes based on WMPlex. These setup functions are at the moment: The default configuration puts most queries in genframe_bindings while the exit and restart queries are in global_bindings. For a listing of the functions, see section 6.9 in the Function reference.

Querylib also provides functions to generate more queries; for details see the script.

3.6  Menus

3.6.1  Defining menus

In the stock configuration file setup, menus are configured in the file ion-menus.lua as previously mentioned. An example of a definition of a rather simple menu with a submenu is:
include("menulib")

defmenu("exitmenu", {
    menuentry("Restart", restart_wm),
    menuentry("Exit", exit_wm),
})

defmenu("mainmenu", {
    menuentry("Lock screen", make_exec_fn("xlock")),
    menuentry("Help", querylib.query_man),
    submenu("Exit", "exitmenu"),
})
The menulib library must be loaded for some of the functions discussed here to be available.

The defmenu function is used to define a named menu that can later be accessed with this name. The menuentry function is used to create an entry in the menu with a title and an entry handler function to be called when the menu entry is activated. If the functions discussed in subsection 3.6.3 are used to display the menu from a binding, the parameters that are passed to the function are those that the binding handler was passed. It is as if the function was called from that binding.

The submenu function is used to insert a submenu at that point in the menu. (One could as well just pass a table with the menu entries, but it is not encouraged.)

3.6.2  Special menus

The menulib library predefines the following special menus. These can be used as the menus defined as above.
Menu name Description
windowlist List of all client windows. Activating an entry jumps to that window.
workspacelist List of all workspaces. Activating an entry jumps to that workspaces.
stylemenu List of available look-*.lua style files. Activating an entry loads that style and ask to save the selection.

3.6.3  Displaying menus

Menus defined with the routines described in the previous subsection should be bound to key and pointer actions by creating a bindable function with one of the following routines: make_menu_fn, make_bigmenu_fn or make_pmenu_fn. The first two create functions to display in-frame (or in-mplex more generally) menus that appear on the bottom-left corner of the WMPlex where the bound action occurred. The difference between the two is the different drawing engine style used. The last function creates a pop-up menu display function and can only be bound to mouse press actions.

An example of a binding to display a menu is:
global_bindings{
    kpress("F12", make_bigmenu_fn("mainmenu")),
}
The low-level functions menu_menu and menu_pmenu can also be used to display menus with different kinds of handlers and so on, but most users should not need to be concerned with these. If you use these functions note that they do not call the menu entry handlers but pass the entry to a specified handler the responsibility of which it is to decide what to do.

3.7  Some common configuration tasks

3.7.1  Binding a key to execute a program

Because the exec function immediately executes the argument string, we must wrap this function in the bindings in the following way:
    kpress("SomeKey", function() exec("program --param") end)
The make_exec_fn function can be used as a convenience; the above is equivalent to
    kpress("SomeKey", make_exec_fn("program --param"))

1
This file can usually be found in the directory /usr/X11R6/include/X11/.
2
Completely useless keys that should be gotten rid of in the author's opinion.
3
There's a patch to xprop to fix this, but nothing seems to be happening with respect to including it in XFree86.

Previous Up Next