-
Notifications
You must be signed in to change notification settings - Fork 27
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 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.
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.
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).
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.
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.
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).
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);
}
}
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 :)