3 Basic configuration
This chapter should help your configure Ion to your liking. As the your
probably already know, Ion uses Lua as a configuration and extension
language. If you're new to it, you might first want to read some Lua
documentation as already suggested and pointed to in the Introduction
before continuing with this chapter.
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.5 winprops are
explained. For a reference on exported functions, see section
6.
3.1 The configuration files
Ion3, to which document applies, stores its stock configuration files in
/usr/local/etc/ion3/ unless you, the OS package maintainer or
whoever installed the package on the system 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 the 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 cfg_ion.lua might help provided updatedb
has been run recently.
User configuration files go in ~/.ion3/.
Ion always searches the 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 ~/.ion3/ 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.
All the configuration files are named cfg_*.lua with the ”*”
part varying. The configuration file for each module mod_modname is
cfg_modname.lua, with modname varying by the module in
question. The following table summarises these and other configuration
files:
|
File |
Description |
|
cfg_ion.lua |
The main configuration file |
cfg_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. |
cfg_menus.lua |
Menu definitions; see section 3.4. |
cfg_kludges.lua |
Settings to get some applications behave more nicely have been
collected here. See section 3.5. |
cfg_ionws.lua
cfg_floatws.lua
cfg_panews.lua
cfg_query.lua
cfg_menu.lua
cfg_dock.lua
cfg_statusbar.lua
... |
Configuration files for different modules. |
Additionally, there's look.lua that configures the drawing engine,
but it is covered in chapter 4.
3.2 A walk through cfg_ion.lua
As already mentioned cfg_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 cfg_ion.lua.
The first thing that is done in that file is set
MOD1="Mod1+"
MOD2=""
This causes most of Ion's key bindings to use Mod1 as the
modifier key. If MOD2 is set, it is used as modifier for the keys
that don't normally use a modifier. for details on modifiers and key
binding setup in general see section 3.3.
Next we do some basic feel configuration:
ioncore.set{
dblclick_delay=250,
kbresize_delay=1500,
}
These two will set the delay between button presses in a double click, and
the timeout to quit resize mode in milliseconds.
ioncore.set{
opaque_resize=true,
warp=true,
}
The first of these two settings enables opaque resize mode: in move/resize
move frames and other objects mirror you actions immediately. If opaque
resize is disabled, a XOR rubber band is shown during the mode instead.
This will, unfortunately, cause Ion to also grab the X server and has some
side effects.
ioncore.set{
default_ws_type="WIonWS",
}
This will set the default workspace type to WIonWS – tiled workspaces.
To actually be able to do something besides display windows in full screen
mode, we must next load some modules:
dopath("mod_query")
dopath("mod_menu")
dopath("mod_ionws")
dopath("mod_floatws")
dopath("mod_panews")
dopath("mod_statusbar")
--dopath("mod_dock")
--dopath("mod_sp")
As already mentioned, each of these modules have their own configuration
files. Finally, additional configuration files are loaded:
dopath("cfg_kludges")
dopath("cfg_bindings")
dopath("cfg_menus")
Bindings and menus are defined in cfg_bindings.lua and
cfg_menus.lua. Details on making such definitions follow in
sections 3.3 and 3.4, respectively.
some kludges or ”winprops” to make some applications behave better
under Ion are colledted in cfg_kludges.lua; see section
3.5 for details. In addition to these, this file
lists quite a few statements of the form
ioncore.defshortening("[^:]+: (.*)(<[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 ioncore.defshortening.
3.3 Keys and rodents
In the stock configuration file setup, most key and mouse bindings are set
from the file cfg_bindings.lua while module-specific bindings
are set from the modules' main configuration files (cfg_modname.lua).
This, however, does not have to be so as long as the module has been
loaded prior to defining any module-specific bindings.
Bindings are defined by calling the function
ioncore with the ”context” of the
bindings and the a table of new bindings to make. The context is simply
string indicating one of the classes of regions (or modes such as
WMoveresMode) introduced in section 2.2, and fully
listed in appendix B, although not all define
a binding map. For example, the following skeleton would be used to
define new bindings for all frames:
defbindings("WFrame", {
-- List of bindings to make goes here.
})
There has been some confusion among users about the need to define the
”context” for each binding, so let me try to explain this design
decision here. The thing is that if there was a just a simple 'bind this
key to this action' method without knowledge of the context, some
limitations would have to be made on the available actions and writing
custom handlers 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 while the switch to
n:th tab is bound to the sequence Mod1+k n.
The following subsections describe how to construct elements of the
binding table. Note that ioncore adds
the the newly defined bindings to the previous bindings of the context,
overriding duplicates. To unbind an event, set the handler parameter
to nil for each of the functions to be described in the following
subsections.
Also 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 Binding handlers and special variables
Unlike in Ion2, in Ion3 binding handlers are not normally passed as
”anonymous functions”, although this is still possible. The preferred
method now is to pass the code of the handler as a string. Two special
variables are available in this code. These are
|
Variable |
Description |
|
_ (underscore) |
Reference to the object on which the
binding was triggered. The object is of the same class as the the
context of the ioncore call
defining the binding. |
_sub |
Usually, the currently active child of the
object referred to by _, but sometimes (e.g. mouse actions
on tabs of frames) something else relevant to the action triggering
the binding. |
For example, supposing '_' is a WFrame, the following
handler should move the active window to the right, if possible:
"_:inc_index(_sub)"
To suppress error messages, each binding handler may also be accompanied
by a ”guard” expression that blocks the handler from being called when
the guard condition is not met. Currently the following guard expressions
are supported:
|
Guard |
Description |
|
"_sub:non-nil" |
The _sub parameter must be set. |
"_sub:SomeClass" |
The _sub parameter must be member
of class SomeClass. |
3.3.3 Defining the bindings
The descriptions of the individual bindings in the binding table argument
to ioncore should be constructed with the following
functions.
Key presses:
-
kpress(keyspec, handler [, guard]),
- kpress_wait(keyspec, handler [, guard]) and
- submap(keyspec, { ... more key bindings ... }).
Mouse actions:
-
mclick(buttonspec, handler [, guard]),
- mdblclick(buttonspec, handler [, guard]),
- mpress(buttonspec, handler [, guard]) and
- mdrag(buttonspec, handler [, guard]).
The actions that most of these functions correspond to should be clear
and as explained in the reference, kpress_wait 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.rqclose 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 handler is the handler for the binding,
and the optional parameter guard its guard. These should normally
be strings as explained above.
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
defbindings("WScreen", {
kpress("Mod1+Right", "_:switch_next()"),
kpress("Mod1+1", "_:switch_nth(1)"),
})
Note that _:switch_nth(1) is the same as calling
WMPlex.switch_next(_, 1) as WScreen inherits
WMPlex and this is where the function is actually defined.
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:
defbindings("WFrame", {
submap("Mod1+K", {
kpress("Right", "_:switch_next()"),
kpress("1", "_:switch_nth(1)"),
}),
})
3.3.5 Key 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.
Ion defaults to AnyModifier in submaps. This can sometimes lead to
unwanted effects when the same key is used with and without explicitly
specified modifiers in nested regions. For this reason, Ion recognises
NoModifier as a special modifier that can be used to reset this
default.
Ion ignores the Lock modifier and any ModN (N=1… 5)
bound to NumLock or
ScrollLock
by default because such2 locking keys may otherwise
cause confusion.
3.3.6 Button specifications
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. Additionally the
specification may end with an optional area name following an @-sign.
Only frames currently support areas, and the supported values in this
case are
"border", "tab", "empty_tab", "client" and
nil (for the whole frame).
For example, the following code binds dragging a tab with the first
button pressed to initiate tab drag&drop handling:
defbindings("WFrame", {
mdrag("Button1@tab", "_:p_tabdrag()"),
})
3.3.7 A further note on the default binding configuration
The default binding configuration contains references to the variables
MOD1 and MOD2 instead of directly using the default
values of "Mod1+" and "" (nothing). As explained in
section 3.2, the definitions of these variables
appear in cfg_ion.lua. This way you can easily change the the
modifiers used by all bindings in the default configuration without
changing the whole binding configuration. 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.8 Client window bindings
As client windows do not have a binding map of their own due to technical
reasons, it is necessary to call client window functions by specifying the
bindings somewhere else. In the stock configuration file setup this is done
among WMPlex bindings, setting the guard to _sub:WClientWin
and using _sub to refer to the client window.
For example, the full screen toggle key is bound like this:
defbindings("WMPlex", {
kpress_wait("Mod1+Return",
"_:toggle_fullscreen()", "_sub:WClientWin"),
})
3.4.1 Defining menus
In the stock configuration file setup, menus are defined in the file
cfg_menus.lua as previously mentioned. The mod_menu module
must be loaded for one to be able to define menus, and this is done with
the function mod_menu provided by it.
Here's an example of the definition of a rather simple menu with a submenu:
defmenu("exitmenu", {
menuentry("Restart", "ioncore.restart()"),
menuentry("Exit", "ioncore.shutdown()"),
})
defmenu("mainmenu", {
menuentry("Lock screen", "ioncore.exec('xlock')"),
menuentry("Help", "mod_query.query_man(_)"),
submenu("Exit", "exitmenu"),
})
The mod_menu function is used to create an entry in the
menu with a title and an entry handler to be called when the menu entry
is activated. The parameters to the handler are similar to those of binding
handlers, and usually the same as those of the binding that opened the menu.
The mod_menu 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.4.2 Special menus
The menu module predefines the following special menus. These can be used
just like 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. |
ctxmenu |
Context menu for given object. |
3.4.3 Defining context menus
The ”ctxmenu” is a special menu that is assembled from a defined context
menu for the object for which the menu was opened for, but also includes
the context menus for the manager objects as submenus.
Context menus for a given region class are defined with the
mod_menu function. This is other ways similar to
mod_menu, but the first argument instead being the name
of the menu, the name of the region class to define context menu for.
For example, here's part of the stock WFrame context menu
definition:
defctxmenu("WFrame", {
menuentry("Close", "WRegion.rqclose_propagate(_, _sub)"),
menuentry("Kill", "WClientWin.kill(_sub)", "_sub:WClientWin"),
})
3.4.4 Displaying menus
The following functions may be used to display menus from binding
handlers (and elsewhere):
|
Function |
Description |
|
mod_menu.menu |
Keyboard (or mouse) operated menus that open in the bottom-left corner
of a screen or frame. |
mod_menu.bigmenu |
Same as previous, but uses another graphical style. |
mod_menu.pmenu |
Mouse-operated drop-down menus. This function can only be called from a
mouse press or drag handler. |
mod_menu.grabmenu |
A special version of mod_menu.menu that grabs the keyboard
and is scrolled with a given key until all modifiers have been released,
after which the selected entry is activated. This function is meant to
be used for implementing, for example, Win***s-style Alt-Tab
handling.3 |
The mod_menu function takes the extra key parameter, but
aside from that each of these functions takes three arguments, which when
called from a binding handler, should be the parameters to the handler, and
the name of the menu. For example, the following snippet of of code binds
the both ways to open a context menu for a frame:
defbindings("WFrame", {
kpress(MOD1.."M", "mod_menu.menu(_, _sub, 'ctxmenu')"),
mpress("Button3", "mod_menu.pmenu(_, _sub, 'ctxmenu')"),
})
3.5 Winprops
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
defwinprop with a table containing the properties to set and the
necessary information to identify a window. The currently supported
winprops are listed in the following table, and the subsequent
subsections explain the usual method of identifying windows, and how
to obtain this information.
|
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? |
ignore_net_active_window |
boolean |
Ignore extended WM hints _NET_ACTIVE_WINDOW request. |
3.5.1 Classes, roles and instances
The identification information in the winprop specification is usually the
class,
role,
instance and
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.5.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.4
If you can guess the title of the transient, the simplest solution
is to use the Mod1+A query (mod_query.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
(mod_query.query_lua) Lua code execution query, assuming there's
only one transient (all on one line):
local id=_:current():managed_list()[1]:get_ident();
mod_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.5.3 Some common examples
Acrobat Reader
The following is absolutely necessary for Acrobat reader:
defwinprop{
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.
defwinprop{
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:
defwinprop{
class = "XTerm",
instance = "sysmon",
target = "sysmonframe",
}
For this example to work, we have to somehow create a frame named
sysmonframe. One way to do this is to make the following
call in the Mod1+F3 Lua code query:
mod_query.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.
- 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
- See the wcirculate.lua script in the Ion
scripts repository http://iki.fi/tuomov/repos/ion-scripts-3/.
- 4
- There's a patch to xprop to
fix this, but nothing seems to be happening with respect to including it in
XFree86.