Subsections


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 explained in chapter 3.


5.1 Hooks

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 successfully. 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 (successful) 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.9.

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 scripts 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 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 initialisation 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)