This chapter should help your configure Notion to your liking. As you probably already know, Notion 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 is an overview of the multiple configuration files Notion uses and as a perhaps more understandable introduction to the general layout of the configuration files, a walk-through of the main configuration file cfg_notion.lua is provided in section . How keys and mouse action are bound to functions is described in detail in and in section winprops are explained. Finally, the statusbar is explained in . For a reference on exported functions, see section .
Notion stores its stock configuration files in /usr/local/etc/notion/ 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 Notion. 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_notion.lua might help provided updatedb has been run recently.
User configuration files go in ~/.notion/. Notion 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 Notion will restore the stock configuration files. Instead you should always make a copy of the stock file in ~/.notion/ and modify this file. For sake of maintainability of your customised configuration, it is recommended against copying all of the files there. Only copy those files you actually need to modify. Most simple customisations, such as changes in a few bindings, are best done entirely within cfg_notion.lua.
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. Configuration files can also be compiled into .lc files, and these are attempted by the configuration file search routines before .lua files.
The following table summarises these and other configuration files:
File |
cfg_notion.lua |
cfg_notioncore.lua |
cfg_kludges.lua |
cfg_layouts.lua |
cfg_tiling.lua cfg_query.lua cfg_menu.lua cfg_dock.lua cfg_statusbar.lua ... |
Additionally, there's the file look.lua that configures the drawing engine, but it is covered in chapter .
As already mentioned cfg_notion.lua is Notion'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 Notion are loaded there. In this section we
take a walk through the stock cfg_notion.lua.
Notice that most of the settings are commented-out (--
is a
line comment in Lua) in the actual file, as they're the defaults
nevertheless.
The first thing done in the file, is to set
META="Mod1+" ALTMETA=""These settings cause most of Notion's key bindings to use Mod1 as the modifier key. If ALTMETA is set, it is used as modifier for the keys that don't normally use a modifier. Note that these two are Lua variables used in the configuration files only, and not Notion settings. For details on modifiers and key binding setup in general, see section .
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 Notion to also grab the X server and has some side effects.
There are some other options as well; see the documentation for ioncore.set for details.
As a next step, in the actual cfg_notion.lua file, we load cfg_defaults.lua. However, it is merely a convenience file for doing exactly what we will going through below, and what is commented out in the actual file. If you do not want to load what cfg_defaults.lua loads, just comment out the corresponding line, and uncomment the lines for the files that you want:
--dopath("cfg_defaults") dopath("cfg_notioncore") dopath("cfg_kludges") dopath("cfg_layouts")
Most bindings and menus are defined in cfg_ioncore.lua. Details on making such definitions follow in sections and , respectively. some kludges or ``winprops'' to make some applications behave better under Notion are collected in cfg_kludges.lua; see section 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 Notion 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. Finally, cfg_layouts.lua defines some workspace layouts, available through the F9 workspace creation query.
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_tiling") dopath("mod_statusbar") --dopath("mod_dock") dopath("mod_sp")
In the stock configuration file setup, most key and mouse bindings are set from the file cfg_notioncore.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 defbindings 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 , and fully listed in appendix , 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.
Currently known contexts include: `WScreen', `WMPlex', `WMPlex.toplevel', `WFrame', `WFrame.toplevel', `WFrame.floating', `WFrame.tiled', `WFrame.transient', `WMoveresMode', `WGroup', `WGroupCW', `WGroupWS', `WClientWin', `WTiling', and `WStatusBar'. Most of these should be self-explanatory, corresponding to objects of class with the same name. The ones with `.toplevel' suffix refer to screens and ``toplevel'' frames, i.e. frames that are not used for transient windows. Likewise `.transient' refers to frames in transient mode, and `.tiled' and `.floating' to frames in, respectively, tiled and floating modes.
The following subsections describe how to construct elements of the binding table. Note that defbindings 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 ) of objects gets to handle the action.
Unlike in Ion2, in Notion 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 following special variables are available in this code.
Variable |
_ (underscore) |
_sub |
_chld |
For example, supposing _ (underscore) 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 (for both _sub and _chld):
Guard |
`_sub:non-nil' |
`_sub:SomeClass' |
The descriptions of the individual bindings in the binding table argument to defbindings should be constructed with the following functions.
Key presses:
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 Notioncore 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)"), }), })
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.h3.1 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.
Notion 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, Notion recognises NoModifier as a special modifier that can be used to reset this default.
Notion ignores the Lock modifier and any ModN ( N = 1...5) bound to NumLock or ScrollLock by default because such3.2 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. 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()"), })
The default binding configuration contains references to the variables META and ALTMETA instead of directly using the default values of `Mod1+' and `' (nothing). As explained in section , the definitions of these variables appear in cfg_notion.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.
Caps Lock is rarely used for its original purpose. For this reason, you might want to put it to different use, and bind it to some window management feature. However, if you would simply bind Caps_Lock to some functionality, each time you would access it it would switch case.
You can use the X Keyboard Extension to disable the 'Lock' feature of Caps Lock, without disabling or swapping the key wholesale. To this end, create a /usr/share/X11/xkb/symbols/misc file with the following contents:
partial modifier_keys xkb_symbols "capsoff" { key <CAPS> { type[Group1] = "ONE_LEVEL", symbols[Group1] = [ Caps_Lock ], actions[Group1] = [ NoAction() ] }; };
Then load it with:
$ setxkbmap -symbols 'pc+us+misc(capsoff)'
To make this change persistent, put it in your startup script.
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 defmenu 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 menuentry 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 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.)
The menu module predefines the following special menus. These can be used just like the menus defined as above.
Menu name |
`windowlist' |
`workspacelist' |
`focuslist' |
`focuslist_' |
`stylemenu' |
`ctxmenu' |
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 defctxmenu function. This is other ways similar to defmenu, 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"), })
Some of the same ``modes'' as were available for some bindings may also be used: `WFrame.tiled', `WFrame.floating', and `WFrame.transient'.
The following functions may be used to display menus from binding handlers (and elsewhere):
Function |
mod_menu.menu |
mod_menu.pmenu |
mod_menu.grabmenu |
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')"), })
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 below, and the subsequent subsections explain the usual method of identifying windows, and how to obtain this information.
These properties can be used to tell Notion how to treat the matched windows:
Additionally, the winprops max_size, min_size, aspect, resizeinc, and ignore_max_size, ignore_min_size, ignore_aspect, ignore_resizeinc, may be used to override application-supplied size hints. The four first ones are tables with the fields w and h, indicating the width and height size hints in pixels, and the latter ignore winprop is a boolean.
Finally, the boolean userpos option may be used to override the USPosition flag of the size hints. Normally, when this flag is set, Notion tries to respect the supplied window position more than when it is not set. Obviously, this makes sense only for floating windows.
The primary identification information supported is the class, role and instance of the window.
Secondary filters are the name, is_transient and is_dockapp fields, or a custom matching algorithm.
Notion first filters the winprops based on the primary identification fields. Of those winprops that match the primary identification fields, the most recently defined winprop that also matches the secondary filters is used.
The primary identification fields are strings, and must exactly match the corresponding information obtained from the window's properties.
Notion looks for a 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. 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 |
E | E | E |
E | E | * |
E | * | E |
E | * | * |
&vellip#vdots; | &vellip#vdots; | etc. |
The name field is a Lua-style regular expression matched against the window's title. The is_transient field is a boolean that can be used to include or exclude transients only, while the is_dockapp field is set by Notion for the dock windows of Window Maker dockapp protocol dockapps. Usually this is the only information available for these icon windows.
If a custom matching algorithm is used, the secondary filters are ignored.
A custom matching algorithm can be specified by simple adding a lua function named match to the winprop table. This function will be passed 3 parameters: the winprop itself, the client window against which the winprop must be matched, and the identification information of this client window.
The identification information of the client window is a table containing the class, role, instance, is_transient and is_dockapp fields of the window.
The 'Window info' context menu entry (Mod1+M or Button3 on a tab) can be used to list the identification information required to set winprops for a window and all the transient windows managed within it.
Another way to get the identification information is to use xprop. Simply run To get class and instance, simply run 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. This method, however, will not work on transients.
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 Notion displays these windows simultaneously 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.3
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 program's windows, for example. Some other programs only set this information after the window has been mapped, i.e. the window manager has been told to start managing it, which is obviously too late. Gtk applications in particular are often guilty on both counts.
The following is absolutely necessary for Acrobat reader:
defwinprop{ class = "AcroRead", instance = "documentShell", acrobatic = true, }
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.
Most window managers have some kind of 'dock' or statusbar/panel to give a quick overview of running processes and system status. Notion is no different - actually we have several options.
When a part of the screen is reserved for this purpose, in Notion this is called a ``status display'' or ``stdisp''. You can have one stdisp per Screen - it is independent of the current workspace or tiling.
Notion has 2 kinds of stdisp's: if you want to use one or more existing panel/dock applications, you can use mod_dock with the ``embedded'' mode. Alternatively, you can use the built-in mod_statusbar. Since a Screen can only hold one stdisp, you can have either - not both.
Notion 4+ loads mod_dock by default via ``cfg_defaults.lua``; previous versions loaded mod_statusbar. To switch, simply adjust this line to load the other module instead.
Generally, if you want to use some panel application like the xfce4-panel, fbpanel or the gnome-panel, use an embedded dock. When used in this way, it is common to add only one panel to the dock and have it span the entire width of the screen. However, the dock does support adding multiple applications to it, so if you prefer WindowMaker-style dockapps, mod_dock (either embedded or floating) is also the way to go.
Given the high quality of 3rd-party panels, mod_statusbar is mostly a legacy part of Notion.
The mod_statusbar module provides a statusbar that adapts to layouts of tilings, using only the minimal space needed.
The statusbar is configured in cfg_statusbar.lua. Typically, the configuration consists of two steps: creating a statusbar with mod_statusbar.create, and then launching the separate ion-statusd status daemon process with mod_statusbar.launch_statusd. This latter phase is done automatically, if it was not done by the configuration file, but the configuration file may pass extra parameters to ion-statusd monitors. (See Section for more information on writing ion-statusd monitors.)
A typical cfg_statusbar.lua configuration might look as follows:
-- Create a statusbar mod_statusbar.create{ screen = 0, -- First screen, pos = 'bl', -- bottom left corner systray = true, -- Swallow systray windows -- The template template = "[ %date || load:% %>load || mail:% %>mail_new/%>mail_total ]" .. " %filler%systray", } -- Launch ion-statusd. mod_statusbar.launch_statusd{ -- Date meter date={ -- ISO-8601 date format with additional abbreviated day name date_format='%a %Y-%m-%d %H:%M', }, }
The template specifies what is shown on the statusbar; for information on the other options to mod_statusbar.create, see the reference. Strings of the form `%spec' tokens specially interpreter by the statusbar; the rest appears verbatim. The spec typically consists of the name of the value/meter to display (beginning with a latin alphabet), but may be preceded by an alignment specifier and a number specifying the minimum width. The alignment specifiers are: `>' for right, `<' for left, and `|' for centring. Additionally, space following `%' (that is, the string `% '), adds ``stretchable space'' at that point. The special string `%filler' may be used to flush the rest of the template to the right end of the statusbar.
The stretchable space works as follows: mod_statusbar remembers the widest string (in terms of graphical presentation) that it has seen for each meter, unless the width has been otherwise constrained. If there is stretchable space in the template, it tries to make the meter always take this much space, by stretching any space found in the direction indicated by the alignment specifier: the opposite direction for left or right alignment, and both for centring.
The special `%systray' and `%systray_*' (`*' varying) monitors indicate where to place system tray windows. There may be multiple of these. KDE-protocol system tray icons are placed in `%systray' automatically, unless disabled with the systray option. Otherwise the statusbar winprop may be used to place any window in any particular `%systray_*'.
The part before the first underscore of each monitor name, describes the script/plugin/module that provides the meter, and any configuration should be passed in the a corresponding sub-table mod_statusbar.launch_statusd. Notion comes with date, load and mail (for plain old mbox) ion-statusd monitor scripts. More may be obtained from the scripts repository [1]. These included scripts provide the following monitors and their options
Options: date_format: The date format in as seen above, in the usual strftime format. formats: table of formats for additional date monitors, the key being the name of the monitor (without the `date_' prefix).
Monitors: `date' and other user-specified ones with the `date_' prefix.
Options: update_interval: Update interval in milliseconds (default 10s). important_threshold: Threshold above which the load is marked as important (default 1.5), so that the drawing engine may be suitably hinted. critical_threshold: Threshold above which the load is marked as critical (default 4.0).
Monitors: `load' (for all three values), `load_1min', `load_5min' and `load_15min'.
Options: update_interval: Update interval in milliseconds
(default 1min). mbox: mbox-format mailbox location
(default $MAIL
).
files: list of additional mailboxes, the key giving the
name of the monitor.
Monitors: `mail_new', `mail_unread', `mail_total', and corresponding `mail_*_new', `mail_*_unread', and `mail_*_total' for the additional mailboxes (`*' varying).