5 Scripting
This chapter documents some additional features of the Ion configuration
and scripting interface that can be used for more advanced scripting than
the basic configuration exlained in chapter 3.
Hooks are lists of functions to be called when a certain event occurs.
There are two types of them; normal and ”alternative” hooks. Normal
hooks do not return anything, but alt-hooks should return a boolean
indicating whether it handled its assigned task succesfully. In the case
that true is returned, remaining handlers are not called.
Hook handlers are registered by first finding the hook
with ioncore.get_hook and then calling WHook.add
on the (succesfull) result with the handler as parameter. Similarly
handlers are unregistered with WHook.remove. For example:
ioncore.get_hook("ioncore_snapshot_hook"):add(
function() print("Snapshot hook called.") end
)
In this example the hook handler has no parameters, but many hook
handlers do. The types of parameters for each hook are listed in
the hook reference, section 6.10.
Note that many of the hooks are called in ”protected mode” and can not
use any functions that modify Ion's internal state. TODO: More detailed
documentation when this is final.
5.2 Referring to regions
5.2.1 Direct object references
All Ion objects are passed to Lua scriptss as 'userdatas', and you may
safely store such object references for future use. The C-side object
may be destroyed while Lua still refers to the object. All exported
functions gracefully fail in such a case, but if you need to explicitly
test that the C-side object still exists, use obj_exists.
As an example, the following short piece of code implements
bookmarking:
local bookmarks={}
-- Set bookmark bm point to the region reg
function set_bookmark(bm, reg)
bookmarks[bm]=reg
end
-- Go to bookmark bm
function goto_bookmark(bm)
if bookmarks[bm] then
-- We could check that bookmarks[bm] still exists, if we
-- wanted to avoid an error message.
bookmarks[bm]:goto()
end
end
5.2.2 Name-based lookups
If you want to a single non-WClientWin region with an exact known
name, use ioncore.lookup_region. If you want a list of all regions,
use ioncore.region_list. Both functions accept an optional argument
that can be used to specify that the returned region(s) must be of a more
specific type. Client windows live in a different namespace and for them
you should use the equivalent functions ioncore.lookup_clientwin
and ioncore.clientwin_list.
To get the name of an object, use WRegion.name. Please be
aware, that the names of client windows reflect their titles and
are subject to changes. To change the name of a non-client window
region, use WRegion.set_name.
5.3 Alternative winprop selection criteria
It is possible to write more complex winprop selection routines than
those described in section 3.5. To match a particular
winprop using whatever way you want to, just set the match
field of the winprop to a function that receives the client window
as its sole parameter, and that returns true if the winprop
matches, and false otherwise.
The class, instance and role properties can be obtained with
WClientWin.get_ident, and the title with WRegion.name.
If you want to match against (almost) arbitrary window properties,
have a look at the documentation for the following functions, and
their standard Xlib counterparts: ioncore.x_intern_atom
(XInternAtom), ioncore.x_get_window_property (XGetWindowProperty),
and ioncore.x_get_text_property (XGetTextProperty).
5.4 Layers
In ion3, WMPlex manage two lists of objects. The layer 1
objects are the “normal” objects that appear on screen, and in
addition, you can add layer 2 objects, that will either be hidden, or
appear on top of the screen. The Scratchpad, for example, is a layer 2
object.
Layer 2 objects can be either passive (ion will never give them the
focus) or not (ion will never give the focus to a layer 1 object while
a non-passive object is displayed).
You can, for example, attach a WFloatWS on top of a
WIonWS with the following lua code:
fws = foo:screen_of():attach_new({
type = "WFloatWS",
name = "foobar",
layer = 2,
passive = false,
switchto = false,
})
While the WFloatWS is empty, it will off course not be visible,
but windows opened in this workspace will appear floating on top of
the layer 1 workspace.
You can hide this workspace (and all it contains) with
fws:screen_of():l2_hide(fws)
and show it again with
fws:screen_of():l2_show(fws)
The script detach.lua available in the Ion3 scripts repository1
can give you some ideas about what it is possible to do with this feature.
5.5 Writing ion-statusd monitors
All statusbar meters that do not monitor the internal state of Ion should
go in the separate ion-statusd program.
Whenever the user requests a meter %foo or %foo_bar to be
inserted in a statusbar, mod_statusbar asks ion-statusd to
load statusd_foo.lua on its search path (same as that for Ion-side
scripts). This script should then supply all meters with the initial part
'foo'.
To provide this value, the script should simply call statusd.inform
with the name of the meter and the value as a string.
Additionally the script should provide a 'template' for the meter to
facilitate expected width calculation by mod_statusbar, and
may provide a 'hint' for colour-coding the value. The interpretation
of hints depends on the graphical style in use, and currently the
stock styles support the normal, important and
critical hints.
In our example of the 'foo monitor', at script init we might broadcast
the template as follows:
statusd.inform("foo_template", "000")
To inform mod_statusbar of the actual value of the meter and
indicate that the value is critical if above 100, we might write the
following function:
local function inform_foo(foo)
statusd.inform("foo", tostring(foo))
if foo>100 then
statusd.inform("foo_hint", "critical")
else
statusd.inform("foo_hint", "normal")
end
end
To periodically update the value of the meter, we must use timers.
First we must create one:
local foo_timer=statusd.create_timer()
Then we write a function to be called whenever the timer expires.
This function must also restart the timer.
local function update_foo()
local foo= ... measure foo somehow ...
inform_foo(foo)
foo_timer:set(settings.update_interval, update_foo)
end
Finally, at the end of our script we want to do the initial
measurement, and set up timer for further measurements:
update_foo()
If our scripts supports configurable parameters, the following code
(at the beginning of the script) will allow them to be configured in
cfg_statusbar.lua and passed to the status daemon and our script:
local defaults={
update_interval=10*1000, -- 10 seconds
}
local settings=table.join(statusd.get_config("foo"), defaults)
- 1
- http://iki.fi/tuomov/repos/ion-scripts-3/