Skip to content

Custom Modules

Federico Di Pierro edited this page Mar 24, 2021 · 18 revisions

Custom modules

Starting with Clight 4.0, thanks to libmodule 5.0.0 port, clight is capable of runtime loading custom modules.
This means users can write their own specific modules to customize Clight behaviour.

Clight will load modules located in:

  • System-wide: $CMAKE_INSTALL_FULL_DATADIR/clight/modules.d/ (normally /usr/share/clight/modules.d)
  • User: $XDG_DATA_HOME/clight/modules.d/ (defaults to $HOME/.local/share/clight/modules.d/)

Note that User modules take precedence.
There can only be a single module with same name in Clight; thus the following rules apply:

  • Nobody can override a clight internal module
  • An User custom module can override a System custom module (by taking same name)

Clight takes advantage of libmodule's PubSub interface that makes it super easy to interact with Clight private modules.
The idea here is that custom modules should subscribe to certain Clight internal events, and react to them by publishing requests to Clight internal modules.

Custom modules behave just like private Clight modules but:

  • have no access to Clight conf
  • have no access to Clight internal state
  • can log to clight's log just like any other module, but cannot use ERROR() log macro (that would exit clight)

In the end, they can only interact with other modules through PubSub interface as defined in clight/public.h header.

Clight public API

Clight will install its public API in ${CMAKE_INSTALL_INCLUDEDIR}/clight/public.h (normally /usr/include/clight/public.h).
Note that, while I'll strongly try to avoid any API break, this API should be considered as unstable, and may change in the future.

Build

Custom modules must be built as shared objects with Position Independent Code:
gcc -shared -fPIC myMod.c -o myMod -Wno-unused

And placed in either System-wide or User-local clight modules folder.

Internal modules Availability

Clight internal modules are not guaranteed to be running. Here is a short table:

Modules Availability
LOCATION * Geoclue avialable and no fixed sunrise sunset/location specified
UPOWER * Upower available and on laptop device
DAYTIME * LOCATION available or fixed sunrise sunset/location
INHIBIT * At least one between DPMS or DIMMER module enabled
DISPLAY * At least one between DPMS or DIMMER module enabled
PM * INHIBIT available
KEYBOARD * Keyboard backlight supported
GAMMA * DAYTIME available

Moreover, conf exposed modules are only available when not disabled (plus any other specified condition in above table).

Examples

A couple of skeletons modules are provided too, to be either customized or used right away. You can find them in $CMAKE_INSTALL_FULL_DATADIR/clight/*.skel.

I will list them here; they implement some highly requested behaviours.

Night DE theme

The first, very simple custom module, adds support for changing your DE theme on daytime updates (eg: dark theme on NIGHT and normal theme during the DAY).
It is pretty straightforward:

#include <clight/public.h>

CLIGHT_MODULE("NIGHTMODE");

static void init(void) {
    /* Suscribe to daytime updates */
    M_SUB(DAYTIME_UPD);
}

static void receive(const msg_t *msg, const void *userdata) {
    switch (MSG_TYPE()) {
    case DAYTIME_UPD: {
        daytime_upd *up = (daytime_upd *)MSG_DATA();
        if (up->new == DAY) {
            // system("lookandfeeltool -a org.kde.breeze.desktop");
            INFO("We're now during the day!\n");
        } else {
            // system("lookandfeeltool -a org.kde.breezedark.desktop");
            INFO("We're now during the night!\n");
        }
        break;
    }
    default:
        break;
    }
}

As you can see, some macros where intentionally developed to make developing custom modules as simple as possible.

Inhibit Backlight

The second skeleton provides another highly requested feature: setting a certain backlight level and pause backlight autocalibration when Clight gets inhibited (eg: when starting to watch a movie):

#include <clight/public.h>

CLIGHT_MODULE("INHIBIT_BL");

DECLARE_MSG(bl_req, BL_REQ);
DECLARE_MSG(capture_req, CAPTURE_REQ);

static void init(void) {
    capture_req.capture.reset_timer = false; // avoid resetting clight internal BACKLIGHT timer
    bl_req.bl.new = 1.0;    // 100% screen backlight
    bl_req.bl.smooth = -1;  // use conf values
    
    /* Subscribe to inhibit state */
    M_SUB(INHIBIT_UPD);
}

static void receive(const msg_t *msg, const void *userdata) {
    DECLARE_HEAP_MSG(calib_req, NO_AUTOCALIB_REQ);
    switch (MSG_TYPE()) {
    case INHIBIT_UPD: {
        inhibit_upd *up = (inhibit_upd *)MSG_DATA();
        if (up->new) {
            calib_req->nocalib.new = true;
            M_PUB(calib_req);
            M_PUB(&bl_req);
            INFO("We are now inhibited! Set 100%% screen backlight.\n");
        } else {
            INFO("We're not inhibited anymore. Do a quick backlight calibration.\n");
            calib_req->nocalib.new = false;
            M_PUB(calib_req);
            M_PUB(&capture_req);
        }
        break;
    }
    default:
        break;
    }
}

This is somewhat more complex and offers a nice overview of all the available possibilities:

  • When you wish to publish a request to Clight private modules, you must DECLARE_MSG() with your desired message type.
  • Requests can have mandatory fields. You can check them out in public header.
  • To publish a message, you should M_PUB() your desired message's address.
  • DECLARE_HEAP_MSG() api allows creating heap messages that will be automatically freed when all recipients receive them. This is useful to be sure that each message is unique (as messages are sent per-reference).

Notifier

This one is a small module that will listen for SUNRISE/SUNSET/LOCATION updates and send notifications to the user through libnotify.
Again, it can obviously be expanded as much as you wish!
It needs libnotify (libnotify-dev on ubuntu), and can be built with:

gcc -shared -fPIC notifier.c -o notifier -Wno-unused `pkg-config --cflags --libs libnotify`
#include <clight/public.h>
#include <stdlib.h>
#include<libnotify/notify.h>

CLIGHT_MODULE("NOTIFIER");

static void init(void) {
    M_SUB(SUNRISE_UPD);
    M_SUB(SUNSET_UPD);
    M_SUB(LOC_UPD);
    
    notify_init("clight-notify");
}

static void receive(const msg_t *msg, const void *userdata) {
    char *title = NULL, *message = NULL;
    switch (MSG_TYPE()) {
    case SUNRISE_UPD: {
        evt_upd *sunrise = (evt_upd *)MSG_DATA();
        title = strdup("Sunrise Update");
        message = strdup(ctime(&sunrise->new));
        break;
    }
    case SUNSET_UPD: {
        evt_upd *sunset = (evt_upd *)MSG_DATA();
        title = strdup("Sunset Update");
        message = strdup(ctime(&sunset->new));
        break;
    }
    case LOC_UPD: {
        loc_upd *loc = (loc_upd *)MSG_DATA();
        title = strdup("Location Update");
        char msg[64];
        sprintf(msg, "New location: %.2lf, %.2lf", loc->new.lat, loc->new.lon);
        message = strdup(msg);
        break;
    }
    default:
        break;
    }
    
    if (title && message) {
        NotifyNotification *notif = notify_notification_new(title, message, NULL);
        notify_notification_show(notif, NULL);
        free(title);
        free(message);
    }
}

Automatic curve

User NICHOLAS85 has created a trendlog custom module, to automatically log any Clight ambient brightness together with currently user-setted backlight.
Then he is using the data to compute new, super-fit, backlight curves.
Have a look at trendlog module!


All in all, I think this is a really great way to customize your Clight experience!
For any doubt, do not hesitate to contact me.
If you wrote a fresh new custom module, and you wish to share it with other users, feel free to open a feature request and propose it to be installed as a skeleton :)

Clone this wiki locally