diff --git a/CMakeLists.txt b/CMakeLists.txt index 54d5c63..026b3cd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,6 +39,7 @@ add_executable(f00bar particles/ramp.c particles/ramp.h particles/string.c particles/string.h + modules/backlight/backlight.c modules/backlight/backlight.h modules/battery/battery.c modules/battery/battery.h modules/clock/clock.c modules/clock/clock.h modules/i3/dynlist-exposable.c modules/i3/dynlist-exposable.h diff --git a/config.c b/config.c index 502d72f..218526a 100644 --- a/config.c +++ b/config.c @@ -14,6 +14,7 @@ #include "particles/string.h" #include "module.h" +#include "modules/backlight/backlight.h" #include "modules/battery/battery.h" #include "modules/clock/clock.h" #include "modules/i3/i3.h" @@ -329,6 +330,20 @@ module_xkb_from_config(const struct yml_node *node, return module_xkb(particle_from_config(c, parent_font)); } +static struct module * +module_backlight_from_config(const struct yml_node *node, + const struct font *parent_font) +{ + const struct yml_node *name = yml_get_value(node, "name"); + const struct yml_node *c = yml_get_value(node, "content"); + + assert(yml_is_scalar(name)); + assert(yml_is_dict(c)); + + return module_backlight( + yml_value_as_string(name), particle_from_config(c, parent_font)); +} + struct bar * conf_to_bar(const struct yml_node *bar) { @@ -435,6 +450,8 @@ conf_to_bar(const struct yml_node *bar) mods[idx] = module_battery_from_config(it.node, font); else if (strcmp(mod_name, "xkb") == 0) mods[idx] = module_xkb_from_config(it.node, font); + else if (strcmp(mod_name, "backlight") == 0) + mods[idx] = module_backlight_from_config(it.node, font); else assert(false); } diff --git a/modules/backlight/backlight.c b/modules/backlight/backlight.c new file mode 100644 index 0000000..c0aa04a --- /dev/null +++ b/modules/backlight/backlight.c @@ -0,0 +1,215 @@ +#include "backlight.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#define LOG_MODULE "battery" +#include "../../log.h" +#include "../../bar.h" + +struct private { + struct particle *label; + + char *device; + long max_brightness; + long current_brightness; +}; + +static void +destroy(struct module *mod) +{ + struct private *m = mod->private; + free(m->device); + + m->label->destroy(m->label); + + free(m); + module_default_destroy(mod); +} + +static struct exposable * +content(struct module *mod) +{ + const struct private *m = mod->private; + + mtx_lock(&mod->lock); + struct tag_set tags = { + .tags = (struct tag *[]){ + tag_new_int_range("brightness", m->current_brightness, 0, m->max_brightness), + tag_new_int("max_brightness", m->max_brightness), + }, + .count = 2, + }; + mtx_unlock(&mod->lock); + + struct exposable *exposable = m->label->instantiate(m->label, &tags); + + tag_set_destroy(&tags); + return exposable; +} + +static const char * +readline_from_fd(int fd) +{ + static char buf[4096]; + + ssize_t sz = read(fd, buf, sizeof(buf) - 1); + lseek(fd, 0, SEEK_SET); + + if (sz < 0) { + LOG_WARN("failed to read from FD=%d", fd); + return NULL; + } + + buf[sz] = '\0'; + for (ssize_t i = sz - 1; i >= 0 && buf[i] == '\n'; sz--) + buf[i] = '\0'; + + return buf; +} + +static long +readint_from_fd(int fd) +{ + const char *s = readline_from_fd(fd); + if (s == NULL) + return 0; + + long ret; + int r = sscanf(s, "%ld", &ret); + if (r != 1) { + LOG_WARN("failed to convert \"%s\" to an integer", s); + return 0; + } + + return ret; +} + +static int +initialize(struct private *m) +{ + int backlight_fd = open("/sys/class/backlight", O_RDONLY); + if (backlight_fd == -1) { + LOG_ERRNO("/sys/class/backlight"); + return -1; + } + + int base_dir_fd = openat(backlight_fd, m->device, O_RDONLY); + close(backlight_fd); + + if (base_dir_fd == -1) { + LOG_ERRNO("/sys/class/backlight/%s", m->device); + return -1; + } + + int max_fd = openat(base_dir_fd, "max_brightness", O_RDONLY); + if (max_fd == -1) { + LOG_ERRNO("/sys/class/backlight/%s/max_brightness", m->device); + close(base_dir_fd); + return -1; + } + + m->max_brightness = readint_from_fd(max_fd); + close(max_fd); + + int current_fd = openat(base_dir_fd, "brightness", O_RDONLY); + close(base_dir_fd); + + if (current_fd == -1) { + LOG_ERRNO("/sys/class/backlight/%s/brightness", m->device); + return -1; + } + + m->current_brightness = readint_from_fd(current_fd); + + LOG_INFO("%s: brightness: %ld (max: %ld)", m->device, m->current_brightness, + m->max_brightness); + + return current_fd; +} + +static int +run(struct module_run_context *ctx) +{ + const struct bar *bar = ctx->module->bar; + struct private *m = ctx->module->private; + + int current_fd = initialize(m); + module_signal_ready(ctx); + + if (current_fd == -1) + return 1; + + struct udev *udev = udev_new(); + struct udev_monitor *mon = udev_monitor_new_from_netlink(udev, "udev"); + + if (udev == NULL || mon == NULL) { + LOG_ERR("failed to initialize udev monitor"); + + if (udev == NULL) + udev_unref(udev); + + close(current_fd); + return 1; + } + + udev_monitor_filter_add_match_subsystem_devtype(mon, "backlight", NULL); + udev_monitor_enable_receiving(mon); + + while (true) { + struct pollfd fds[] = { + {.fd = ctx->abort_fd, .events = POLLIN}, + {.fd = udev_monitor_get_fd(mon), .events = POLLIN}, + }; + poll(fds, 2, -1); + + if (fds[0].revents & POLLIN) + break; + + struct udev_device *dev = udev_monitor_receive_device(mon); + const char *sysname = udev_device_get_sysname(dev); + + bool is_us = strcmp(sysname, m->device) == 0; + udev_device_unref(dev); + + if (!is_us) + continue; + + mtx_lock(&ctx->module->lock); + m->current_brightness = readint_from_fd(current_fd); + mtx_unlock(&ctx->module->lock); + bar->refresh(bar); + } + + udev_monitor_unref(mon); + udev_unref(udev); + + close(current_fd); + return 0; +} + +struct module * +module_backlight(const char *device, struct particle *label) +{ + struct private *m = malloc(sizeof(*m)); + m->label = label; + m->device = strdup(device); + m->max_brightness = 0; + m->current_brightness = 0; + + struct module *mod = module_common_new(); + mod->private = m; + mod->run = &run; + mod->destroy = &destroy; + mod->content = &content; + return mod; +} diff --git a/modules/backlight/backlight.h b/modules/backlight/backlight.h new file mode 100644 index 0000000..ab0b9b3 --- /dev/null +++ b/modules/backlight/backlight.h @@ -0,0 +1,6 @@ +#pragma once + +#include "../../module.h" +#include "../../particle.h" + +struct module *module_backlight(const char *device, struct particle *label);