From 57e755477c1220b878c9be120001349da359c44f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 5 Sep 2020 09:28:06 +0200 Subject: [PATCH 001/611] changelog: new 'unreleased' section --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d833aa..ea8f2c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,18 @@ # Changelog +* [Unreleased](#unreleased) * [1.5.0](#1-5-0) +## Unreleased +### Added +### Deprecated +### Removed +### Fixed +### Security +### Contributors + + ## 1.5.0 ### Added From eceee99fb0aaddaa3842d07c41a8a9b9ff947af0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 24 Sep 2020 13:27:48 +0200 Subject: [PATCH 002/611] =?UTF-8?q?yml:=20don=E2=80=99t=20overwrite=20erro?= =?UTF-8?q?rs=20from=20yml=5Fparser=5Fparse()?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When we detect an error in yml_parser_parse(), we handle that specifically, and then jump to the generic error handler. The generic error handler overwrites the previously formatted error before cleaning up and returning. This meant that a) the actual error message was lost, and replaced with a generic “unknown error”, and b) the dynamically allocated error string buffer was leaked. --- CHANGELOG.md | 5 +++++ yml.c | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ea8f2c6..bdd3c67 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,11 @@ ### Deprecated ### Removed ### Fixed + +* YAML parsing error messages being replaced with a generic “unknown error”. +* Memory leak when a YAML parsing error was encoutered. + + ### Security ### Contributors diff --git a/yml.c b/yml.c index 73e5083..40ece40 100644 --- a/yml.c +++ b/yml.c @@ -383,7 +383,7 @@ yml_load(FILE *yml, char **error) yaml.context != NULL ? yaml.context : ""); } - goto err; + goto err_no_error_formatting; } switch (event.type) { @@ -550,6 +550,8 @@ err: yaml.mark.line + 1, yaml.mark.column); } +err_no_error_formatting: + yml_destroy(root); yaml_parser_delete(&yaml); return NULL; From 85ae4cca37aa3832bd5cc6fffcd3b128141d72fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 24 Sep 2020 13:39:52 +0200 Subject: [PATCH 003/611] =?UTF-8?q?module/alsa:=20add=20=E2=80=98percent?= =?UTF-8?q?=E2=80=99=20tag=20-=20volume=20level=20as=20a=20percentage?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes part of #10 --- CHANGELOG.md | 5 +++++ doc/yambar-modules.5.scd | 3 +++ modules/alsa.c | 7 ++++++- 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bdd3c67..61037ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,11 @@ ## Unreleased ### Added + +* alsa: `percentage` tag. This is an integer tag that represents the + current volume as a percentage value. + + ### Deprecated ### Removed ### Fixed diff --git a/doc/yambar-modules.5.scd b/doc/yambar-modules.5.scd index 947b57d..3a099c6 100644 --- a/doc/yambar-modules.5.scd +++ b/doc/yambar-modules.5.scd @@ -145,6 +145,9 @@ Monitors an alsa soundcard for volume and mute/unmute changes. | volume : range : Volume level, with min and max as start and end range values +| percent +: range +: Volume level, as a percentage | muted : bool : True if muted, otherwise false diff --git a/modules/alsa.c b/modules/alsa.c index b274903..6933fe5 100644 --- a/modules/alsa.c +++ b/modules/alsa.c @@ -43,13 +43,18 @@ content(struct module *mod) { struct private *m = mod->private; + int percent = m->vol_max - m->vol_min > 0 + ? 100 * m->vol_cur / (m->vol_max - m->vol_min) + : 0; + mtx_lock(&mod->lock); struct tag_set tags = { .tags = (struct tag *[]){ tag_new_int_range(mod, "volume", m->vol_cur, m->vol_min, m->vol_max), + tag_new_int_range(mod, "percent", percent, 0, 100), tag_new_bool(mod, "muted", m->muted), }, - .count = 2, + .count = 3, }; mtx_unlock(&mod->lock); From db53cf924556ca1cb7e5ca1ad630b1daa6827817 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 24 Sep 2020 13:55:17 +0200 Subject: [PATCH 004/611] module/alsa+backlight: round() percentage values Closes #10 --- meson.build | 1 + modules/alsa.c | 3 ++- modules/backlight.c | 3 ++- modules/meson.build | 4 ++-- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/meson.build b/meson.build index db6a7c2..2209469 100644 --- a/meson.build +++ b/meson.build @@ -42,6 +42,7 @@ endif # Common dependencies dl = cc.find_library('dl') +m = cc.find_library('m') threads = dependency('threads') pixman = dependency('pixman-1') yaml = dependency('yaml-0.1') diff --git a/modules/alsa.c b/modules/alsa.c index 6933fe5..9e410df 100644 --- a/modules/alsa.c +++ b/modules/alsa.c @@ -1,5 +1,6 @@ #include #include +#include #include @@ -44,7 +45,7 @@ content(struct module *mod) struct private *m = mod->private; int percent = m->vol_max - m->vol_min > 0 - ? 100 * m->vol_cur / (m->vol_max - m->vol_min) + ? round(100. * m->vol_cur / (m->vol_max - m->vol_min)) : 0; mtx_lock(&mod->lock); diff --git a/modules/backlight.c b/modules/backlight.c index 1c65d38..053a998 100644 --- a/modules/backlight.c +++ b/modules/backlight.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -46,7 +47,7 @@ content(struct module *mod) const long current = m->current_brightness; const long max = m->max_brightness; - const long percent = max > 0 ? 100 * current / max : 0; + const long percent = max > 0 ? round(100. * current / max) : 0; struct tag_set tags = { .tags = (struct tag *[]){ diff --git a/modules/meson.build b/modules/meson.build index a42828e..3efcc9e 100644 --- a/modules/meson.build +++ b/modules/meson.build @@ -10,8 +10,8 @@ xcb_xkb = dependency('xcb-xkb', required: get_option('backend-x11')) # Module name -> (source-list, dep-list) deps = { - 'alsa': [[], [alsa]], - 'backlight': [[], [udev]], + 'alsa': [[], [m, alsa]], + 'backlight': [[], [m, udev]], 'battery': [[], [udev]], 'clock': [[], []], 'i3': [['i3-common.c', 'i3-common.h'], [dynlist, json]], From 51e9dbd4fc3d7b9431b3087ec6eb3faf2c07760b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 24 Sep 2020 13:59:15 +0200 Subject: [PATCH 005/611] changelog: alsa: percentage -> percent --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 61037ab..68f3690 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ ## Unreleased ### Added -* alsa: `percentage` tag. This is an integer tag that represents the +* alsa: `percent` tag. This is an integer tag that represents the current volume as a percentage value. From 88f0b7b8c0992b38404bc793bd75fdcff6a2efd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 24 Sep 2020 13:59:58 +0200 Subject: [PATCH 006/611] =?UTF-8?q?changelog:=20highlight=20=E2=80=9Cunkno?= =?UTF-8?q?wn=20error=E2=80=9D=20+=20spell=20fix=20=E2=80=98encountered?= =?UTF-8?q?=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 68f3690..77bb49f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,8 +15,9 @@ ### Removed ### Fixed -* YAML parsing error messages being replaced with a generic “unknown error”. -* Memory leak when a YAML parsing error was encoutered. +* YAML parsing error messages being replaced with a generic _“unknown + error”_. +* Memory leak when a YAML parsing error was encountered. ### Security From 1d579dff6bb5b5a22247ac661553b35143c4abe1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 24 Sep 2020 14:00:50 +0200 Subject: [PATCH 007/611] changelog: use backticks for filenames and properties --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 77bb49f..c99a507 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,10 +28,10 @@ ### Added -* battery: support for drivers that use 'charge\_\*' (instead of - 'energy\_\*') sys files. +* battery: support for drivers that use `charge\_\*` (instead of + `energy\_\*`) sys files. * removables: SD card support. -* removables: new 'ignore' property. +* removables: new `ignore` property. * Wayland: multi-seat support. * **Experimental**: 'river': new module for the river Wayland compositor. From a041b8f971830f718b08708cd315e4f2cad12001 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 24 Sep 2020 14:01:30 +0200 Subject: [PATCH 008/611] =?UTF-8?q?changelog:=20don=E2=80=99t=20do=20escap?= =?UTF-8?q?ing=20in=20backtick=20quoted=20string?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c99a507..1a95f16 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,8 +28,8 @@ ### Added -* battery: support for drivers that use `charge\_\*` (instead of - `energy\_\*`) sys files. +* battery: support for drivers that use `charge_*` (instead of + `energy_*`) sys files. * removables: SD card support. * removables: new `ignore` property. * Wayland: multi-seat support. From 318965b71597b0617a893739d958e153a08faa00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 24 Sep 2020 14:32:39 +0200 Subject: [PATCH 009/611] module/clock: internally use either minutes or seconds granularity --- modules/clock.c | 58 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 51 insertions(+), 7 deletions(-) diff --git a/modules/clock.c b/modules/clock.c index 0599e9a..2ff4f28 100644 --- a/modules/clock.c +++ b/modules/clock.c @@ -4,7 +4,10 @@ #include #include +#include +#define LOG_MODULE "clock" +#include "../log.h" #include "../bar/bar.h" #include "../config.h" #include "../config-verify.h" @@ -12,6 +15,10 @@ struct private { struct particle *label; + enum { + UPDATE_GRANULARITY_SECONDS, + UPDATE_GRANULARITY_MINUTES, + } update_granularity; char *date_format; char *time_format; }; @@ -55,20 +62,56 @@ content(struct module *mod) static int run(struct module *mod) { + const struct private *m = mod->private; const struct bar *bar = mod->bar; bar->refresh(bar); while (true) { - time_t now = time(NULL); - time_t now_no_secs = now / 60 * 60; - assert(now_no_secs % 60 == 0); + struct timespec _now; + clock_gettime(CLOCK_REALTIME, &_now); - time_t next_min = now_no_secs + 60; - time_t timeout = next_min - now; - assert(timeout >= 0 && timeout <= 60); + const struct timeval now = { + .tv_sec = _now.tv_sec, + .tv_usec = _now.tv_nsec / 1000, + }; + + int timeout_ms; + + switch (m->update_granularity) { + case UPDATE_GRANULARITY_SECONDS: { + const struct timeval next_second = { + .tv_sec = now.tv_sec + 1, + .tv_usec = 0}; + + struct timeval _timeout; + timersub(&next_second, &now, &_timeout); + + assert(_timeout.tv_sec == 0 || + (_timeout.tv_sec == 1 && _timeout.tv_usec == 0)); + timeout_ms = _timeout.tv_usec / 1000; + break; + } + + case UPDATE_GRANULARITY_MINUTES: { + const struct timeval next_minute = { + .tv_sec = now.tv_sec / 60 * 60 + 60, + .tv_usec = 0, + }; + + struct timeval _timeout; + timersub(&next_minute, &now, &_timeout); + timeout_ms = _timeout.tv_sec * 1000 + _timeout.tv_usec / 1000; + } + } + + /* Add 1ms to account for rounding errors */ + timeout_ms++; + + LOG_DBG("now: %lds %ldµs -> timeout: %dms", + now.tv_sec, now.tv_usec, timeout_ms); struct pollfd fds[] = {{.fd = mod->abort_fd, .events = POLLIN}}; - poll(fds, 1, timeout * 1000); + poll(fds, 1, timeout_ms); if (fds[0].revents & POLLIN) break; @@ -86,6 +129,7 @@ clock_new(struct particle *label, const char *date_format, const char *time_form m->label = label; m->date_format = strdup(date_format); m->time_format = strdup(time_format); + m->update_granularity = UPDATE_GRANULARITY_MINUTES; struct module *mod = module_common_new(); mod->private = m; From 0cde404d2a5145d2fd91e049e5b0b874cd199ad1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 24 Sep 2020 15:59:46 +0200 Subject: [PATCH 010/611] module/clock: detect when we need to update every second Closes #12 --- CHANGELOG.md | 2 ++ modules/clock.c | 25 +++++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a95f16..e42b0fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,8 @@ * YAML parsing error messages being replaced with a generic _“unknown error”_. * Memory leak when a YAML parsing error was encountered. +* clock: update every second when necessary + (https://codeberg.org/dnkl/yambar/issues/12). ### Security diff --git a/modules/clock.c b/modules/clock.c index 2ff4f28..6c0cb56 100644 --- a/modules/clock.c +++ b/modules/clock.c @@ -7,6 +7,7 @@ #include #define LOG_MODULE "clock" +#define LOG_ENABLE_DBG 0 #include "../log.h" #include "../bar/bar.h" #include "../config.h" @@ -129,8 +130,32 @@ clock_new(struct particle *label, const char *date_format, const char *time_form m->label = label; m->date_format = strdup(date_format); m->time_format = strdup(time_format); + + static const char *const seconds_formatters[] = { + "%c", + "%s", + "%S", + "%T", + "%r", + "%X", + }; + m->update_granularity = UPDATE_GRANULARITY_MINUTES; + for (size_t i = 0; + i < sizeof(seconds_formatters) / sizeof(seconds_formatters[0]); + i++) + { + if (strstr(time_format, seconds_formatters[i]) != NULL) { + m->update_granularity = UPDATE_GRANULARITY_SECONDS; + break; + } + } + + LOG_DBG("using %s update granularity", + (m->update_granularity == UPDATE_GRANULARITY_MINUTES + ? "minutes" : "seconds")); + struct module *mod = module_common_new(); mod->private = m; mod->run = &run; From a5faf4896ac98a2d46112a42ed4fdb09ad4cc154 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 24 Sep 2020 16:01:46 +0200 Subject: [PATCH 011/611] changelog: add issue link for alsa: volume as percentage --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e42b0fd..7c87a62 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,8 @@ ### Added * alsa: `percent` tag. This is an integer tag that represents the - current volume as a percentage value. + current volume as a percentage value + (https://codeberg.org/dnkl/yambar/issues/10). ### Deprecated From 41d0a17ea591afb3da7543b687fa8e5f1c7f14a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 24 Sep 2020 17:03:36 +0200 Subject: [PATCH 012/611] doc: yambar-tags: document the :hex and :oct suffixes --- doc/yambar-tags.5.scd | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/doc/yambar-tags.5.scd b/doc/yambar-tags.5.scd index 9b31712..32132cf 100644 --- a/doc/yambar-tags.5.scd +++ b/doc/yambar-tags.5.scd @@ -25,14 +25,22 @@ The available tag *types* are: | range : Value is an integer, with a minimum and maximum value associated with it. By default, the _string_ particle renders the value. The - _:min_ or _:max_ suffixes by be added to instead render the mininum - or maximum value (_\"{tag_name:min}\"_). + *:min* or *:max* suffixes may be added to instead render the + mininum or maximum value (_\"{tag_name:min}\"_). | realtime : Value is an integer that changes in a predictable manner (in "realtime"). This allows the particle to update itself periodically. Only supported by the *yambar-particle-progress-bar*(5). Other particles can still render - the tag's value. And, the _string_ particle recognizes the _:unit_ + the tag's value. And, the _string_ particle recognizes the *:unit* suffix, which will be translated to a "s" for a tag with "seconds" resolution, or "ms" for one with "milliseconds" resolution. +# FORMATTING + +As mentioned above, each tag type has a default representation that is +used when the tag is rendered by a string particle. + +All integer, floating point and boolean tag types can be modified to +instead be rendered in hexadecimal or octal form, by appending either +the *:hex* or *:oct* suffixes. For example, _\"{tag_name:hex}\"_. \ No newline at end of file From e25c42dc8721f5a171c636d772e1270b4b69e5fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 24 Sep 2020 17:28:48 +0200 Subject: [PATCH 013/611] module/river: meson: explicitly add wl_proto_src + wl_proto_headers to sources MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ‘river’ module depends on generated protocol sources. These are normally provided, indirectly, by the Wayland bar backend. But when building plugins as shared modules, the plugins no longer depend on the bar, and thus ‘river’ fails to build. Fix this by doing two things: * **Remove** the ‘river’ protocol from the ‘generic’ Wayland protocols * Explicitly add both the generic and the ‘river’ specific protocols to the ‘river’ module. --- bar/meson.build | 1 - modules/meson.build | 20 +++++++++++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/bar/meson.build b/bar/meson.build index 5df4b09..90f050b 100644 --- a/bar/meson.build +++ b/bar/meson.build @@ -17,7 +17,6 @@ if backend_wayland wl_proto_src = [] foreach prot : [ '../external/wlr-layer-shell-unstable-v1.xml', - '../external/river-status-unstable-v1.xml', wayland_protocols_datadir + '/stable/xdg-shell/xdg-shell.xml', wayland_protocols_datadir + '/unstable/xdg-output/xdg-output-unstable-v1.xml'] diff --git a/modules/meson.build b/modules/meson.build index 3efcc9e..e05362f 100644 --- a/modules/meson.build +++ b/modules/meson.build @@ -30,8 +30,26 @@ if backend_x11 endif if backend_wayland + river_proto_headers = [] + river_proto_src = [] + + foreach prot : ['../external/river-status-unstable-v1.xml'] + + river_proto_headers += custom_target( + prot.underscorify() + '-client-header', + output: '@BASENAME@.h', + input: prot, + command: [wscanner_prog, 'client-header', '@INPUT@', '@OUTPUT@']) + + river_proto_src += custom_target( + prot.underscorify() + '-private-code', + output: '@BASENAME@.c', + input: prot, + command: [wscanner_prog, 'private-code', '@INPUT@', '@OUTPUT@']) + endforeach + deps += { - 'river': [[], []], + 'river': [[wl_proto_src + wl_proto_headers + river_proto_src + river_proto_headers], []], } endif From d645aff48e5182b020dc87061aa70977122f2749 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 24 Sep 2020 17:47:20 +0200 Subject: [PATCH 014/611] module/clock: silence compiler warning: ensure timeout_ms is initialized --- modules/clock.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/clock.c b/modules/clock.c index 6c0cb56..e9f1694 100644 --- a/modules/clock.c +++ b/modules/clock.c @@ -76,7 +76,7 @@ run(struct module *mod) .tv_usec = _now.tv_nsec / 1000, }; - int timeout_ms; + int timeout_ms = 1000; switch (m->update_granularity) { case UPDATE_GRANULARITY_SECONDS: { From c55153bad88dd013fd4b9642521d2e13cea98213 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 24 Sep 2020 20:15:06 +0200 Subject: [PATCH 015/611] readme installation: mention -Dbackend-*=disabled|enabled --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index beb2b17..8395ad4 100644 --- a/README.md +++ b/README.md @@ -105,6 +105,11 @@ might also want `--prefix=/usr`): meson --buildtype=release ../.. ``` +Optionally, explicitly disable a backend (or enable, if you want a +configuration error if not all dependencies are met) by adding either +`-Dbackend-x11=disabled|enabled` or +`-Dbackend-wayland=disabled|enabled` to the meson command line. + Three, build it: ```sh ninja From fcfc7442c79a67782d590d27212673185083b496 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 1 Nov 2020 13:08:09 +0100 Subject: [PATCH 016/611] doc: yambar-modules: document the river module Closes #9 --- CHANGELOG.md | 2 ++ doc/yambar-modules.5.scd | 75 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c87a62..a8be77b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ * alsa: `percent` tag. This is an integer tag that represents the current volume as a percentage value (https://codeberg.org/dnkl/yambar/issues/10). +* river: added documentation + (https://codeberg.org/dnkl/yambar/issues/9). ### Deprecated diff --git a/doc/yambar-modules.5.scd b/doc/yambar-modules.5.scd index 3a099c6..330a9f8 100644 --- a/doc/yambar-modules.5.scd +++ b/doc/yambar-modules.5.scd @@ -641,6 +641,81 @@ bar: deco: {underline: {size: 2, color: ffffffff}} ``` +# RIVER + +This module uses river's (https://github.com/ifreund/river, a dynamic +tiling Wayland compositor) status protocol to provide information +about the river tags. + +It has an interface similar to the i3/sway module. + +The configuration for the river module speficies one _title_ particle, +which will be instantiated with tags representing the currently active +seat and the currently focused view's title. + +It also specifies a _content_ template particle, which is instantiated +once for all 32 river tags. This means you probably want to use a +*map* particle to hide unused river tags. + +## TAGS + +[[ *Name* +:[ *Type* +:[ *Description* +| id +: int +: River tag number +| visible +: bool +: True if the river tag is focused by at least one output (i.e. visible on at least one monitor). +| focused +: bool +: True if the river tag is _visible_ and has keyboard focus. +| occupied +: bool +: True if the river tag has views (i.e. windows). +| state +: string +: Set to *focused* if _focused_ is true, *unfocused* if _visible_ is true, but _focused_ is false, or *invisible* if the river tag is not visible on any monitors. +| seat +: string +: The name of the currently active seat (*title* particle only, see CONFIGURATION) +| title +: string +: The focused view's title (*title* particle only, see CONFIGURATION) + +## CONFIGURATION + +[[ *Name* +:[ *Type* +:[ *Req* +:[ *Description* +| title +: particle +: no +: Particle that will be instantiated with the _seat_ and _title_ tags. +| content +: particle +: yes +: Template particle that will be instantiated once for all of the 32 river tags. + +## EXAMPLES + +``` +bar: + left: + - river: + title: {string: { text: "{seat} - {title}" }} + content: + map: + tag: occupied + values: + false: {empty: {}} + true: + string: + margin: 5 + text: "{id}: {state}"``` + # SWAY-XKB This module uses *Sway* extenions to the I3 IPC API to monitor input From 1262f1b3d14a4f7092dfeb1547dc6752dfa190ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 9 Nov 2020 19:53:57 +0100 Subject: [PATCH 017/611] bar/wayland: use wl_*_release() instead of wl_*_destroy() This lets the compositor free up internal state. --- bar/wayland.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/bar/wayland.c b/bar/wayland.c index 7d87558..32bd87a 100644 --- a/bar/wayland.c +++ b/bar/wayland.c @@ -124,11 +124,11 @@ seat_destroy(struct seat *seat) if (seat->pointer.theme != NULL) wl_cursor_theme_destroy(seat->pointer.theme); if (seat->wl_pointer != NULL) - wl_pointer_destroy(seat->wl_pointer); + wl_pointer_release(seat->wl_pointer); if (seat->pointer.surface != NULL) wl_surface_destroy(seat->pointer.surface); if (seat->seat != NULL) - wl_seat_destroy(seat->seat); + wl_seat_release(seat->seat); } void * @@ -964,7 +964,7 @@ cleanup(struct bar *_bar) if (mon->xdg != NULL) zxdg_output_v1_destroy(mon->xdg); if (mon->output != NULL) - wl_output_destroy(mon->output); + wl_output_release(mon->output); tll_remove(backend->monitors, it); } @@ -987,8 +987,10 @@ cleanup(struct bar *_bar) wl_shm_destroy(backend->shm); if (backend->registry != NULL) wl_registry_destroy(backend->registry); - if (backend->display != NULL) + if (backend->display != NULL) { + wl_display_flush(backend->display); wl_display_disconnect(backend->display); + } /* Destroyed when freeing buffer list */ bar->pix = NULL; From 8a7e07af285d4e4c7be4086b1da25661b3ec9007 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 9 Nov 2020 20:27:42 +0100 Subject: [PATCH 018/611] =?UTF-8?q?module/mpd:=20don=E2=80=99t=20overload?= =?UTF-8?q?=20enum=20mpd=5Fstate?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We overloaded enum mpd_state to add an “offline” state. Don’t do this. We can detect the offline state by checking if our connection object is NULL. Closes #16 --- CHANGELOG.md | 2 ++ modules/mpd.c | 31 ++++++++++++++----------------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a8be77b..fa81d03 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,8 @@ * Memory leak when a YAML parsing error was encountered. * clock: update every second when necessary (https://codeberg.org/dnkl/yambar/issues/12). +* mpd: fix compilation with clang + (https://codeberg.org/dnkl/yambar/issues/16). ### Security diff --git a/modules/mpd.c b/modules/mpd.c index 743dc2e..f6ae445 100644 --- a/modules/mpd.c +++ b/modules/mpd.c @@ -27,13 +27,6 @@ #include "../config-verify.h" #include "../plugin.h" -enum state { - STATE_OFFLINE = 1000, - STATE_STOP = MPD_STATE_STOP, - STATE_PAUSE = MPD_STATE_PAUSE, - STATE_PLAY = MPD_STATE_PLAY, -}; - struct private { char *host; uint16_t port; @@ -41,7 +34,7 @@ struct private { struct mpd_connection *conn; - enum state state; + enum mpd_state state; bool repeat; bool random; bool consume; @@ -127,7 +120,7 @@ content(struct module *mod) /* Calculate what 'elapsed' is now */ uint64_t elapsed = m->elapsed.value; - if (m->state == STATE_PLAY) { + if (m->state == MPD_STATE_PLAY) { elapsed += timespec_diff_milli_seconds(&now, &m->elapsed.when); if (elapsed > m->duration) { LOG_DBG( @@ -148,15 +141,19 @@ content(struct module *mod) /* State as string */ const char *state_str = NULL; - switch (m->state) { - case STATE_OFFLINE: state_str = "offline"; break; - case STATE_STOP: state_str = "stopped"; break; - case STATE_PAUSE: state_str = "paused"; break; - case STATE_PLAY: state_str = "playing"; break; + if (m->conn == NULL) + state_str = "offline"; + else { + switch (m->state) { + case MPD_STATE_UNKNOWN: state_str = "unknown"; break; + case MPD_STATE_STOP: state_str = "stopped"; break; + case MPD_STATE_PAUSE: state_str = "paused"; break; + case MPD_STATE_PLAY: state_str = "playing"; break; + } } /* Tell particle to real-time track? */ - enum tag_realtime_unit realtime = m->state == STATE_PLAY + enum tag_realtime_unit realtime = m->state == MPD_STATE_PLAY ? TAG_REALTIME_MSECS : TAG_REALTIME_NONE; struct tag_set tags = { @@ -384,7 +381,7 @@ run(struct module *mod) free(m->album); m->album = NULL; free(m->artist); m->artist = NULL; free(m->title); m->title = NULL; - m->state = STATE_OFFLINE; + m->state = MPD_STATE_UNKNOWN; m->elapsed.value = m->duration = 0; m->elapsed.when.tv_sec = m->elapsed.when.tv_nsec = 0; mtx_unlock(&mod->lock); @@ -579,7 +576,7 @@ mpd_new(const char *host, uint16_t port, struct particle *label) priv->host = strdup(host); priv->port = port; priv->label = label; - priv->state = STATE_OFFLINE; + priv->state = MPD_STATE_UNKNOWN; priv->refresh_abort_fd = -1; struct module *mod = module_common_new(); From c0c5df0f6ac9a5f8b0e7dc3dbdba6124a3129e96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 14 Nov 2020 23:02:18 +0100 Subject: [PATCH 019/611] =?UTF-8?q?module/battery:=20map=20=E2=80=9CNot=20?= =?UTF-8?q?charging=E2=80=9D=20to=20STATE=5FDISCHARGING?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/battery.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/battery.c b/modules/battery.c index ce0d04c..fda099b 100644 --- a/modules/battery.c +++ b/modules/battery.c @@ -297,6 +297,8 @@ update_status(struct module *mod, int capacity_fd, int energy_fd, int power_fd, state = STATE_CHARGING; else if (strcmp(status, "Discharging") == 0) state = STATE_DISCHARGING; + else if (strcmp(state, "Not charging") == 0) + state = STATE_DISCHARGING; else if (strcmp(status, "Unknown") == 0) state = STATE_DISCHARGING; else { From cadf227bc6a9ebe759e961ccbc832d506c4bdc45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 14 Nov 2020 23:03:11 +0100 Subject: [PATCH 020/611] =?UTF-8?q?module/battery:=20doh!=20fix=20spelling?= =?UTF-8?q?=20of=20=E2=80=98status=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/battery.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/battery.c b/modules/battery.c index fda099b..4759a99 100644 --- a/modules/battery.c +++ b/modules/battery.c @@ -297,7 +297,7 @@ update_status(struct module *mod, int capacity_fd, int energy_fd, int power_fd, state = STATE_CHARGING; else if (strcmp(status, "Discharging") == 0) state = STATE_DISCHARGING; - else if (strcmp(state, "Not charging") == 0) + else if (strcmp(status, "Not charging") == 0) state = STATE_DISCHARGING; else if (strcmp(status, "Unknown") == 0) state = STATE_DISCHARGING; From 19fe2f5a6f56c85a4ca0496fd36a84157811db37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 25 Oct 2020 15:20:28 +0100 Subject: [PATCH 021/611] module/script: wip: new plugin, reads data from a user provided script/binary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This module exec’s a script (or binary), specified by the ‘path’ attribute in the configuration. It then reads tags from the script’s stdout. The format of the output is: tag|type|value tag|type|value I.e. the script writes N tags followed by an empty line. This constitutes a transaction. When a new transaction is received, its tags replaces *all* previous tags. --- modules/meson.build | 1 + modules/script.c | 486 ++++++++++++++++++++++++++++++++++++++++++++ plugin.c | 2 + 3 files changed, 489 insertions(+) create mode 100644 modules/script.c diff --git a/modules/meson.build b/modules/meson.build index e05362f..ed1683e 100644 --- a/modules/meson.build +++ b/modules/meson.build @@ -19,6 +19,7 @@ deps = { 'mpd': [[], [mpd]], 'network': [[], []], 'removables': [[], [dynlist, udev]], + 'script': [[], []], 'sway_xkb': [['i3-common.c', 'i3-common.h'], [dynlist, json]], } diff --git a/modules/script.c b/modules/script.c new file mode 100644 index 0000000..840578e --- /dev/null +++ b/modules/script.c @@ -0,0 +1,486 @@ +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#define LOG_MODULE "script" +#define LOG_ENABLE_DBG 1 +#include "../log.h" +#include "../config.h" +#include "../config-verify.h" +#include "../module.h" +#include "../plugin.h" + +struct private { + char *path; + struct particle *content; + + struct tag_set tags; + + struct { + char *data; + size_t sz; + size_t idx; + } recv_buf; +}; + +static void +destroy(struct module *mod) +{ + struct private *m = mod->private; + m->content->destroy(m->content); + tag_set_destroy(&m->tags); + free(m->recv_buf.data); + free(m->path); + free(m); + module_default_destroy(mod); +} + +static struct exposable * +content(struct module *mod) +{ + const struct private *m = mod->private; + + mtx_lock(&mod->lock); + struct exposable *e = m->content->instantiate(m->content, &m->tags); + mtx_unlock(&mod->lock); + + return e; +} + +static struct tag * +process_line(struct module *mod, const char *line, size_t len) +{ + const char *_name = line; + LOG_INFO("LINE: %.*s", (int)len, line); + + const char *type = memchr(line, '|', len); + if (type == NULL) + goto bad_tag; + + size_t name_len = type - _name; + type++; + + const char *_value = memchr(type, '|', len - name_len - 1); + if (_value == NULL) + goto bad_tag; + + size_t type_len = _value - type; + _value++; + + size_t value_len = line + len - _value; + + LOG_DBG("%.*s: name=\"%.*s\", type=\"%.*s\", value=\"%.*s\"", + (int)len, line, + (int)name_len, _name, (int)type_len, type, (int)value_len, _value); + + char *name = malloc(name_len + 1); + memcpy(name, _name, name_len); + name[name_len] = '\0'; + + struct tag *tag = NULL; + + if (type_len == 6 && memcmp(type, "string", 6) == 0) + tag = tag_new_string(mod, name, _value); + + else if (type_len == 3 && memcmp(type, "int", 3) == 0) { + long value = strtol(_value, NULL, 0); + tag = tag_new_int(mod, name, value); + } + + else if (type_len == 4 && memcmp(type, "bool", 4) == 0) { + bool value = strtol(_value, NULL, 0); + tag = tag_new_bool(mod, name, value); + } + + else if (type_len == 5 && memcmp(type, "float", 5) == 0) { + double value = strtod(_value, NULL); + tag = tag_new_float(mod, name, value); + } + + else if ((type_len > 6 && memcmp(type, "range:", 6) == 0) || + (type_len > 9 && memcmp(type, "realtime:", 9 == 0))) + { + const char *_start = type + 6; + const char *split = memchr(_start, '-', type_len - 6); + + if (split == NULL || split == _start || (split + 1) - type >= type_len) { + free(name); + goto bad_tag; + } + + const char *_end = split + 1; + + size_t start_len = split - _start; + size_t end_len = type + type_len - _end; + + long start = 0; + for (size_t i = 0; i < start_len; i++) { + if (!(_start[i] >= '0' && _start[i] <= '9')) { + free(name); + goto bad_tag; + } + + start *= 10; + start |= _start[i] - '0'; + } + + long end = 0; + for (size_t i = 0; i < end_len; i++) { + if (!(_end[i] >= '0' && _end[i] < '9')) { + free(name); + goto bad_tag; + } + + end *= 10; + end |= _end[i] - '0'; + } + + if (type_len > 9 && memcmp(type, "realtime:", 9) == 0) { + free(name); + LOG_WARN("unimplemented: realtime tag"); + goto bad_tag; + } + + long value = strtol(_value, NULL, 0); + tag = tag_new_int_range(mod, name, value, start, end); + } + + else { + free(name); + goto bad_tag; + } + + free(name); + return tag; + +bad_tag: + LOG_ERR("invalid: %.*s", (int)len, line); + return NULL; +} + +static void +process_transaction(struct module *mod, size_t size) +{ + struct private *m = mod->private; + mtx_lock(&mod->lock); + + size_t left = size; + const char *line = m->recv_buf.data; + + size_t line_count = 0; + { + const char *p = line; + while ((p = memchr(p, '\n', size - (p - line))) != NULL) { + p++; + line_count++; + } + } + + tag_set_destroy(&m->tags); + m->tags.tags = calloc(line_count, sizeof(m->tags.tags[0])); + m->tags.count = line_count; + + size_t idx = 0; + + while (left > 0) { + char *line_end = memchr(line, '\n', left); + assert(line_end != NULL); + + size_t line_len = line_end - line; + + struct tag *tag = process_line(mod, line, line_len); + if (tag != NULL) + m->tags.tags[idx++] = tag; + + left -= line_len + 1; + line += line_len + 1; + } + + m->tags.count = idx; + + mtx_unlock(&mod->lock); + mod->bar->refresh(mod->bar); +} + +static bool +data_received(struct module *mod, const char *data, size_t len) +{ + struct private *m = mod->private; + + if (len > m->recv_buf.sz - m->recv_buf.idx) { + size_t new_sz = m->recv_buf.sz == 0 ? 1024 : m->recv_buf.sz * 2; + char *new_buf = realloc(m->recv_buf.data, new_sz); + + if (new_buf == NULL) + return false; + + m->recv_buf.data = new_buf; + m->recv_buf.sz = new_sz; + } + + assert(m->recv_buf.sz >= m->recv_buf.idx); + assert(m->recv_buf.sz - m->recv_buf.idx >= len); + + memcpy(&m->recv_buf.data[m->recv_buf.idx], data, len); + m->recv_buf.idx += len; + + const char *eot = memmem(m->recv_buf.data, m->recv_buf.idx, "\n\n", 2); + if (eot == NULL) { + /* End of transaction not yet available */ + return true; + } + + const size_t transaction_size = eot - m->recv_buf.data + 1; + process_transaction(mod, transaction_size); + + assert(m->recv_buf.idx >= transaction_size + 1); + memmove(m->recv_buf.data, + &m->recv_buf.data[transaction_size + 1], + m->recv_buf.idx - (transaction_size + 1)); + m->recv_buf.idx -= transaction_size + 1; + + return true; +} + +static int +run_loop(struct module *mod, int comm_fd) +{ + //struct private *m = mod; + + sigset_t mask; + sigemptyset(&mask); + sigaddset(&mask, SIGCHLD); + + /* Block normal signal handling - we're using a signalfd instead */ + sigset_t original_mask; + if (pthread_sigmask(SIG_BLOCK, &mask, &original_mask) < 0) { + LOG_ERRNO("failed to block SIGCHLD"); + return -1; + } + + int sig_fd = signalfd(-1, &mask, SFD_NONBLOCK | SFD_CLOEXEC); + if (sig_fd < 0) { + LOG_ERRNO("failed to create signal FD"); + pthread_sigmask(SIG_SETMASK, &original_mask, NULL); + return -1; + } + + int ret = 0; + + while (true) { + struct pollfd fds[] = { + {.fd = mod->abort_fd, .events = POLLIN}, + {.fd = sig_fd, .events = POLLIN}, + {.fd = comm_fd, .events = POLLIN}, + }; + + int r = poll(fds, sizeof(fds) / sizeof(fds[0]), -1); + if (r < 0) { + if (errno == EINTR) + continue; + LOG_ERRNO("failed to poll"); + break; + } + + if (fds[2].revents & POLLIN) { + char data[4096]; + ssize_t amount = read(comm_fd, data, sizeof(data)); + if (amount < 0) { + LOG_ERRNO("failed to read from script"); + break; + } + + data_received(mod, data, amount); + } + + if (fds[0].revents & POLLHUP) { + /* Aborted */ + break; + } + + if (fds[1].revents & POLLHUP) { + LOG_ERR("signal FD closed unexpectedly"); + ret = 1; + break; + } + + if (fds[2].revents & POLLHUP) { + /* Child's stdout closed */ + break; + } + + if (fds[0].revents & POLLIN) + break; + + if (fds[1].revents & POLLIN) { + struct signalfd_siginfo info; + ssize_t amount = read(sig_fd, &info, sizeof(info)); + + if (amount < 0) { + LOG_ERRNO("failed to read from signal FD"); + break; + } + + assert(info.ssi_signo == SIGCHLD); + LOG_WARN("script died"); + break; + } + } + + close(sig_fd); + pthread_sigmask(SIG_SETMASK, &original_mask, NULL); + return ret; +} + +static int +run(struct module *mod) +{ + struct private *m = mod->private; + + int exec_pipe[2]; + if (pipe2(exec_pipe, O_CLOEXEC) < 0) { + LOG_ERRNO("failed to create pipe"); + return -1; + } + + int comm_pipe[2]; + if (pipe(comm_pipe) < 0) { + LOG_ERRNO("failed to create stdin/stdout redirection pipe"); + close(exec_pipe[0]); + close(exec_pipe[1]); + return -1; + } + + int pid = fork(); + if (pid < 0) { + LOG_ERRNO("failed to fork"); + close(comm_pipe[0]); + close(comm_pipe[1]); + close(exec_pipe[0]); + close(exec_pipe[1]); + return -1; + } + + if (pid == 0) { + /* Child */ + + setsid(); + setpgid(0, 0); + + /* Close pipe read ends */ + close(exec_pipe[0]); + close(comm_pipe[0]); + + /* Re-direct stdin/stdout/stderr */ + int dev_null = open("/dev/null", O_RDWR); + if (dev_null < 0) + goto fail; + + if (dup2(dev_null, STDIN_FILENO) < 0 || + dup2(dev_null, STDERR_FILENO) < 0 || + dup2(comm_pipe[1], STDOUT_FILENO) < 0) + { + goto fail; + } + + close(comm_pipe[1]); + + char *const argv[] = {NULL}; + execvp(m->path, argv); + + fail: + write(exec_pipe[1], &errno, sizeof(errno)); + close(exec_pipe[1]); + close(comm_pipe[1]); + _exit(errno); + } + + /* Close pipe write ends */ + close(exec_pipe[1]); + close(comm_pipe[1]); + + int _errno; + static_assert(sizeof(_errno) == sizeof(errno), "errno size mismatch"); + + int r = read(exec_pipe[0], &_errno, sizeof(_errno)); + close(exec_pipe[0]); + + if (r < 0) { + LOG_ERRNO("failed to read from pipe"); + return -1; + } + + if (r > 0) { + LOG_ERRNO_P("%s: failed to start", _errno, m->path); + waitpid(pid, NULL, 0); + return -1; + } + + LOG_WARN("child running under PID=%u", pid); + + int ret = run_loop(mod, comm_pipe[0]); + + close(comm_pipe[0]); + if (waitpid(pid, NULL, WNOHANG) == 0) { + LOG_WARN("sending SIGTERM to PGRP=%u", pid); + killpg(pid, SIGTERM); + + /* TODO: send SIGKILL after X seconds */ + waitpid(pid, NULL, 0); + } + + return ret; +} + +static struct module * +script_new(const char *path, struct particle *_content) +{ + struct private *m = calloc(1, sizeof(*m)); + m->path = strdup(path); + m->content = _content; + + struct module *mod = module_common_new(); + mod->private = m; + mod->run = &run; + mod->destroy = &destroy; + mod->content = &content; + return mod; +} + +static struct module * +from_conf(const struct yml_node *node, struct conf_inherit inherited) +{ + const struct yml_node *run = yml_get_value(node, "path"); + const struct yml_node *c = yml_get_value(node, "content"); + return script_new(yml_value_as_string(run), conf_to_particle(c, inherited)); +} + +static bool +verify_conf(keychain_t *chain, const struct yml_node *node) +{ + static const struct attr_info attrs[] = { + {"path", true, &conf_verify_string}, + MODULE_COMMON_ATTRS, + }; + + return conf_verify_dict(chain, node, attrs); +} + +const struct module_iface module_script_iface = { + .verify_conf = &verify_conf, + .from_conf = &from_conf, +}; + +#if defined(CORE_PLUGINS_AS_SHARED_LIBRARIES) +extern const struct module_iface iface __attribute__((weak, alias("module_script_iface"))); +#endif diff --git a/plugin.c b/plugin.c index 7ae2fb4..05a2c1a 100644 --- a/plugin.c +++ b/plugin.c @@ -43,6 +43,7 @@ EXTERN_MODULE(network); EXTERN_MODULE(removables); EXTERN_MODULE(river); EXTERN_MODULE(sway_xkb); +EXTERN_MODULE(script); EXTERN_MODULE(xkb); EXTERN_MODULE(xwindow); @@ -119,6 +120,7 @@ init(void) REGISTER_CORE_MODULE(river, river); #endif REGISTER_CORE_MODULE(sway-xkb, sway_xkb); + REGISTER_CORE_MODULE(script, script); #if defined(HAVE_PLUGIN_xkb) REGISTER_CORE_MODULE(xkb, xkb); #endif From 99aa8dea820229c1d365b648fa08e9059f28e2d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 25 Oct 2020 15:24:06 +0100 Subject: [PATCH 022/611] main: block SIGCHLD globally This allows threads (modules) to create signal FDs for SIGCHLD. --- main.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/main.c b/main.c index d93b869..61a89f1 100644 --- a/main.c +++ b/main.c @@ -279,6 +279,11 @@ main(int argc, char *const *argv) sigaction(SIGINT, &sa, NULL); sigaction(SIGTERM, &sa, NULL); + sigset_t proc_signal_mask; + sigemptyset(&proc_signal_mask); + sigaddset(&proc_signal_mask, SIGCHLD); + sigprocmask(SIG_BLOCK, &proc_signal_mask, NULL); + /* Block SIGINT (this is under the assumption that threads inherit * the signal mask */ sigset_t signal_mask; @@ -326,7 +331,7 @@ main(int argc, char *const *argv) thrd_t bar_thread; thrd_create(&bar_thread, (int (*)(void *))bar->run, bar); - /* Now unblock. We should be only thread receiving SIGINT */ + /* Now unblock. We should be only thread receiving SIGINT/SIGTERM */ pthread_sigmask(SIG_UNBLOCK, &signal_mask, NULL); if (pid_file != NULL) { From 80d0025e6480bab31c7db10566acdd3bdd6b019b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 25 Oct 2020 15:41:02 +0100 Subject: [PATCH 023/611] module/script: drop setsid() call --- modules/script.c | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/script.c b/modules/script.c index 840578e..c27633f 100644 --- a/modules/script.c +++ b/modules/script.c @@ -374,7 +374,6 @@ run(struct module *mod) if (pid == 0) { /* Child */ - setsid(); setpgid(0, 0); /* Close pipe read ends */ From fbaa208768d9294cc77ba587504cb06bb11183cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 25 Oct 2020 16:04:17 +0100 Subject: [PATCH 024/611] module/script: disable debug output --- modules/script.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/script.c b/modules/script.c index c27633f..9e5babe 100644 --- a/modules/script.c +++ b/modules/script.c @@ -13,7 +13,7 @@ #include #define LOG_MODULE "script" -#define LOG_ENABLE_DBG 1 +#define LOG_ENABLE_DBG 0 #include "../log.h" #include "../config.h" #include "../config-verify.h" From 430e505bd24f04d4667d01f6da84f04bcdce2bff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 25 Oct 2020 16:04:47 +0100 Subject: [PATCH 025/611] =?UTF-8?q?module/script:=20remove=20debug=20outpu?= =?UTF-8?q?t=20that=20wasn=E2=80=99t=20actually=20using=20LOG=5FDBG()?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/script.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/modules/script.c b/modules/script.c index 9e5babe..ae92b88 100644 --- a/modules/script.c +++ b/modules/script.c @@ -61,7 +61,6 @@ static struct tag * process_line(struct module *mod, const char *line, size_t len) { const char *_name = line; - LOG_INFO("LINE: %.*s", (int)len, line); const char *type = memchr(line, '|', len); if (type == NULL) @@ -425,13 +424,12 @@ run(struct module *mod) return -1; } - LOG_WARN("child running under PID=%u", pid); + LOG_DBG("script running under PID=%u", pid); int ret = run_loop(mod, comm_pipe[0]); close(comm_pipe[0]); if (waitpid(pid, NULL, WNOHANG) == 0) { - LOG_WARN("sending SIGTERM to PGRP=%u", pid); killpg(pid, SIGTERM); /* TODO: send SIGKILL after X seconds */ From f2814f786e18210b013c0a3d8a1e9bc191a17969 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 25 Oct 2020 16:05:20 +0100 Subject: [PATCH 026/611] =?UTF-8?q?module/script:=20copy=20=E2=80=98value?= =?UTF-8?q?=E2=80=99=20to=20a=20NULL-terminated=20string?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This ensures e.g. strtol() doesn’t parse data beyond current tag/value. --- modules/script.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/modules/script.c b/modules/script.c index ae92b88..2235083 100644 --- a/modules/script.c +++ b/modules/script.c @@ -60,6 +60,9 @@ content(struct module *mod) static struct tag * process_line(struct module *mod, const char *line, size_t len) { + char *name = NULL; + char *value = NULL; + const char *_name = line; const char *type = memchr(line, '|', len); @@ -82,10 +85,14 @@ process_line(struct module *mod, const char *line, size_t len) (int)len, line, (int)name_len, _name, (int)type_len, type, (int)value_len, _value); - char *name = malloc(name_len + 1); + name = malloc(name_len + 1); memcpy(name, _name, name_len); name[name_len] = '\0'; + value = malloc(value_len + 1); + memcpy(value, _value, value_len); + value[value_len] = '\0'; + struct tag *tag = NULL; if (type_len == 6 && memcmp(type, "string", 6) == 0) @@ -112,10 +119,8 @@ process_line(struct module *mod, const char *line, size_t len) const char *_start = type + 6; const char *split = memchr(_start, '-', type_len - 6); - if (split == NULL || split == _start || (split + 1) - type >= type_len) { - free(name); + if (split == NULL || split == _start || (split + 1) - type >= type_len) goto bad_tag; - } const char *_end = split + 1; @@ -124,10 +129,8 @@ process_line(struct module *mod, const char *line, size_t len) long start = 0; for (size_t i = 0; i < start_len; i++) { - if (!(_start[i] >= '0' && _start[i] <= '9')) { - free(name); + if (!(_start[i] >= '0' && _start[i] <= '9')) goto bad_tag; - } start *= 10; start |= _start[i] - '0'; @@ -135,17 +138,14 @@ process_line(struct module *mod, const char *line, size_t len) long end = 0; for (size_t i = 0; i < end_len; i++) { - if (!(_end[i] >= '0' && _end[i] < '9')) { - free(name); + if (!(_end[i] >= '0' && _end[i] < '9')) goto bad_tag; - } end *= 10; end |= _end[i] - '0'; } if (type_len > 9 && memcmp(type, "realtime:", 9) == 0) { - free(name); LOG_WARN("unimplemented: realtime tag"); goto bad_tag; } @@ -155,15 +155,17 @@ process_line(struct module *mod, const char *line, size_t len) } else { - free(name); goto bad_tag; } free(name); + free(value); return tag; bad_tag: LOG_ERR("invalid: %.*s", (int)len, line); + free(name); + free(value); return NULL; } From c911d20e73d273f9f2506a3abb63316abab59554 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 25 Oct 2020 16:06:01 +0100 Subject: [PATCH 027/611] module/script: add debug logging of raw received data --- modules/script.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/script.c b/modules/script.c index 2235083..25323eb 100644 --- a/modules/script.c +++ b/modules/script.c @@ -301,6 +301,8 @@ run_loop(struct module *mod, int comm_fd) break; } + LOG_DBG("recv: \"%.*s\"", (int)amount, data); + data_received(mod, data, amount); } From 8702378c745980db947bb8d93f3fb2502f142055 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 25 Oct 2020 16:06:16 +0100 Subject: [PATCH 028/611] module/script: restore signal handlers and mask in child process MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes the issue where `killpg()` didn’t manage to kill the sub-process tree. --- modules/script.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/modules/script.c b/modules/script.c index 25323eb..5c04e70 100644 --- a/modules/script.c +++ b/modules/script.c @@ -377,6 +377,20 @@ run(struct module *mod) if (pid == 0) { /* Child */ + /* Restore signal handlers */ + + sigset_t mask; + sigemptyset(&mask); + + const struct sigaction sa = {.sa_handler = SIG_DFL}; + if (sigaction(SIGINT, &sa, NULL) < 0 || + sigaction(SIGTERM, &sa, NULL) < 0 || + sigaction(SIGCHLD, &sa, NULL) < 0 || + sigprocmask(SIG_SETMASK, &mask, NULL) < 0) + { + goto fail; + } + setpgid(0, 0); /* Close pipe read ends */ From 08bac77907d71acc366e5617ae2b9e7603b71d75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 25 Oct 2020 18:29:52 +0100 Subject: [PATCH 029/611] module/script: script arguments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add new config attribute, ‘args’. This is a list of strings, that will be passed as arguments to the script. --- modules/script.c | 47 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 4 deletions(-) diff --git a/modules/script.c b/modules/script.c index 5c04e70..024654d 100644 --- a/modules/script.c +++ b/modules/script.c @@ -22,6 +22,9 @@ struct private { char *path; + size_t argc; + char **argv; + struct particle *content; struct tag_set tags; @@ -39,6 +42,10 @@ destroy(struct module *mod) struct private *m = mod->private; m->content->destroy(m->content); tag_set_destroy(&m->tags); + + for (size_t i = 0; i < m->argc; i++) + free(m->argv[i]); + free(m->argv); free(m->recv_buf.data); free(m->path); free(m); @@ -377,8 +384,13 @@ run(struct module *mod) if (pid == 0) { /* Child */ - /* Restore signal handlers */ + char *argv[1 + m->argc + 1]; + argv[0] = m->path; + for (size_t i = 0; i < m->argc; i++) + argv[i + 1] = m->argv[i]; + argv[1 + m->argc] = NULL; + /* Restore signal handlers */ sigset_t mask; sigemptyset(&mask); @@ -411,7 +423,6 @@ run(struct module *mod) close(comm_pipe[1]); - char *const argv[] = {NULL}; execvp(m->path, argv); fail: @@ -458,11 +469,16 @@ run(struct module *mod) } static struct module * -script_new(const char *path, struct particle *_content) +script_new(const char *path, size_t argc, const char *const argv[static argc], + struct particle *_content) { struct private *m = calloc(1, sizeof(*m)); m->path = strdup(path); m->content = _content; + m->argc = argc; + m->argv = malloc(argc * sizeof(m->argv[0])); + for (size_t i = 0; i < argc; i++) + m->argv[i] = strdup(argv[i]); struct module *mod = module_common_new(); mod->private = m; @@ -476,8 +492,30 @@ static struct module * from_conf(const struct yml_node *node, struct conf_inherit inherited) { const struct yml_node *run = yml_get_value(node, "path"); + const struct yml_node *args = yml_get_value(node, "args"); const struct yml_node *c = yml_get_value(node, "content"); - return script_new(yml_value_as_string(run), conf_to_particle(c, inherited)); + + size_t argc = args != NULL ? yml_list_length(args) : 0; + const char *argv[argc]; + + if (args != NULL) { + size_t i = 0; + for (struct yml_list_iter iter = yml_list_iter(args); + iter.node != NULL; + yml_list_next(&iter), i++) + { + argv[i] = yml_value_as_string(iter.node); + } + } + + return script_new( + yml_value_as_string(run), argc, argv, conf_to_particle(c, inherited)); +} + +static bool +conf_verify_args(keychain_t *chain, const struct yml_node *node) +{ + return conf_verify_list(chain, node, &conf_verify_string); } static bool @@ -485,6 +523,7 @@ verify_conf(keychain_t *chain, const struct yml_node *node) { static const struct attr_info attrs[] = { {"path", true, &conf_verify_string}, + {"args", false, &conf_verify_args}, MODULE_COMMON_ATTRS, }; From 73407853e4a3c1b3cbfec41b8eabb5f71fbe677f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 28 Oct 2020 19:05:03 +0100 Subject: [PATCH 030/611] =?UTF-8?q?module/script:=20can=E2=80=99t=20use=20?= =?UTF-8?q?logical=20OR=20when=20building=20a=20base=2010=20number?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/script.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/script.c b/modules/script.c index 024654d..b518b9f 100644 --- a/modules/script.c +++ b/modules/script.c @@ -140,7 +140,7 @@ process_line(struct module *mod, const char *line, size_t len) goto bad_tag; start *= 10; - start |= _start[i] - '0'; + start += _start[i] - '0'; } long end = 0; @@ -149,7 +149,7 @@ process_line(struct module *mod, const char *line, size_t len) goto bad_tag; end *= 10; - end |= _end[i] - '0'; + end += _end[i] - '0'; } if (type_len > 9 && memcmp(type, "realtime:", 9) == 0) { From 328ebe8fe94cdcf016ade4122d098f28786528ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 29 Oct 2020 18:02:45 +0100 Subject: [PATCH 031/611] bar/wayland: plug memory leak: free seat name --- bar/wayland.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bar/wayland.c b/bar/wayland.c index 32bd87a..35355c4 100644 --- a/bar/wayland.c +++ b/bar/wayland.c @@ -121,6 +121,8 @@ seat_destroy(struct seat *seat) if (seat == NULL) return; + free(seat->name); + if (seat->pointer.theme != NULL) wl_cursor_theme_destroy(seat->pointer.theme); if (seat->wl_pointer != NULL) From 008235d904801466a6458c07162f2d77022bfce9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 29 Oct 2020 18:03:14 +0100 Subject: [PATCH 032/611] bar/wayland: close command pipe FDs in cleanup --- bar/wayland.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/bar/wayland.c b/bar/wayland.c index 35355c4..f4b3cce 100644 --- a/bar/wayland.c +++ b/bar/wayland.c @@ -136,7 +136,9 @@ seat_destroy(struct seat *seat) void * bar_backend_wayland_new(void) { - return calloc(1, sizeof(struct wayland_backend)); + struct wayland_backend *backend = calloc(1, sizeof(struct wayland_backend)); + backend->pipe_fds[0] = backend->pipe_fds[1] = -1; + return backend; } static void @@ -949,6 +951,11 @@ cleanup(struct bar *_bar) struct private *bar = _bar->private; struct wayland_backend *backend = bar->backend.data; + if (backend->pipe_fds[0] >= 0) + close(backend->pipe_fds[0]); + if (backend->pipe_fds[1] >= 0) + close(backend->pipe_fds[1]); + tll_foreach(backend->buffers, it) { if (it->item.wl_buf != NULL) wl_buffer_destroy(it->item.wl_buf); From fb0d443e1d04e930f6c8be96888e49370d4a1983 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 29 Oct 2020 18:03:32 +0100 Subject: [PATCH 033/611] =?UTF-8?q?module/script:=20plug=20memory=20leak:?= =?UTF-8?q?=20free=20=E2=80=98tags=E2=80=99=20array?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/script.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/modules/script.c b/modules/script.c index b518b9f..5f21df8 100644 --- a/modules/script.c +++ b/modules/script.c @@ -41,7 +41,10 @@ destroy(struct module *mod) { struct private *m = mod->private; m->content->destroy(m->content); + + struct tag **tag_array = m->tags.tags; tag_set_destroy(&m->tags); + free(tag_array); for (size_t i = 0; i < m->argc; i++) free(m->argv[i]); @@ -194,7 +197,10 @@ process_transaction(struct module *mod, size_t size) } } + struct tag **old_tag_array = m->tags.tags; tag_set_destroy(&m->tags); + free(old_tag_array); + m->tags.tags = calloc(line_count, sizeof(m->tags.tags[0])); m->tags.count = line_count; From 7f1ffd126b3eb8156324405a0a1c5b1be74e2218 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 29 Oct 2020 18:03:52 +0100 Subject: [PATCH 034/611] module/script: close script communication pipe FD on error --- modules/script.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/script.c b/modules/script.c index 5f21df8..851f153 100644 --- a/modules/script.c +++ b/modules/script.c @@ -450,11 +450,13 @@ run(struct module *mod) if (r < 0) { LOG_ERRNO("failed to read from pipe"); + close(comm_pipe[0]); return -1; } if (r > 0) { LOG_ERRNO_P("%s: failed to start", _errno, m->path); + close(comm_pipe[0]); waitpid(pid, NULL, 0); return -1; } From 9945fce2d2454ac6733a84769bba5705b31e5641 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 29 Oct 2020 18:04:13 +0100 Subject: [PATCH 035/611] module/script: explicitly ignore return value of write(3) --- modules/script.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/script.c b/modules/script.c index 851f153..a005283 100644 --- a/modules/script.c +++ b/modules/script.c @@ -432,7 +432,7 @@ run(struct module *mod) execvp(m->path, argv); fail: - write(exec_pipe[1], &errno, sizeof(errno)); + (void)!write(exec_pipe[1], &errno, sizeof(errno)); close(exec_pipe[1]); close(comm_pipe[1]); _exit(errno); From 37447cd955fa42f5fc7a0767ff53c4ebfc82239c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 30 Oct 2020 10:55:27 +0100 Subject: [PATCH 036/611] =?UTF-8?q?module/script:=20initialize=20string=20?= =?UTF-8?q?tags=20from=20our=20NULL-terminated=20=E2=80=98value=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `_value` is a pointer into the receive buffer, and may not be NULL terminated, and may also contain data belonging to the next tag. --- modules/script.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/script.c b/modules/script.c index a005283..b3e3fd4 100644 --- a/modules/script.c +++ b/modules/script.c @@ -106,7 +106,7 @@ process_line(struct module *mod, const char *line, size_t len) struct tag *tag = NULL; if (type_len == 6 && memcmp(type, "string", 6) == 0) - tag = tag_new_string(mod, name, _value); + tag = tag_new_string(mod, name, value); else if (type_len == 3 && memcmp(type, "int", 3) == 0) { long value = strtol(_value, NULL, 0); From 5c9030129dd438d32d9bc9a0351540238dc57356 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 30 Oct 2020 11:00:40 +0100 Subject: [PATCH 037/611] =?UTF-8?q?module/script:=20use=20NULL=20terminate?= =?UTF-8?q?d=20=E2=80=98value=E2=80=99=20when=20converting=20to=20int/bool?= =?UTF-8?q?/float?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/script.c | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/modules/script.c b/modules/script.c index b3e3fd4..1ee4b72 100644 --- a/modules/script.c +++ b/modules/script.c @@ -108,20 +108,14 @@ process_line(struct module *mod, const char *line, size_t len) if (type_len == 6 && memcmp(type, "string", 6) == 0) tag = tag_new_string(mod, name, value); - else if (type_len == 3 && memcmp(type, "int", 3) == 0) { - long value = strtol(_value, NULL, 0); - tag = tag_new_int(mod, name, value); - } + else if (type_len == 3 && memcmp(type, "int", 3) == 0) + tag = tag_new_int(mod, name, strtol(value, NULL, 0)); - else if (type_len == 4 && memcmp(type, "bool", 4) == 0) { - bool value = strtol(_value, NULL, 0); - tag = tag_new_bool(mod, name, value); - } + else if (type_len == 4 && memcmp(type, "bool", 4) == 0) + tag = tag_new_bool(mod, name, strtol(value, NULL, 0)); - else if (type_len == 5 && memcmp(type, "float", 5) == 0) { - double value = strtod(_value, NULL); - tag = tag_new_float(mod, name, value); - } + else if (type_len == 5 && memcmp(type, "float", 5) == 0) + tag = tag_new_float(mod, name, strtod(value, NULL)); else if ((type_len > 6 && memcmp(type, "range:", 6) == 0) || (type_len > 9 && memcmp(type, "realtime:", 9 == 0))) @@ -160,8 +154,7 @@ process_line(struct module *mod, const char *line, size_t len) goto bad_tag; } - long value = strtol(_value, NULL, 0); - tag = tag_new_int_range(mod, name, value, start, end); + tag = tag_new_int_range(mod, name, strtol(value, NULL, 0), start, end); } else { From 2bb70c6fcbc9d0332715b521b59f65d07817e0c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 30 Oct 2020 11:53:25 +0100 Subject: [PATCH 038/611] =?UTF-8?q?module/script:=20require=20=E2=80=98pat?= =?UTF-8?q?h=E2=80=99=20to=20be=20an=20absolute=20path?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/script.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/modules/script.c b/modules/script.c index 1ee4b72..b4e165b 100644 --- a/modules/script.c +++ b/modules/script.c @@ -492,7 +492,7 @@ script_new(const char *path, size_t argc, const char *const argv[static argc], static struct module * from_conf(const struct yml_node *node, struct conf_inherit inherited) { - const struct yml_node *run = yml_get_value(node, "path"); + const struct yml_node *path = yml_get_value(node, "path"); const struct yml_node *args = yml_get_value(node, "args"); const struct yml_node *c = yml_get_value(node, "content"); @@ -510,7 +510,22 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) } return script_new( - yml_value_as_string(run), argc, argv, conf_to_particle(c, inherited)); + yml_value_as_string(path), argc, argv, conf_to_particle(c, inherited)); +} + +static bool +conf_verify_path(keychain_t *chain, const struct yml_node *node) +{ + if (!conf_verify_string(chain, node)) + return false; + + const char *path = yml_value_as_string(node); + if (strlen(path) < 1 || path[0] != '/') { + LOG_ERR("%s: path must be absolute", conf_err_prefix(chain, node)); + return false; + } + + return true; } static bool @@ -523,7 +538,7 @@ static bool verify_conf(keychain_t *chain, const struct yml_node *node) { static const struct attr_info attrs[] = { - {"path", true, &conf_verify_string}, + {"path", true, &conf_verify_path}, {"args", false, &conf_verify_args}, MODULE_COMMON_ATTRS, }; From 4d05947985815a7e7e743c891268115bcc6cf377 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 30 Oct 2020 16:25:12 +0100 Subject: [PATCH 039/611] bar: deal with NULL exposables Allow modules to return NULL in begin_expose() --- bar/bar.c | 15 ++++++++++++--- module.c | 3 ++- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/bar/bar.c b/bar/bar.c index d3abd14..8dbda4b 100644 --- a/bar/bar.c +++ b/bar/bar.c @@ -36,19 +36,22 @@ calculate_widths(const struct private *b, int *left, int *center, int *right) for (size_t i = 0; i < b->left.count; i++) { struct exposable *e = b->left.exps[i]; - assert(e != NULL); + if (e == NULL) + continue; *left += b->left_spacing + e->width + b->right_spacing; } for (size_t i = 0; i < b->center.count; i++) { struct exposable *e = b->center.exps[i]; - assert(e != NULL); + if (e == NULL) + continue; *center += b->left_spacing + e->width + b->right_spacing; } for (size_t i = 0; i < b->right.count; i++) { struct exposable *e = b->right.exps[i]; - assert(e != NULL); + if (e == NULL) + continue; *right += b->left_spacing + e->width + b->right_spacing; } @@ -116,6 +119,8 @@ expose(const struct bar *_bar) int x = bar->border.width + bar->left_margin - bar->left_spacing; for (size_t i = 0; i < bar->left.count; i++) { const struct exposable *e = bar->left.exps[i]; + if (e == NULL) + continue; e->expose(e, pix, x + bar->left_spacing, y, bar->height); x += bar->left_spacing + e->width + bar->right_spacing; } @@ -123,6 +128,8 @@ expose(const struct bar *_bar) x = bar->width / 2 - center_width / 2 - bar->left_spacing; for (size_t i = 0; i < bar->center.count; i++) { const struct exposable *e = bar->center.exps[i]; + if (e == NULL) + continue; e->expose(e, pix, x + bar->left_spacing, y, bar->height); x += bar->left_spacing + e->width + bar->right_spacing; } @@ -135,6 +142,8 @@ expose(const struct bar *_bar) for (size_t i = 0; i < bar->right.count; i++) { const struct exposable *e = bar->right.exps[i]; + if (e == NULL) + continue; e->expose(e, pix, x + bar->left_spacing, y, bar->height); x += bar->left_spacing + e->width + bar->right_spacing; } diff --git a/module.c b/module.c index 1e80c32..6d30839 100644 --- a/module.c +++ b/module.c @@ -23,6 +23,7 @@ struct exposable * module_begin_expose(struct module *mod) { struct exposable *e = mod->content(mod); - e->begin_expose(e); + if (e != NULL) + e->begin_expose(e); return e; } From 1e5a1d034152bfa418db310ab6b9cdf8bc0403f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 30 Oct 2020 16:25:55 +0100 Subject: [PATCH 040/611] particle/map: return NULL if we neither find a matching tag, nor have a default tag --- particles/map.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/particles/map.c b/particles/map.c index 2f5c460..6a65a72 100644 --- a/particles/map.c +++ b/particles/map.c @@ -87,11 +87,13 @@ instantiate(const struct particle *particle, const struct tag_set *tags) { const struct private *p = particle->private; const struct tag *tag = tag_for_name(tags, p->tag); - assert(tag != NULL || p->default_particle != NULL); - - if (tag == NULL) - return p->default_particle->instantiate(p->default_particle, tags); + if (tag == NULL) { + if (p->default_particle != NULL) + return p->default_particle->instantiate(p->default_particle, tags); + else + return NULL; + } const char *tag_value = tag->as_string(tag); struct particle *pp = NULL; From f0a34d00553379611bd2eee8da09b347cd1474ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 30 Oct 2020 16:28:48 +0100 Subject: [PATCH 041/611] module/script: parse booleans correctly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit User is expected to send ‘false’ or ‘true’. But we were parsing the value using `strtol()`. This caused all bools to be false, since `strtol()` would always return 0. --- modules/script.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/modules/script.c b/modules/script.c index b4e165b..ebb55df 100644 --- a/modules/script.c +++ b/modules/script.c @@ -67,6 +67,16 @@ content(struct module *mod) return e; } + +static bool +str_to_bool(const char *s) +{ + return strcasecmp(s, "on") == 0 || + strcasecmp(s, "true") == 0 || + strcasecmp(s, "yes") == 0 || + strtoul(s, NULL, 0) > 0; +} + static struct tag * process_line(struct module *mod, const char *line, size_t len) { @@ -112,7 +122,7 @@ process_line(struct module *mod, const char *line, size_t len) tag = tag_new_int(mod, name, strtol(value, NULL, 0)); else if (type_len == 4 && memcmp(type, "bool", 4) == 0) - tag = tag_new_bool(mod, name, strtol(value, NULL, 0)); + tag = tag_new_bool(mod, name, str_to_bool(value)); else if (type_len == 5 && memcmp(type, "float", 5) == 0) tag = tag_new_float(mod, name, strtod(value, NULL)); From d10ad8924bbedd25fdab6026bb8b09d1f813fa87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 31 Oct 2020 11:15:12 +0100 Subject: [PATCH 042/611] =?UTF-8?q?doc/yambar-modules:=20document=20the=20?= =?UTF-8?q?=E2=80=98script=E2=80=99=20module?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/yambar-modules.5.scd | 100 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 98 insertions(+), 2 deletions(-) diff --git a/doc/yambar-modules.5.scd b/doc/yambar-modules.5.scd index 330a9f8..0e02495 100644 --- a/doc/yambar-modules.5.scd +++ b/doc/yambar-modules.5.scd @@ -714,11 +714,107 @@ bar: true: string: margin: 5 - text: "{id}: {state}"``` + text: "{id}: {state}" +``` + +# SCRIPT + +This module executes a user-provided script (or binary!) that writes +tags on its stdout. + +The script can either exit immediately after writing a set of tags, in +which case yambar will display those tags until yambar is +terminated. Or, the script can continue executing and update yambar +with new tag sets, either periodically, or when there is new data to +feed to yambar. + +Tag sets, or _transactions_, are separated by an empty line. Each +_tag_ is a single line on the format: + +``` +name|type|value +``` + +Where _name_ is what you also use to refer to the tag in the yambar +configuration, _type_ is one of the tag types defined in +*yambar-tags*(5), and _value_ is the tag’s value. + +Example: + +``` +var1|string|hello +var2|int|13 + +var1|string|world +var2|int|37 +``` + +The example above consists of two transactions. Each transaction has +two tags: one string tag and one integer tag. The second transaction +replaces the tags from the first transaction. + +Supported _types_ are: + +- string +- int +- bool +- float +- range:n-m (e.g. *var|range:0-100|57*) + +## TAGS + +User defined. + +## CONFIGURATION + +[[ *Name* +:[ *Type* +:[ *Req* +:[ *Description* +| path +: string +: yes +: Path to script/binary to execute. Must be an absolute path. +| args +: list of strings +: no +: Arguments to pass to the script/binary. + +## EXAMPLES + +Here is an "hello world" example script: + +``` +#!/bin/sh + +while true; do + echo "test|string|hello" + echo "" + sleep 3 + + echo "test|string|world" + echo "" + sleep 3 +done +``` + +This script will emit a single string tag, _test_, and alternate its +value between *hello* and *world* every three seconds. + +A corresponding yambar configuration could look like this: + +``` +bar: + left: + - script: + path: /path/to/script.sh + args: [] + content: {string: {text: "{test}"}} +``` # SWAY-XKB -This module uses *Sway* extenions to the I3 IPC API to monitor input +This module uses *Sway* extensions to the I3 IPC API to monitor input devices' active XKB layout. As such, it requires Sway to be running. *Note* that the _content_ configuration option is a *template*; From 74754b0ab951e936848663ba2c5151c04534c131 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 31 Oct 2020 12:08:43 +0100 Subject: [PATCH 043/611] module/script: improved verification of tag type parameters and tag values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Verify int/float/bool values are that, and nothing else * Verify tag ranges are integers * Verify a range tag value is inside its range * Don’t allow anything but false|true for booleans --- modules/script.c | 89 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 67 insertions(+), 22 deletions(-) diff --git a/modules/script.c b/modules/script.c index ebb55df..ed41bfd 100644 --- a/modules/script.c +++ b/modules/script.c @@ -67,16 +67,6 @@ content(struct module *mod) return e; } - -static bool -str_to_bool(const char *s) -{ - return strcasecmp(s, "on") == 0 || - strcasecmp(s, "true") == 0 || - strcasecmp(s, "yes") == 0 || - strtoul(s, NULL, 0) > 0; -} - static struct tag * process_line(struct module *mod, const char *line, size_t len) { @@ -118,14 +108,43 @@ process_line(struct module *mod, const char *line, size_t len) if (type_len == 6 && memcmp(type, "string", 6) == 0) tag = tag_new_string(mod, name, value); - else if (type_len == 3 && memcmp(type, "int", 3) == 0) - tag = tag_new_int(mod, name, strtol(value, NULL, 0)); + else if (type_len == 3 && memcmp(type, "int", 3) == 0) { + errno = 0; + char *end; + long v = strtol(value, &end, 0); - else if (type_len == 4 && memcmp(type, "bool", 4) == 0) - tag = tag_new_bool(mod, name, str_to_bool(value)); + if (errno != 0 || *end != '\0') { + LOG_ERR("tag value is not an integer: %s", value); + goto bad_tag; + } + tag = tag_new_int(mod, name, v); + } - else if (type_len == 5 && memcmp(type, "float", 5) == 0) - tag = tag_new_float(mod, name, strtod(value, NULL)); + else if (type_len == 4 && memcmp(type, "bool", 4) == 0) { + bool v; + if (strcmp(value, "true") == 0) + v = true; + else if (strcmp(value, "false") == 0) + v = false; + else { + LOG_ERR("tag value is not a boolean: %s", value); + goto bad_tag; + } + tag = tag_new_bool(mod, name, v); + } + + else if (type_len == 5 && memcmp(type, "float", 5) == 0) { + errno = 0; + char *end; + double v = strtod(value, &end); + + if (errno != 0 || *end != '\0') { + LOG_ERR("tag value is not a float: %s", value); + goto bad_tag; + } + + tag = tag_new_float(mod, name, v); + } else if ((type_len > 6 && memcmp(type, "range:", 6) == 0) || (type_len > 9 && memcmp(type, "realtime:", 9 == 0))) @@ -133,8 +152,12 @@ process_line(struct module *mod, const char *line, size_t len) const char *_start = type + 6; const char *split = memchr(_start, '-', type_len - 6); - if (split == NULL || split == _start || (split + 1) - type >= type_len) + if (split == NULL || split == _start || (split + 1) - type >= type_len) { + LOG_ERR( + "tag range delimiter ('-') not found in type: %.*s", + (int)type_len, type); goto bad_tag; + } const char *_end = split + 1; @@ -143,8 +166,12 @@ process_line(struct module *mod, const char *line, size_t len) long start = 0; for (size_t i = 0; i < start_len; i++) { - if (!(_start[i] >= '0' && _start[i] <= '9')) + if (!(_start[i] >= '0' && _start[i] <= '9')) { + LOG_ERR( + "tag range start is not an integer: %.*s", + (int)start_len, _start); goto bad_tag; + } start *= 10; start += _start[i] - '0'; @@ -152,19 +179,37 @@ process_line(struct module *mod, const char *line, size_t len) long end = 0; for (size_t i = 0; i < end_len; i++) { - if (!(_end[i] >= '0' && _end[i] < '9')) + if (!(_end[i] >= '0' && _end[i] < '9')) { + LOG_ERR( + "tag range end is not an integer: %.*s", + (int)end_len, _end); goto bad_tag; + } end *= 10; end += _end[i] - '0'; } if (type_len > 9 && memcmp(type, "realtime:", 9) == 0) { - LOG_WARN("unimplemented: realtime tag"); + LOG_ERR("unimplemented: realtime tag"); goto bad_tag; } - tag = tag_new_int_range(mod, name, strtol(value, NULL, 0), start, end); + errno = 0; + char *vend; + long v = strtol(value, &vend, 0); + if (errno != 0 || *vend != '\0') { + LOG_ERR("tag value is not an integer: %s", value); + goto bad_tag; + } + + if (v < start || v > end) { + LOG_ERR("tag value is outside range: %ld <= %ld <= %ld", + start, v, end); + goto bad_tag; + } + + tag = tag_new_int_range(mod, name, v, start, end); } else { @@ -176,7 +221,7 @@ process_line(struct module *mod, const char *line, size_t len) return tag; bad_tag: - LOG_ERR("invalid: %.*s", (int)len, line); + LOG_ERR("invalid tag: %.*s", (int)len, line); free(name); free(value); return NULL; From 074af015fbd7eb2fb6ba20bd832c77f647e26b10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 2 Nov 2020 19:01:46 +0100 Subject: [PATCH 044/611] module/river: exclude seats while river is starting up This is mainly to fix a race when river is *not* running; sometimes we ended up allocating particles for N seats in content(), but then when iterating the seats, run() had destroyed all, or some of the seats, causing us to feed NULL pointers to dynlist, which crashed. --- modules/river.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/modules/river.c b/modules/river.c index bb6a03b..b6e3db1 100644 --- a/modules/river.c +++ b/modules/river.c @@ -90,7 +90,8 @@ content(struct module *mod) } } - const size_t seat_count = m->title != NULL ? tll_length(m->seats) : 0; + const size_t seat_count = m->title != NULL && !m->is_starting_up + ? tll_length(m->seats) : 0; struct exposable *tag_parts[32 + seat_count]; for (unsigned i = 0; i < 32; i++) { @@ -122,7 +123,7 @@ content(struct module *mod) tag_set_destroy(&tags); } - if (m->title != NULL) { + if (m->title != NULL && !m->is_starting_up) { size_t i = 32; tll_foreach(m->seats, it) { const struct seat *seat = &it->item; @@ -565,6 +566,10 @@ run(struct module *mod) } wl_display_roundtrip(display); + + bool unlock_at_exit = true; + mtx_lock(&mod->lock); + m->is_starting_up = false; tll_foreach(m->outputs, it) @@ -572,6 +577,9 @@ run(struct module *mod) tll_foreach(m->seats, it) instantiate_seat(&it->item); + unlock_at_exit = false; + mtx_unlock(&mod->lock); + while (true) { wl_display_flush(display); @@ -618,6 +626,9 @@ out: wl_registry_destroy(registry); if (display != NULL) wl_display_disconnect(display); + + if (unlock_at_exit) + mtx_unlock(&mod->lock); return ret; } From d0360f2de18d1a6597772f74acd2fd4dc2a6715e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 2 Nov 2020 19:03:33 +0100 Subject: [PATCH 045/611] particle: reset signals and signal mask when executing an on-click handler --- particle.c | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/particle.c b/particle.c index be98e0e..ffe330f 100644 --- a/particle.c +++ b/particle.c @@ -219,14 +219,25 @@ exposable_default_on_mouse(struct exposable *exposable, struct bar *bar, LOG_DBG("executing on-click handler: %s", cmd); + sigset_t mask; + sigemptyset(&mask); + + const struct sigaction sa = {.sa_handler = SIG_DFL}; + if (sigaction(SIGINT, &sa, NULL) < 0 || + sigaction(SIGTERM, &sa, NULL) < 0 || + sigaction(SIGCHLD, &sa, NULL) < 0 || + sigprocmask(SIG_SETMASK, &mask, NULL) < 0) + { + goto fail; + } + /* Redirect stdin/stdout/stderr to /dev/null */ int dev_null_r = open("/dev/null", O_RDONLY | O_CLOEXEC); int dev_null_w = open("/dev/null", O_WRONLY | O_CLOEXEC); if (dev_null_r == -1 || dev_null_w == -1) { LOG_ERRNO("/dev/null: failed to open"); - (void)!write(pipe_fds[1], &errno, sizeof(errno)); - _exit(1); + goto fail; } if (dup2(dev_null_r, STDIN_FILENO) == -1 || @@ -234,15 +245,20 @@ exposable_default_on_mouse(struct exposable *exposable, struct bar *bar, dup2(dev_null_w, STDERR_FILENO) == -1) { LOG_ERRNO("failed to redirect stdin/stdout/stderr"); - (void)!write(pipe_fds[1], &errno, sizeof(errno)); - _exit(1); + goto fail; } + /* Close *all* other FDs (e.g. script modules' FDs) */ + for (int i = STDERR_FILENO + 1; i < 65536; i++) + close(i); + execvp(argv[0], argv); + fail: /* Signal failure to parent process */ (void)!write(pipe_fds[1], &errno, sizeof(errno)); - _exit(1); + close(pipe_fds[1]); + _exit(errno); break; default: From 198a351c7c5ad5fd13168a2b4aede4c65537d1bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 2 Nov 2020 19:03:58 +0100 Subject: [PATCH 046/611] meson: particles: data-driven foreach loop, and link map against dynlist --- particles/meson.build | 42 ++++++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/particles/meson.build b/particles/meson.build index f571f12..6b31081 100644 --- a/particles/meson.build +++ b/particles/meson.build @@ -1,21 +1,5 @@ particle_sdk = declare_dependency(dependencies: [pixman, tllist, fcft]) -particles = [] -foreach particle : ['empty', 'list', 'map', 'progress-bar', 'ramp', 'string'] - if plugs_as_libs - shared_module('@0@'.format(particle), '@0@.c'.format(particle), - dependencies: particle_sdk, - name_prefix: 'particle_', - install: true, - install_dir: join_paths(get_option('libdir'), 'yambar')) - else - particles += [declare_dependency( - sources: '@0@.c'.format(particle), - dependencies: particle_sdk, - compile_args: '-DHAVE_PLUGIN_@0@'.format(particle.underscorify()))] - endif -endforeach - dynlist_lib = build_target( 'dynlist', 'dynlist.c', 'dynlist.h', dependencies: particle_sdk, target_type: plugs_as_libs ? 'shared_library' : 'static_library', @@ -25,3 +9,29 @@ dynlist_lib = build_target( ) dynlist = declare_dependency(link_with: dynlist_lib) + +# Particle name -> dep-list +deps = { + 'empty': [], + 'list': [], + 'map': [dynlist], + 'progress-bar': [], + 'ramp': [], + 'string': [], +} + +particles = [] +foreach particle, particle_deps : deps + if plugs_as_libs + shared_module('@0@'.format(particle), '@0@.c'.format(particle), + dependencies: [particle_sdk] + particle_deps, + name_prefix: 'particle_', + install: true, + install_dir: join_paths(get_option('libdir'), 'yambar')) + else + particles += [declare_dependency( + sources: '@0@.c'.format(particle), + dependencies: [particle_sdk] + particle_deps, + compile_args: '-DHAVE_PLUGIN_@0@'.format(particle.underscorify()))] + endif +endforeach From 86ef9dcc028e1ca900a75fd810dc689bfba30627 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 2 Nov 2020 19:04:32 +0100 Subject: [PATCH 047/611] =?UTF-8?q?particle/map:=20don=E2=80=99t=20return?= =?UTF-8?q?=20NULL=20from=20instantiate()?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead return an empty dynlist. --- particles/map.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/particles/map.c b/particles/map.c index 6a65a72..57bb0fb 100644 --- a/particles/map.c +++ b/particles/map.c @@ -8,6 +8,7 @@ #include "../config-verify.h" #include "../particle.h" #include "../plugin.h" +#include "dynlist.h" struct particle_map { const char *tag_value; @@ -89,10 +90,9 @@ instantiate(const struct particle *particle, const struct tag_set *tags) const struct tag *tag = tag_for_name(tags, p->tag); if (tag == NULL) { - if (p->default_particle != NULL) - return p->default_particle->instantiate(p->default_particle, tags); - else - return NULL; + return p->default_particle != NULL + ? p->default_particle->instantiate(p->default_particle, tags) + : dynlist_exposable_new(NULL, 0, 0, 0); } const char *tag_value = tag->as_string(tag); @@ -108,13 +108,16 @@ instantiate(const struct particle *particle, const struct tag_set *tags) break; } - if (pp == NULL) { - assert(p->default_particle != NULL); - pp = p->default_particle; - } - struct eprivate *e = calloc(1, sizeof(*e)); - e->exposable = pp->instantiate(pp, tags); + + if (pp != NULL) + e->exposable = pp->instantiate(pp, tags); + else if (p->default_particle != NULL) + e->exposable = p->default_particle->instantiate(p->default_particle, tags); + else + e->exposable = dynlist_exposable_new(NULL, 0, 0, 0); + + assert(e->exposable != NULL); char *on_click = tags_expand_template(particle->on_click_template, tags); struct exposable *exposable = exposable_common_new(particle, on_click); From c3cfae13e80bc00b0a0b1678601f28477cda4916 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 2 Nov 2020 19:04:55 +0100 Subject: [PATCH 048/611] particle/progress-bar: assert sub particles where instantiated correctly --- particles/progress-bar.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/particles/progress-bar.c b/particles/progress-bar.c index 5c16802..d03931c 100644 --- a/particles/progress-bar.c +++ b/particles/progress-bar.c @@ -210,6 +210,8 @@ instantiate(const struct particle *particle, const struct tag_set *tags) epriv->exposables[idx++] = p->end_marker->instantiate(p->end_marker, tags); assert(idx == epriv->count); + for (size_t i = 0; i < epriv->count; i++) + assert(epriv->exposables[i] != NULL); char *on_click = tags_expand_template(particle->on_click_template, tags); From 44db9304c5596b2c43e6bd127012c0451a431812 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 2 Nov 2020 19:05:14 +0100 Subject: [PATCH 049/611] particle/ramp: assert sub particles where instantiated correctly --- particles/ramp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/particles/ramp.c b/particles/ramp.c index b513681..db63aff 100644 --- a/particles/ramp.c +++ b/particles/ramp.c @@ -122,6 +122,7 @@ instantiate(const struct particle *particle, const struct tag_set *tags) struct eprivate *e = calloc(1, sizeof(*e)); e->exposable = pp->instantiate(pp, tags); + assert(e->exposable != NULL); char *on_click = tags_expand_template(particle->on_click_template, tags); struct exposable *exposable = exposable_common_new(particle, on_click); From df2d8fec36c39a3f3a6302886d44c01d33bcaced Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 2 Nov 2020 19:05:23 +0100 Subject: [PATCH 050/611] particle/list: assert sub particles where instantiated correctly --- particles/list.c | 1 + 1 file changed, 1 insertion(+) diff --git a/particles/list.c b/particles/list.c index 720e8a5..6f51152 100644 --- a/particles/list.c +++ b/particles/list.c @@ -118,6 +118,7 @@ instantiate(const struct particle *particle, const struct tag_set *tags) for (size_t i = 0; i < p->count; i++) { const struct particle *pp = p->particles[i]; e->exposables[i] = pp->instantiate(pp, tags); + assert(e->exposables[i] != NULL); } char *on_click = tags_expand_template(particle->on_click_template, tags); From fef40d18e1bdd9202f8f5ffd2cbc31fc58317230 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 2 Nov 2020 19:06:52 +0100 Subject: [PATCH 051/611] module/sway-xkb: fix name of .so file, fixes load failures when built as shared module --- modules/meson.build | 20 ++++++++++---------- modules/{sway_xkb.c => sway-xkb.c} | 0 2 files changed, 10 insertions(+), 10 deletions(-) rename modules/{sway_xkb.c => sway-xkb.c} (100%) diff --git a/modules/meson.build b/modules/meson.build index ed1683e..5b480d7 100644 --- a/modules/meson.build +++ b/modules/meson.build @@ -9,7 +9,7 @@ mpd = dependency('libmpdclient') xcb_xkb = dependency('xcb-xkb', required: get_option('backend-x11')) # Module name -> (source-list, dep-list) -deps = { +mod_data = { 'alsa': [[], [m, alsa]], 'backlight': [[], [m, udev]], 'battery': [[], [udev]], @@ -20,11 +20,11 @@ deps = { 'network': [[], []], 'removables': [[], [dynlist, udev]], 'script': [[], []], - 'sway_xkb': [['i3-common.c', 'i3-common.h'], [dynlist, json]], + 'sway-xkb': [['i3-common.c', 'i3-common.h'], [dynlist, json]], } if backend_x11 - deps += { + mod_data += { 'xkb': [[], [xcb_stuff, xcb_xkb]], 'xwindow': [[], [xcb_stuff]], } @@ -49,25 +49,25 @@ if backend_wayland command: [wscanner_prog, 'private-code', '@INPUT@', '@OUTPUT@']) endforeach - deps += { - 'river': [[wl_proto_src + wl_proto_headers + river_proto_src + river_proto_headers], []], + mod_data += { + 'river': [[wl_proto_src + wl_proto_headers + river_proto_src + river_proto_headers], [dynlist]], } endif -foreach mod, data : deps +foreach mod, data : mod_data sources = data[0] - dep = data[1] + deps = data[1] if plugs_as_libs shared_module(mod, '@0@.c'.format(mod), sources, - dependencies: [module_sdk] + dep, + dependencies: [module_sdk] + deps, name_prefix: 'module_', install: true, install_dir: join_paths(get_option('libdir'), 'yambar')) else modules += [declare_dependency( sources: ['@0@.c'.format(mod)] + sources, - dependencies: [module_sdk] + dep, - compile_args: '-DHAVE_PLUGIN_@0@'.format(mod))] + dependencies: [module_sdk] + deps, + compile_args: '-DHAVE_PLUGIN_@0@'.format(mod.underscorify()))] endif endforeach diff --git a/modules/sway_xkb.c b/modules/sway-xkb.c similarity index 100% rename from modules/sway_xkb.c rename to modules/sway-xkb.c From c11a79c98df124fb33705f5c173f98b93bc0fb81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 2 Nov 2020 19:09:58 +0100 Subject: [PATCH 052/611] bar, module: particles may no longer return NULL in instantiate() --- bar/bar.c | 21 --------------------- module.c | 3 +-- 2 files changed, 1 insertion(+), 23 deletions(-) diff --git a/bar/bar.c b/bar/bar.c index 8dbda4b..526f80f 100644 --- a/bar/bar.c +++ b/bar/bar.c @@ -36,22 +36,16 @@ calculate_widths(const struct private *b, int *left, int *center, int *right) for (size_t i = 0; i < b->left.count; i++) { struct exposable *e = b->left.exps[i]; - if (e == NULL) - continue; *left += b->left_spacing + e->width + b->right_spacing; } for (size_t i = 0; i < b->center.count; i++) { struct exposable *e = b->center.exps[i]; - if (e == NULL) - continue; *center += b->left_spacing + e->width + b->right_spacing; } for (size_t i = 0; i < b->right.count; i++) { struct exposable *e = b->right.exps[i]; - if (e == NULL) - continue; *right += b->left_spacing + e->width + b->right_spacing; } @@ -85,30 +79,24 @@ expose(const struct bar *_bar) for (size_t i = 0; i < bar->left.count; i++) { struct module *m = bar->left.mods[i]; struct exposable *e = bar->left.exps[i]; - if (e != NULL) e->destroy(e); - bar->left.exps[i] = module_begin_expose(m); } for (size_t i = 0; i < bar->center.count; i++) { struct module *m = bar->center.mods[i]; struct exposable *e = bar->center.exps[i]; - if (e != NULL) e->destroy(e); - bar->center.exps[i] = module_begin_expose(m); } for (size_t i = 0; i < bar->right.count; i++) { struct module *m = bar->right.mods[i]; struct exposable *e = bar->right.exps[i]; - if (e != NULL) e->destroy(e); - bar->right.exps[i] = module_begin_expose(m); } @@ -119,8 +107,6 @@ expose(const struct bar *_bar) int x = bar->border.width + bar->left_margin - bar->left_spacing; for (size_t i = 0; i < bar->left.count; i++) { const struct exposable *e = bar->left.exps[i]; - if (e == NULL) - continue; e->expose(e, pix, x + bar->left_spacing, y, bar->height); x += bar->left_spacing + e->width + bar->right_spacing; } @@ -128,8 +114,6 @@ expose(const struct bar *_bar) x = bar->width / 2 - center_width / 2 - bar->left_spacing; for (size_t i = 0; i < bar->center.count; i++) { const struct exposable *e = bar->center.exps[i]; - if (e == NULL) - continue; e->expose(e, pix, x + bar->left_spacing, y, bar->height); x += bar->left_spacing + e->width + bar->right_spacing; } @@ -142,8 +126,6 @@ expose(const struct bar *_bar) for (size_t i = 0; i < bar->right.count; i++) { const struct exposable *e = bar->right.exps[i]; - if (e == NULL) - continue; e->expose(e, pix, x + bar->left_spacing, y, bar->height); x += bar->left_spacing + e->width + bar->right_spacing; } @@ -316,7 +298,6 @@ destroy(struct bar *bar) for (size_t i = 0; i < b->left.count; i++) { struct module *m = b->left.mods[i]; struct exposable *e = b->left.exps[i]; - if (e != NULL) e->destroy(e); m->destroy(m); @@ -324,7 +305,6 @@ destroy(struct bar *bar) for (size_t i = 0; i < b->center.count; i++) { struct module *m = b->center.mods[i]; struct exposable *e = b->center.exps[i]; - if (e != NULL) e->destroy(e); m->destroy(m); @@ -332,7 +312,6 @@ destroy(struct bar *bar) for (size_t i = 0; i < b->right.count; i++) { struct module *m = b->right.mods[i]; struct exposable *e = b->right.exps[i]; - if (e != NULL) e->destroy(e); m->destroy(m); diff --git a/module.c b/module.c index 6d30839..1e80c32 100644 --- a/module.c +++ b/module.c @@ -23,7 +23,6 @@ struct exposable * module_begin_expose(struct module *mod) { struct exposable *e = mod->content(mod); - if (e != NULL) - e->begin_expose(e); + e->begin_expose(e); return e; } From 31f6a4a6a004f62726a66a1b089b1a5612f168ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 2 Nov 2020 19:12:12 +0100 Subject: [PATCH 053/611] =?UTF-8?q?module/script:=20don=E2=80=99t=20re-clo?= =?UTF-8?q?se=20comm-pipe=20on=20failure?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/script.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/script.c b/modules/script.c index ed41bfd..da11a80 100644 --- a/modules/script.c +++ b/modules/script.c @@ -475,14 +475,17 @@ run(struct module *mod) goto fail; } + /* We're done with the redirection pipe */ close(comm_pipe[1]); + comm_pipe[1] = -1; execvp(m->path, argv); fail: (void)!write(exec_pipe[1], &errno, sizeof(errno)); close(exec_pipe[1]); - close(comm_pipe[1]); + if (comm_pipe[1] >= 0) + close(comm_pipe[1]); _exit(errno); } From aa34925f54754b5a6535ca0e062723825863f88d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 2 Nov 2020 19:12:26 +0100 Subject: [PATCH 054/611] module/script: close all unrelated FDs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While most FDs are CLOEXEC, not all are. For example, other script modules’ re-direction pipes. --- modules/script.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/modules/script.c b/modules/script.c index da11a80..e18570e 100644 --- a/modules/script.c +++ b/modules/script.c @@ -479,6 +479,16 @@ run(struct module *mod) close(comm_pipe[1]); comm_pipe[1] = -1; + /* Close *all* other FDs */ + for (int i = STDERR_FILENO + 1; i < 65536; i++) { + if (i == exec_pipe[1]) { + /* Needed for error reporting. Automatically closed + * when execvp() succeeds */ + continue; + } + close(i); + } + execvp(m->path, argv); fail: From e0169d38f3258026f6cc498aff4080764bd16c86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 2 Nov 2020 19:13:11 +0100 Subject: [PATCH 055/611] =?UTF-8?q?module/script:=20don=E2=80=99t=20re-dir?= =?UTF-8?q?ect=20stderr=20to=20/dev/null?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/script.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/modules/script.c b/modules/script.c index e18570e..a904387 100644 --- a/modules/script.c +++ b/modules/script.c @@ -463,13 +463,12 @@ run(struct module *mod) close(exec_pipe[0]); close(comm_pipe[0]); - /* Re-direct stdin/stdout/stderr */ - int dev_null = open("/dev/null", O_RDWR); + /* Re-direct stdin/stdout */ + int dev_null = open("/dev/null", O_RDONLY); if (dev_null < 0) goto fail; if (dup2(dev_null, STDIN_FILENO) < 0 || - dup2(dev_null, STDERR_FILENO) < 0 || dup2(comm_pipe[1], STDOUT_FILENO) < 0) { goto fail; From ba54e709eea46c540e4b97d0e73b2f6a6ae9fe42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 2 Nov 2020 19:14:15 +0100 Subject: [PATCH 056/611] module/script: no need to handle SIGCHLD MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Assume that a closed pipe means the child died. Even if it hasn’t, we can’t read anymore from it. We’ll end up killing it anyway before returning from run(). --- modules/script.c | 63 +++++++++++------------------------------------- 1 file changed, 14 insertions(+), 49 deletions(-) diff --git a/modules/script.c b/modules/script.c index a904387..70c889f 100644 --- a/modules/script.c +++ b/modules/script.c @@ -10,7 +10,6 @@ #include #include -#include #define LOG_MODULE "script" #define LOG_ENABLE_DBG 0 @@ -315,34 +314,13 @@ data_received(struct module *mod, const char *data, size_t len) } static int -run_loop(struct module *mod, int comm_fd) +run_loop(struct module *mod, pid_t pid, int comm_fd) { - //struct private *m = mod; - - sigset_t mask; - sigemptyset(&mask); - sigaddset(&mask, SIGCHLD); - - /* Block normal signal handling - we're using a signalfd instead */ - sigset_t original_mask; - if (pthread_sigmask(SIG_BLOCK, &mask, &original_mask) < 0) { - LOG_ERRNO("failed to block SIGCHLD"); - return -1; - } - - int sig_fd = signalfd(-1, &mask, SFD_NONBLOCK | SFD_CLOEXEC); - if (sig_fd < 0) { - LOG_ERRNO("failed to create signal FD"); - pthread_sigmask(SIG_SETMASK, &original_mask, NULL); - return -1; - } - int ret = 0; while (true) { struct pollfd fds[] = { {.fd = mod->abort_fd, .events = POLLIN}, - {.fd = sig_fd, .events = POLLIN}, {.fd = comm_fd, .events = POLLIN}, }; @@ -354,7 +332,7 @@ run_loop(struct module *mod, int comm_fd) break; } - if (fds[2].revents & POLLIN) { + if (fds[1].revents & POLLIN) { char data[4096]; ssize_t amount = read(comm_fd, data, sizeof(data)); if (amount < 0) { @@ -373,36 +351,17 @@ run_loop(struct module *mod, int comm_fd) } if (fds[1].revents & POLLHUP) { - LOG_ERR("signal FD closed unexpectedly"); - ret = 1; - break; - } - - if (fds[2].revents & POLLHUP) { /* Child's stdout closed */ + LOG_DBG("script pipe closed (script terminated?)"); break; } - if (fds[0].revents & POLLIN) - break; - - if (fds[1].revents & POLLIN) { - struct signalfd_siginfo info; - ssize_t amount = read(sig_fd, &info, sizeof(info)); - - if (amount < 0) { - LOG_ERRNO("failed to read from signal FD"); - break; - } - - assert(info.ssi_signo == SIGCHLD); - LOG_WARN("script died"); + if (fds[0].revents & POLLIN) { + /* Aborted */ break; } } - close(sig_fd); - pthread_sigmask(SIG_SETMASK, &original_mask, NULL); return ret; } @@ -411,12 +370,14 @@ run(struct module *mod) { struct private *m = mod->private; + /* Pipe to detect exec() failures */ int exec_pipe[2]; if (pipe2(exec_pipe, O_CLOEXEC) < 0) { LOG_ERRNO("failed to create pipe"); return -1; } + /* Stdout redirection pipe */ int comm_pipe[2]; if (pipe(comm_pipe) < 0) { LOG_ERRNO("failed to create stdin/stdout redirection pipe"); @@ -438,13 +399,14 @@ run(struct module *mod) if (pid == 0) { /* Child */ + /* Construct argv for execvp() */ char *argv[1 + m->argc + 1]; argv[0] = m->path; for (size_t i = 0; i < m->argc; i++) argv[i + 1] = m->argv[i]; argv[1 + m->argc] = NULL; - /* Restore signal handlers */ + /* Restore signal handlers and signal mask */ sigset_t mask; sigemptyset(&mask); @@ -457,6 +419,7 @@ run(struct module *mod) goto fail; } + /* New process group, so that we can use killpg() */ setpgid(0, 0); /* Close pipe read ends */ @@ -505,6 +468,7 @@ run(struct module *mod) int _errno; static_assert(sizeof(_errno) == sizeof(errno), "errno size mismatch"); + /* Wait for errno from child, or FD being closed in execvp() */ int r = read(exec_pipe[0], &_errno, sizeof(_errno)); close(exec_pipe[0]); @@ -521,10 +485,11 @@ run(struct module *mod) return -1; } + /* Pipe was closed. I.e. execvp() succeeded */ + assert(r == 0); LOG_DBG("script running under PID=%u", pid); - int ret = run_loop(mod, comm_pipe[0]); - + int ret = run_loop(mod, pid, comm_pipe[0]); close(comm_pipe[0]); if (waitpid(pid, NULL, WNOHANG) == 0) { killpg(pid, SIGTERM); From 2fe602a6a2b3816b2bf9be991ae52211dcd961bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 2 Nov 2020 19:15:42 +0100 Subject: [PATCH 057/611] =?UTF-8?q?main:=20no=20need=20to=20block=20SIGCHL?= =?UTF-8?q?D=20anymore,=20we=20don=E2=80=99t=20use=20it?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main.c | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/main.c b/main.c index 61a89f1..f8a8453 100644 --- a/main.c +++ b/main.c @@ -279,11 +279,6 @@ main(int argc, char *const *argv) sigaction(SIGINT, &sa, NULL); sigaction(SIGTERM, &sa, NULL); - sigset_t proc_signal_mask; - sigemptyset(&proc_signal_mask); - sigaddset(&proc_signal_mask, SIGCHLD); - sigprocmask(SIG_BLOCK, &proc_signal_mask, NULL); - /* Block SIGINT (this is under the assumption that threads inherit * the signal mask */ sigset_t signal_mask; @@ -341,15 +336,17 @@ main(int argc, char *const *argv) while (!aborted) { struct pollfd fds[] = {{.fd = abort_fd, .events = POLLIN}}; - int r __attribute__((unused)) = poll(fds, 1, -1); + int r __attribute__((unused)) = poll(fds, sizeof(fds) / sizeof(fds[0]), -1); - /* - * Either the bar aborted (triggering the abort_fd), or user - * killed us (triggering the signal handler which sets - * 'aborted') - */ - assert(aborted || r == 1); - break; + if (fds[0].revents & (POLLIN | POLLHUP)) { + /* + * Either the bar aborted (triggering the abort_fd), or user + * killed us (triggering the signal handler which sets + * 'aborted') + */ + assert(aborted || r == 1); + break; + } } if (aborted) From f438ad9b4468f8c2f2f926bfddf3fe3aef357ed7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 2 Nov 2020 19:15:58 +0100 Subject: [PATCH 058/611] module/script: send SIGINT, SIGTERM, SIGKILL, until child has died --- modules/script.c | 61 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 56 insertions(+), 5 deletions(-) diff --git a/modules/script.c b/modules/script.c index 70c889f..766ea96 100644 --- a/modules/script.c +++ b/modules/script.c @@ -9,7 +9,9 @@ #include #include +#include #include +#include #define LOG_MODULE "script" #define LOG_ENABLE_DBG 0 @@ -491,12 +493,61 @@ run(struct module *mod) int ret = run_loop(mod, pid, comm_pipe[0]); close(comm_pipe[0]); - if (waitpid(pid, NULL, WNOHANG) == 0) { - killpg(pid, SIGTERM); - /* TODO: send SIGKILL after X seconds */ - waitpid(pid, NULL, 0); - } + if (waitpid(pid, NULL, WNOHANG) == 0) { + static const struct { + int signo; + int timeout; + const char *name; + } sig_info[] = { + {SIGINT, 2, "SIGINT"}, + {SIGTERM, 5, "SIGTERM"}, + {SIGKILL, 0, "SIGKILL"}, + }; + + for (size_t i = 0; i < sizeof(sig_info) / sizeof(sig_info[0]); i++) { + struct timeval start; + gettimeofday(&start, NULL); + + const int signo = sig_info[i].signo; + const int timeout = sig_info[i].timeout; + const char *const name __attribute__((unused)) = sig_info[i].name; + + LOG_DBG("sending %s to PID=%u (timeout=%ds)", name, pid, timeout); + killpg(pid, signo); + + /* + * Child is unlikely to terminate *immediately*. Wait a + * *short* period of time before checking waitpid() the + * first time + */ + usleep(10000); + + pid_t waited_pid; + while ((waited_pid = waitpid( + pid, NULL, timeout > 0 ? WNOHANG : 0)) == 0) + { + struct timeval now; + gettimeofday(&now, NULL); + + struct timeval elapsed; + timersub(&now, &start, &elapsed); + + if (elapsed.tv_sec >= timeout) + break; + + /* Don't spinning */ + thrd_yield(); + usleep(100000); /* 100ms */ + } + + if (waited_pid == pid) { + /* Child finally dead */ + break; + } + } + } else + LOG_DBG("PID=%u already terminated", pid); return ret; } From 05aa44f1abb42de1408888b92858494348e201cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 2 Nov 2020 19:55:01 +0100 Subject: [PATCH 059/611] examples: move laptop.conf -> configurations/laptop.conf --- examples/{ => configurations}/laptop.conf | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename examples/{ => configurations}/laptop.conf (100%) diff --git a/examples/laptop.conf b/examples/configurations/laptop.conf similarity index 100% rename from examples/laptop.conf rename to examples/configurations/laptop.conf From 31c015c68051b7ca612bc41f1a7bff387cdd3df2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 2 Nov 2020 19:57:31 +0100 Subject: [PATCH 060/611] particle/ramp: handle tag_for_name() failing --- particles/ramp.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/particles/ramp.c b/particles/ramp.c index db63aff..45cc277 100644 --- a/particles/ramp.c +++ b/particles/ramp.c @@ -95,13 +95,12 @@ instantiate(const struct particle *particle, const struct tag_set *tags) { const struct private *p = particle->private; const struct tag *tag = tag_for_name(tags, p->tag); - assert(tag != NULL); assert(p->count > 0); - long value = tag->as_int(tag); - long min = tag->min(tag); - long max = tag->max(tag); + long value = tag != NULL ? tag->as_int(tag) : 0; + long min = tag != NULL ? tag->min(tag) : 0; + long max = tag != NULL ? tag->max(tag) : 0; assert(value >= min && value <= max); assert(max >= min); From 321d1cdc7d1c8259a65983861755962650daff32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 2 Nov 2020 19:59:24 +0100 Subject: [PATCH 061/611] particle/progress-bar: handle tag_for_name() failing --- particles/progress-bar.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/particles/progress-bar.c b/particles/progress-bar.c index d03931c..ad5c4cd 100644 --- a/particles/progress-bar.c +++ b/particles/progress-bar.c @@ -179,13 +179,13 @@ instantiate(const struct particle *particle, const struct tag_set *tags) { const struct private *p = particle->private; const struct tag *tag = tag_for_name(tags, p->tag); - assert(tag != NULL); - long value = tag->as_int(tag); - long min = tag->min(tag); - long max = tag->max(tag); + long value = tag != NULL ? tag->as_int(tag) : 0; + long min = tag != NULL ? tag->min(tag) : 0; + long max = tag != NULL ? tag->max(tag) : 0; - LOG_DBG("%s: value=%ld, min=%ld, max=%ld", tag->name(tag), value, min, max); + LOG_DBG("%s: value=%ld, min=%ld, max=%ld", + tag != NULL ? tag->name(tag) : "", value, min, max); long fill_count = max == min ? 0 : p->width * value / (max - min); long empty_count = p->width - fill_count; @@ -224,6 +224,9 @@ instantiate(const struct particle *particle, const struct tag_set *tags) exposable->expose = &expose; exposable->on_mouse = &on_mouse; + if (tag == NULL) + return exposable; + enum tag_realtime_unit rt = tag->realtime(tag); if (rt == TAG_REALTIME_NONE) From 58e53b80a907d1c0d7e9ba11a5ec7ca07219276c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 2 Nov 2020 20:31:35 +0100 Subject: [PATCH 062/611] changelog: new module: script --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fa81d03..95c1007 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,8 @@ (https://codeberg.org/dnkl/yambar/issues/10). * river: added documentation (https://codeberg.org/dnkl/yambar/issues/9). +* script: new module, adds support for custom user scripts + (https://codeberg.org/dnkl/yambar/issues/11). ### Deprecated @@ -30,6 +32,8 @@ ### Security ### Contributors +* [JorwLNKwpH](https://codeberg.org/JorwLNKwpH) + ## 1.5.0 From d5a92cbf5f74c63285bc555e1964766381f40586 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 3 Nov 2020 21:25:40 +0100 Subject: [PATCH 063/611] examples: script: cpu.sh - measures CPU usage at a configurable interval --- examples/scripts/cpu.sh | 117 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100755 examples/scripts/cpu.sh diff --git a/examples/scripts/cpu.sh b/examples/scripts/cpu.sh new file mode 100755 index 0000000..1d5cdb5 --- /dev/null +++ b/examples/scripts/cpu.sh @@ -0,0 +1,117 @@ +#!/bin/bash + +# cpu.sh - measures CPU usage at a configurable sample interval +# +# Usage: cpu.sh INTERVAL_IN_SECONDS +# +# This script will emit the following tags on stdout (N is the number +# of logical CPUs): +# +# Name Type +# -------------------- +# cpu range 0-100 +# cpu0 range 0-100 +# cpu1 range 0-100 +# ... +# cpuN-1 range 0-100 +# +# I.e. ‘cpu’ is the average (or aggregated) CPU usage, while cpuX is a +# specific CPUs usage. +# +# Example configuration (update every second): +# +# - script: +# path: /path/to/cpu.sh +# args: [1] +# content: {string: {text: "{cpu}%"}} +# + +interval=${1} + +case ${interval} in + ''|*[!0-9]*) + echo "interval must be an integer" + exit 1 + ;; + *) + ;; +esac + +# Get number of CPUs, by reading /proc/stat +# The output looks like: +# +# cpu A B C D ... +# cpu0 A B C D ... +# cpu1 A B C D ... +# cpuN A B C D ... +# +# The first line is a summary line, accounting *all* CPUs +IFS=$'\n' readarray -t all_cpu_stats < <(grep -e "^cpu" /proc/stat) +cpu_count=$((${#all_cpu_stats[@]} - 1)) + +# Arrays of ‘previous’ idle and total stats, needed to calculate the +# difference between each sample. +prev_idle=() +prev_total=() +for i in $(seq ${cpu_count}); do + prev_idle+=(0) + prev_total+=(0) +done + +prev_average_idle=0 +prev_average_total=0 + +while true; do + IFS=$'\n' readarray -t all_cpu_stats < <(grep -e "^cpu" /proc/stat) + + usage=() # CPU usage in percent, 0 <= x <= 100 + idle=() # idle time since boot, in jiffies + total=() # total time since boot, in jiffies + + average_idle=0 # All CPUs idle time since boot + average_total=0 # All CPUs total time since boot + + for i in $(seq 0 $((cpu_count - 1))); do + # Split this CPUs stats into an array + stats=($(echo "${all_cpu_stats[$((i + 1))]}")) + + # Clear (zero out) “cpuN” + unset "stats[0]" + + # CPU idle time since boot + idle[i]=${stats[4]} + average_idle=$((average_idle + idle[i])) + + # CPU total time since boot + total[i]=0 + for v in "${stats[@]}"; do + total[i]=$((total[i] + v)) + done + average_total=$((average_total + total[i])) + + # Diff since last sample + diff_idle=$((idle[i] - prev_idle[i])) + diff_total=$((total[i] - prev_total[i])) + + usage[i]=$((100 * (diff_total - diff_idle) / diff_total)) + + prev_idle[i]=${idle[i]} + prev_total[i]=${total[i]} + done + + diff_average_idle=$((average_idle - prev_average_idle)) + diff_average_total=$((average_total - prev_average_total)) + + average_usage=$((100 * (diff_average_total - diff_average_idle) / diff_average_total)) + + prev_average_idle=${average_idle} + prev_average_total=${average_total} + + echo "cpu|range:0-100|${average_usage}" + for i in $(seq 0 $((cpu_count - 1))); do + echo "cpu${i}|range:0-100|${usage[i]}" + done + + echo "" + sleep "${interval}" +done From b1ee1ba403c0c8e15b049f61cedbc03e599e804e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 3 Nov 2020 21:32:08 +0100 Subject: [PATCH 064/611] =?UTF-8?q?examples:=20script:=20cpu.sh=20add=20mi?= =?UTF-8?q?ssing=20=E2=80=9C=E2=80=98=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/scripts/cpu.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/scripts/cpu.sh b/examples/scripts/cpu.sh index 1d5cdb5..0bf7813 100755 --- a/examples/scripts/cpu.sh +++ b/examples/scripts/cpu.sh @@ -16,7 +16,7 @@ # cpuN-1 range 0-100 # # I.e. ‘cpu’ is the average (or aggregated) CPU usage, while cpuX is a -# specific CPUs usage. +# specific CPU’s usage. # # Example configuration (update every second): # From ae983b63c241b99927717f06f7e53118e377545b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 3 Nov 2020 21:37:57 +0100 Subject: [PATCH 065/611] readme: add river and sway-xkb to list of modules --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 8395ad4..14522d1 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,8 @@ Available modules: * mpd * network * removables +* river +* sway-xkb * xkb (_XCB backend only_) * xwindow (_XCB backend only_) From f735bc5bd9d3958f22ee50077db689f07c3c4ef6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 3 Nov 2020 21:38:10 +0100 Subject: [PATCH 066/611] readme: add link to configuration examples --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 14522d1..d4993c2 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,8 @@ bar: For details, see the man pages (**yambar**(5) is a good start). +Example configurations can be found in [examples](examples/configuration). + ## Modules From 9d37697c4f01ded8be2ab3bb8a0d2d9cf5158c93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 3 Nov 2020 21:38:16 +0100 Subject: [PATCH 067/611] readme: add script to list of modules, with link to example scripts --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index d4993c2..24b7989 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,7 @@ Available modules: * network * removables * river +* script (see script [examples](examples/scripts)) * sway-xkb * xkb (_XCB backend only_) * xwindow (_XCB backend only_) From 220e43526c43622f6cae014af7ede63a11cdd39a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 3 Nov 2020 21:38:59 +0100 Subject: [PATCH 068/611] readme: fix link to configuration examples --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 24b7989..8052784 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ bar: For details, see the man pages (**yambar**(5) is a good start). -Example configurations can be found in [examples](examples/configuration). +Example configurations can be found in [examples](examples/configurations). ## Modules From f49652130dbe7d7f1cea770c501f32d3664a72c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 5 Nov 2020 21:14:42 +0100 Subject: [PATCH 069/611] =?UTF-8?q?config:=20don=E2=80=99t=20crash=20(div-?= =?UTF-8?q?by-zero)=20if=20the=20alpha=20component=20is=200?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 1 + config.c | 3 +++ 2 files changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 95c1007..115c7a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ (https://codeberg.org/dnkl/yambar/issues/12). * mpd: fix compilation with clang (https://codeberg.org/dnkl/yambar/issues/16). +* Crash when the alpha component in a color value was 0. ### Security diff --git a/config.c b/config.c index 556891c..743a1a3 100644 --- a/config.c +++ b/config.c @@ -53,6 +53,9 @@ conf_to_color(const struct yml_node *node) uint16_t blue = hex_byte(&hex[4]); uint16_t alpha = hex_byte(&hex[6]); + if (alpha == 0) + return (pixman_color_t){0, 0, 0, 0}; + alpha |= alpha << 8; int alpha_div = 0xffff / alpha; From 4a9f5500691a2a90ced56598bc8185d05fe416a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 10 Nov 2020 22:26:49 +0100 Subject: [PATCH 070/611] =?UTF-8?q?module/battery:=20don=E2=80=99t=20crash?= =?UTF-8?q?=20if=20we=20fail=20to=20read=20from=20=E2=80=98status=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/battery.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/battery.c b/modules/battery.c index 4759a99..99bad3c 100644 --- a/modules/battery.c +++ b/modules/battery.c @@ -291,7 +291,10 @@ update_status(struct module *mod, int capacity_fd, int energy_fd, int power_fd, const char *status = readline_from_fd(status_fd); enum state state; - if (strcmp(status, "Full") == 0) + if (status == NULL) { + LOG_WARN("failed to read battery state"); + state = STATE_DISCHARGING; + } else if (strcmp(status, "Full") == 0) state = STATE_FULL; else if (strcmp(status, "Charging") == 0) state = STATE_CHARGING; From 9718c6f31ebf59771de2ace786804ac0a8ce8dad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 15 Nov 2020 12:15:16 +0100 Subject: [PATCH 071/611] examples: scripts: cpu: fix idle and total calculation * include iowait in idle * guest/guestnice is accounted in user/nice, so need to subtract them to not count them twice --- examples/scripts/cpu.sh | 41 ++++++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/examples/scripts/cpu.sh b/examples/scripts/cpu.sh index 0bf7813..66615c5 100755 --- a/examples/scripts/cpu.sh +++ b/examples/scripts/cpu.sh @@ -65,8 +65,6 @@ while true; do IFS=$'\n' readarray -t all_cpu_stats < <(grep -e "^cpu" /proc/stat) usage=() # CPU usage in percent, 0 <= x <= 100 - idle=() # idle time since boot, in jiffies - total=() # total time since boot, in jiffies average_idle=0 # All CPUs idle time since boot average_total=0 # All CPUs total time since boot @@ -75,28 +73,37 @@ while true; do # Split this CPUs stats into an array stats=($(echo "${all_cpu_stats[$((i + 1))]}")) - # Clear (zero out) “cpuN” - unset "stats[0]" + # man procfs(5) + user=${stats[1]} + nice=${stats[2]} + system=${stats[3]} + idle=${stats[4]} + iowait=${stats[5]} + irq=${stats[6]} + softirq=${stats[7]} + steal=${stats[8]} + guest=${stats[9]} + guestnice=${stats[10]} - # CPU idle time since boot - idle[i]=${stats[4]} - average_idle=$((average_idle + idle[i])) + # Guest time already accounted for in user + user=$((user - guest)) + nice=$((nice - guestnice)) - # CPU total time since boot - total[i]=0 - for v in "${stats[@]}"; do - total[i]=$((total[i] + v)) - done - average_total=$((average_total + total[i])) + idle=$((idle + iowait)) + + total=$((user + nice + system + irq + softirq + idle + steal + guest + guestnice)) + + average_idle=$((average_idle + idle)) + average_total=$((average_total + total)) # Diff since last sample - diff_idle=$((idle[i] - prev_idle[i])) - diff_total=$((total[i] - prev_total[i])) + diff_idle=$((idle - prev_idle[i])) + diff_total=$((total - prev_total[i])) usage[i]=$((100 * (diff_total - diff_idle) / diff_total)) - prev_idle[i]=${idle[i]} - prev_total[i]=${total[i]} + prev_idle[i]=${idle} + prev_total[i]=${total} done diff_average_idle=$((average_idle - prev_average_idle)) From 96d2d057e0197914dde9f37f37726bedb6a8715f Mon Sep 17 00:00:00 2001 From: optimus-prime Date: Wed, 2 Dec 2020 13:13:15 +0530 Subject: [PATCH 072/611] module/mpd: add volume tag --- CHANGELOG.md | 2 ++ doc/yambar-modules.5.scd | 3 +++ modules/mpd.c | 3 +++ 3 files changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 115c7a9..59add2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,8 @@ (https://codeberg.org/dnkl/yambar/issues/9). * script: new module, adds support for custom user scripts (https://codeberg.org/dnkl/yambar/issues/11). +* mpd: `volume` tag. This is a range tag that represents MPD's current volume + in percentage (0-100) ### Deprecated diff --git a/doc/yambar-modules.5.scd b/doc/yambar-modules.5.scd index 0e02495..652d6b5 100644 --- a/doc/yambar-modules.5.scd +++ b/doc/yambar-modules.5.scd @@ -452,6 +452,9 @@ artist/album/song. | consume : bool : True if the *consume* flag is set +| volume +: range +: Volume of MPD in percentage | album : string : Currently playing album (also valid in *paused* state) diff --git a/modules/mpd.c b/modules/mpd.c index f6ae445..ed80751 100644 --- a/modules/mpd.c +++ b/modules/mpd.c @@ -38,6 +38,7 @@ struct private { bool repeat; bool random; bool consume; + int volume; char *album; char *artist; char *title; @@ -162,6 +163,7 @@ content(struct module *mod) tag_new_bool(mod, "repeat", m->repeat), tag_new_bool(mod, "random", m->random), tag_new_bool(mod, "consume", m->consume), + tag_new_int_range(mod, "volume", m->volume, 0, 100), tag_new_string(mod, "album", m->album), tag_new_string(mod, "artist", m->artist), tag_new_string(mod, "title", m->title), @@ -320,6 +322,7 @@ update_status(struct module *mod) m->repeat = mpd_status_get_repeat(status); m->random = mpd_status_get_random(status); m->consume = mpd_status_get_consume(status); + m->volume = mpd_status_get_volume(status); m->duration = mpd_status_get_total_time(status) * 1000; m->elapsed.value = mpd_status_get_elapsed_ms(status); m->elapsed.when = now; From b9fda4482d64f835d8f5105757eea5c146e1aaf1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 2 Dec 2020 21:47:16 +0100 Subject: [PATCH 073/611] changelog: fold long lines --- CHANGELOG.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 59add2f..2e52300 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,8 +14,8 @@ (https://codeberg.org/dnkl/yambar/issues/9). * script: new module, adds support for custom user scripts (https://codeberg.org/dnkl/yambar/issues/11). -* mpd: `volume` tag. This is a range tag that represents MPD's current volume - in percentage (0-100) +* mpd: `volume` tag. This is a range tag that represents MPD's current + volume in percentage (0-100) ### Deprecated @@ -36,6 +36,7 @@ ### Contributors * [JorwLNKwpH](https://codeberg.org/JorwLNKwpH) +* [optimus-prime](https://codeberg.org/optimus-prime) ## 1.5.0 From 678d82e4d4c58850a70f3ae48bf04e9109e0fc12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 3 Dec 2020 18:31:42 +0100 Subject: [PATCH 074/611] module/i3: use a tllist for workspaces instead of a dynamic array --- modules/i3.c | 91 +++++++++++++++++++++++++--------------------------- 1 file changed, 43 insertions(+), 48 deletions(-) diff --git a/modules/i3.c b/modules/i3.c index 0f998de..6a83ad4 100644 --- a/modules/i3.c +++ b/modules/i3.c @@ -7,6 +7,8 @@ #include #include +#include + #define LOG_MODULE "i3" #define LOG_ENABLE_DBG 0 #include "../log.h" @@ -50,10 +52,7 @@ struct private { size_t count; } ws_content; - struct { - struct workspace *v; - size_t count; - } workspaces; + tll(struct workspace) workspaces; }; static bool @@ -87,54 +86,50 @@ workspace_from_json(const struct json_object *json, struct workspace *ws) } static void -workspace_free(struct workspace ws) +workspace_free(struct workspace *ws) { - free(ws.name); - free(ws.output); - free(ws.window.title); - free(ws.window.application); + free(ws->name); + free(ws->output); + free(ws->window.title); + free(ws->window.application); } static void workspaces_free(struct private *m) { - for (size_t i = 0; i < m->workspaces.count; i++) - workspace_free(m->workspaces.v[i]); - - free(m->workspaces.v); - m->workspaces.v = NULL; - m->workspaces.count = 0; + tll_foreach(m->workspaces, it) + workspace_free(&it->item); + tll_free(m->workspaces); } static void workspace_add(struct private *m, struct workspace ws) { - size_t new_count = m->workspaces.count + 1; - struct workspace *new_v = realloc(m->workspaces.v, new_count * sizeof(new_v[0])); +#if 0 + tll_push_back(m->workspaces, ws); +#else + tll_rforeach(m->workspaces, it) { + if (strcasecmp(it->item.name, ws.name) < 0) { + tll_insert_after(m->workspaces, it, ws); + return; + } + } - m->workspaces.count = new_count; - m->workspaces.v = new_v; - m->workspaces.v[new_count - 1] = ws; + tll_push_front(m->workspaces, ws); +#endif } static void workspace_del(struct private *m, const char *name) { - struct workspace *workspaces = m->workspaces.v; - - for (size_t i = 0; i < m->workspaces.count; i++) { - const struct workspace *ws = &workspaces[i]; + tll_foreach(m->workspaces, it) { + struct workspace *ws = &it->item; if (strcmp(ws->name, name) != 0) continue; - workspace_free(*ws); - - memmove( - &workspaces[i], - &workspaces[i + 1], - (m->workspaces.count - i - 1) * sizeof(workspaces[0])); - m->workspaces.count--; + workspace_free(ws); + tll_remove(m->workspaces, it); break; } } @@ -142,13 +137,14 @@ workspace_del(struct private *m, const char *name) static struct workspace * workspace_lookup(struct private *m, const char *name) { - for (size_t i = 0; i < m->workspaces.count; i++) - if (strcmp(m->workspaces.v[i].name, name) == 0) - return &m->workspaces.v[i]; + tll_foreach(m->workspaces, it) { + struct workspace *ws = &it->item; + if (strcmp(ws->name, name) == 0) + return ws; + } return NULL; } - static bool handle_get_version_reply(int type, const struct json_object *json, void *_m) { @@ -191,18 +187,17 @@ handle_get_workspaces_reply(int type, const struct json_object *json, void *_mod m->dirty = true; size_t count = json_object_array_length(json); - m->workspaces.count = count; - m->workspaces.v = calloc(count, sizeof(m->workspaces.v[0])); for (size_t i = 0; i < count; i++) { - if (!workspace_from_json( - json_object_array_get_idx(json, i), &m->workspaces.v[i])) { + struct workspace ws = {}; + if (!workspace_from_json(json_object_array_get_idx(json, i), &ws)) { workspaces_free(m); mtx_unlock(&mod->lock); return false; } LOG_DBG("#%zu: %s", i, m->workspaces.v[i].name); + workspace_add(m, ws); } mtx_unlock(&mod->lock); @@ -250,7 +245,7 @@ handle_workspace_event(int type, const struct json_object *json, void *_mod) struct workspace *already_exists = workspace_lookup(m, current_name); if (already_exists != NULL) { LOG_WARN("workspace 'init' event for already existing workspace: %s", current_name); - workspace_free(*already_exists); + workspace_free(already_exists); if (!workspace_from_json(current, already_exists)) goto err; } else { @@ -284,8 +279,8 @@ handle_workspace_event(int type, const struct json_object *json, void *_mod) LOG_DBG("w: %s", w->name); /* Mark all workspaces on current's output invisible */ - for (size_t i = 0; i < m->workspaces.count; i++) { - struct workspace *ws = &m->workspaces.v[i]; + tll_foreach(m->workspaces, it) { + struct workspace *ws = &it->item; if (strcmp(ws->output, w->output) == 0) ws->visible = false; } @@ -346,9 +341,9 @@ handle_window_event(int type, const struct json_object *json, void *_mod) struct workspace *ws = NULL; size_t focused = 0; - for (size_t i = 0; i < m->workspaces.count; i++) { - if (m->workspaces.v[i].focused) { - ws = &m->workspaces.v[i]; + tll_foreach(m->workspaces, it) { + if (it->item.focused) { + ws = &it->item; focused++; } } @@ -536,11 +531,11 @@ content(struct module *mod) mtx_lock(&mod->lock); size_t particle_count = 0; - struct exposable *particles[m->workspaces.count + 1]; + struct exposable *particles[tll_length(m->workspaces) + 1]; struct exposable *current = NULL; - for (size_t i = 0; i < m->workspaces.count; i++) { - const struct workspace *ws = &m->workspaces.v[i]; + tll_foreach(m->workspaces, it) { + struct workspace *ws = &it->item; const struct ws_content *template = NULL; /* Lookup content template for workspace. Fall back to default From d5fc1074d8368daab2092454973728ca2031c1c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 3 Dec 2020 19:07:41 +0100 Subject: [PATCH 075/611] module/i3: revert to old behavior of not sorting workspaces Sorting workspaces needs a config option. --- modules/i3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/i3.c b/modules/i3.c index 6a83ad4..471bcf8 100644 --- a/modules/i3.c +++ b/modules/i3.c @@ -105,7 +105,7 @@ workspaces_free(struct private *m) static void workspace_add(struct private *m, struct workspace ws) { -#if 0 +#if 1 tll_push_back(m->workspaces, ws); #else tll_rforeach(m->workspaces, it) { From d0f1f762ea7040358ee3ae3c21566472e81fdd43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 4 Dec 2020 21:16:14 +0100 Subject: [PATCH 076/611] bar/xcb: fallback to non-primary monitor If the user did *not* configured a specific monitor, we prefer the primary monitor. However, if that monitor is disconnected, yambar would exit with: no matching monitor This patch changes this, to use the *last* connected monitor. It also improves the error message. Note: if the user did specify a monitor in the configuration, but perhaps misspelled it, we will *not* fallback to another monitor, but instead log an error saying that specific monitor could not be found. --- bar/xcb.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/bar/xcb.c b/bar/xcb.c index f95c445..f7a3ee1 100644 --- a/bar/xcb.c +++ b/bar/xcb.c @@ -101,27 +101,38 @@ setup(struct bar *_bar) mon->width, mon->height, mon->x, mon->y, mon->width_in_millimeters, mon->height_in_millimeters); - if (!((bar->monitor == NULL && mon->primary) || - (bar->monitor != NULL && strcmp(bar->monitor, name) == 0))) - { + /* User wants a specific monitor, and this is not the one */ + if (bar->monitor != NULL && strcmp(bar->monitor, name) != 0) { free(name); continue; } - free(name); - backend->x = mon->x; backend->y = mon->y; bar->width = mon->width; backend->y += bar->location == BAR_TOP ? 0 : screen->height_in_pixels - bar->height_with_border; + found_monitor = true; - break; + + if ((bar->monitor != NULL && strcmp(bar->monitor, name) == 0) || + (bar->monitor == NULL && mon->primary)) + { + /* Exact match */ + free(name); + break; + } + + free(name); } free(monitors); if (!found_monitor) { - LOG_ERR("no matching monitor"); + if (bar->monitor == NULL) + LOG_ERR("no monitors found"); + else + LOG_ERR("no monitor '%s'", bar->monitor); + /* TODO: cleanup */ return false; } From 8249b9c7ead514f3ac2fe03b341b8ec6c224a18f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 4 Dec 2020 22:08:05 +0100 Subject: [PATCH 077/611] changelog: fallback to non-primary monitor --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e52300..39a557e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,8 @@ * mpd: fix compilation with clang (https://codeberg.org/dnkl/yambar/issues/16). * Crash when the alpha component in a color value was 0. +* XCB: Fallback to non-primary monitor when the primary monitor is + disconnected (https://codeberg.org/dnkl/yambar/issues/20) ### Security From 20df36093725ea5d2764b5a9a83bebf9d6c5862c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 8 Dec 2020 19:03:30 +0100 Subject: [PATCH 078/611] =?UTF-8?q?module/i3:=20add=20option=20=E2=80=98so?= =?UTF-8?q?rt=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Possible values: none, ascending, descending. Sorts the workspace list accordingly. The default value is ‘none’. --- CHANGELOG.md | 4 +++ doc/yambar-modules.5.scd | 4 +++ modules/i3.c | 59 +++++++++++++++++++++++++++++++--------- 3 files changed, 54 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 39a557e..46ecdeb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,10 @@ (https://codeberg.org/dnkl/yambar/issues/11). * mpd: `volume` tag. This is a range tag that represents MPD's current volume in percentage (0-100) +* i3: `sort` configuration option, that controls how the workspace + list is sorted. Can be set to one of `none`, `ascending` or + `descending`. Default is `none` + (https://codeberg.org/dnkl/yambar/issues/17). ### Deprecated diff --git a/doc/yambar-modules.5.scd b/doc/yambar-modules.5.scd index 652d6b5..693f502 100644 --- a/doc/yambar-modules.5.scd +++ b/doc/yambar-modules.5.scd @@ -374,6 +374,10 @@ with the _application_ and _title_ tags to replace the X11-only : Unlike other modules, _content_ is an associative array mapping workspace names to particles. Use *""* to specify a default fallback particle, or *current* for the currently active workspace. +| sort +: enum +: no +: How to sort the list of workspaces; one of _none_, _ascending_ or _descending_, defaults to _none_. | left-spacing : int : no diff --git a/modules/i3.c b/modules/i3.c index 471bcf8..02bd575 100644 --- a/modules/i3.c +++ b/modules/i3.c @@ -21,6 +21,8 @@ #include "i3-ipc.h" #include "i3-common.h" +enum sort_mode {SORT_NONE, SORT_ASCENDING, SORT_DESCENDING}; + struct ws_content { char *name; struct particle *content; @@ -52,6 +54,7 @@ struct private { size_t count; } ws_content; + enum sort_mode sort_mode; tll(struct workspace) workspaces; }; @@ -105,18 +108,31 @@ workspaces_free(struct private *m) static void workspace_add(struct private *m, struct workspace ws) { -#if 1 - tll_push_back(m->workspaces, ws); -#else - tll_rforeach(m->workspaces, it) { - if (strcasecmp(it->item.name, ws.name) < 0) { - tll_insert_after(m->workspaces, it, ws); - return; - } - } + switch (m->sort_mode) { + case SORT_NONE: + tll_push_back(m->workspaces, ws); + return; - tll_push_front(m->workspaces, ws); -#endif + case SORT_ASCENDING: + tll_foreach(m->workspaces, it) { + if (strcoll(it->item.name, ws.name) > 0) { + tll_insert_before(m->workspaces, it, ws); + return; + } + } + tll_push_back(m->workspaces, ws); + return; + + case SORT_DESCENDING: + tll_foreach(m->workspaces, it) { + if (strcoll(it->item.name, ws.name) < 0) { + tll_insert_before(m->workspaces, it, ws); + return; + } + } + tll_push_back(m->workspaces, ws); + return; + } } static void @@ -600,7 +616,7 @@ struct i3_workspaces { static struct module * i3_new(struct i3_workspaces workspaces[], size_t workspace_count, - int left_spacing, int right_spacing) + int left_spacing, int right_spacing, enum sort_mode sort_mode) { struct private *m = calloc(1, sizeof(*m)); @@ -615,6 +631,8 @@ i3_new(struct i3_workspaces workspaces[], size_t workspace_count, m->ws_content.v[i].content = workspaces[i].content; } + m->sort_mode = sort_mode; + struct module *mod = module_common_new(); mod->private = m; mod->run = &run; @@ -630,12 +648,19 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) const struct yml_node *spacing = yml_get_value(node, "spacing"); const struct yml_node *left_spacing = yml_get_value(node, "left-spacing"); const struct yml_node *right_spacing = yml_get_value(node, "right-spacing"); + const struct yml_node *sort = yml_get_value(node, "sort"); int left = spacing != NULL ? yml_value_as_int(spacing) : left_spacing != NULL ? yml_value_as_int(left_spacing) : 0; int right = spacing != NULL ? yml_value_as_int(spacing) : right_spacing != NULL ? yml_value_as_int(right_spacing) : 0; + const char *sort_value = sort != NULL ? yml_value_as_string(sort) : NULL; + enum sort_mode sort_mode = + sort_value == NULL ? SORT_NONE : + strcmp(sort_value, "none") == 0 ? SORT_NONE : + strcmp(sort_value, "ascending") == 0 ? SORT_ASCENDING : SORT_DESCENDING; + struct i3_workspaces workspaces[yml_dict_length(c)]; size_t idx = 0; @@ -647,7 +672,7 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) workspaces[idx].content = conf_to_particle(it.value, inherited); } - return i3_new(workspaces, yml_dict_length(c), left, right); + return i3_new(workspaces, yml_dict_length(c), left, right, sort_mode); } static bool @@ -680,6 +705,13 @@ verify_content(keychain_t *chain, const struct yml_node *node) return true; } +static bool +verify_sort(keychain_t *chain, const struct yml_node *node) +{ + return conf_verify_enum( + chain, node, (const char *[]){"none", "ascending", "descending"}, 3); +} + static bool verify_conf(keychain_t *chain, const struct yml_node *node) { @@ -687,6 +719,7 @@ verify_conf(keychain_t *chain, const struct yml_node *node) {"spacing", false, &conf_verify_int}, {"left-spacing", false, &conf_verify_int}, {"right-spacing", false, &conf_verify_int}, + {"sort", false, &verify_sort}, {"content", true, &verify_content}, {"anchors", false, NULL}, {NULL, false, NULL}, From d9496152e3f427e3c783043b6ce3263532c805ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 8 Dec 2020 19:08:06 +0100 Subject: [PATCH 079/611] =?UTF-8?q?module/i3:=20add=20new=20tag=20?= =?UTF-8?q?=E2=80=98mode=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This tag is a string: the name of the currently active mode --- CHANGELOG.md | 1 + doc/yambar-modules.5.scd | 3 +++ modules/i3.c | 30 ++++++++++++++++++++++++++++-- 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 46ecdeb..f5301e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ list is sorted. Can be set to one of `none`, `ascending` or `descending`. Default is `none` (https://codeberg.org/dnkl/yambar/issues/17). +* i3: `mode` tag: the name of the currently active mode ### Deprecated diff --git a/doc/yambar-modules.5.scd b/doc/yambar-modules.5.scd index 693f502..5bc15e1 100644 --- a/doc/yambar-modules.5.scd +++ b/doc/yambar-modules.5.scd @@ -361,6 +361,9 @@ with the _application_ and _title_ tags to replace the X11-only | title : string : This workspace's focused window's title +| mode +: string +: The name of the current mode ## CONFIGURATION diff --git a/modules/i3.c b/modules/i3.c index 02bd575..89083ce 100644 --- a/modules/i3.c +++ b/modules/i3.c @@ -49,6 +49,8 @@ struct private { bool dirty; + char *mode; + struct { struct ws_content *v; size_t count; @@ -459,6 +461,25 @@ handle_window_event(int type, const struct json_object *json, void *_mod) return true; } +static bool +handle_mode_event(int type, const struct json_object *json, void *_mod) +{ + struct module *mod = _mod; + struct private *m = mod->private; + + struct json_object *change; + if (!json_object_object_get_ex(json, "change", &change)) { + LOG_ERR("mode event without 'change' property"); + return false; + } + + const char *current_mode = json_object_get_string(change); + free(m->mode); + m->mode = strdup(current_mode); + m->dirty = true; + return true; +} + static void burst_done(void *_mod) { @@ -492,7 +513,7 @@ run(struct module *mod) } i3_send_pkg(sock, I3_IPC_MESSAGE_TYPE_GET_VERSION, NULL); - i3_send_pkg(sock, I3_IPC_MESSAGE_TYPE_SUBSCRIBE, "[\"workspace\", \"window\"]"); + i3_send_pkg(sock, I3_IPC_MESSAGE_TYPE_SUBSCRIBE, "[\"workspace\", \"window\", \"mode\"]"); i3_send_pkg(sock, I3_IPC_MESSAGE_TYPE_GET_WORKSPACES, NULL); static const struct i3_ipc_callbacks callbacks = { @@ -502,6 +523,7 @@ run(struct module *mod) .reply_workspaces = &handle_get_workspaces_reply, .event_workspace = &handle_workspace_event, .event_window = &handle_window_event, + .event_mode = &handle_mode_event, }; bool ret = i3_receive_loop(mod->abort_fd, sock, &callbacks, mod); @@ -523,6 +545,7 @@ destroy(struct module *mod) free(m->ws_content.v); workspaces_free(m); + free(m->mode); free(m); module_default_destroy(mod); } @@ -578,8 +601,10 @@ content(struct module *mod) tag_new_string(mod, "application", ws->window.application), tag_new_string(mod, "title", ws->window.title), + + tag_new_string(mod, "mode", m->mode), }, - .count = 7, + .count = 8, }; if (ws->focused) { @@ -620,6 +645,7 @@ i3_new(struct i3_workspaces workspaces[], size_t workspace_count, { struct private *m = calloc(1, sizeof(*m)); + m->mode = strdup("default"); m->left_spacing = left_spacing; m->right_spacing = right_spacing; From 3da796810bd37e0038fcbbc71b20dcb862ae8eb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 8 Dec 2020 19:36:18 +0100 Subject: [PATCH 080/611] =?UTF-8?q?module/i3:=20take=20lock=20while=20upda?= =?UTF-8?q?ting=20=E2=80=98mode=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/i3.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/modules/i3.c b/modules/i3.c index 89083ce..c6975fb 100644 --- a/modules/i3.c +++ b/modules/i3.c @@ -474,9 +474,14 @@ handle_mode_event(int type, const struct json_object *json, void *_mod) } const char *current_mode = json_object_get_string(change); - free(m->mode); - m->mode = strdup(current_mode); - m->dirty = true; + + mtx_lock(&mod->lock); + { + free(m->mode); + m->mode = strdup(current_mode); + m->dirty = true; + } + mtx_unlock(&mod->lock); return true; } From e7f1d3e6b50d280a53d8885a76c9e1ee871fc3a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 19 Dec 2020 12:21:07 +0100 Subject: [PATCH 081/611] changelog: prepare for 1.6.0 --- CHANGELOG.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f5301e5..cf0b774 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,11 @@ # Changelog -* [Unreleased](#unreleased) +* [1.6.0](#1-6-0) * [1.5.0](#1-5-0) -## Unreleased +## 1.6.0 + ### Added * alsa: `percent` tag. This is an integer tag that represents the @@ -23,8 +24,6 @@ * i3: `mode` tag: the name of the currently active mode -### Deprecated -### Removed ### Fixed * YAML parsing error messages being replaced with a generic _“unknown @@ -39,7 +38,6 @@ disconnected (https://codeberg.org/dnkl/yambar/issues/20) -### Security ### Contributors * [JorwLNKwpH](https://codeberg.org/JorwLNKwpH) From 105409b0e0ce4fd102ac41f8c8481f7a26f476df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 19 Dec 2020 12:21:33 +0100 Subject: [PATCH 082/611] meson/pkgbuild: bump version to 1.6.0 --- PKGBUILD | 2 +- PKGBUILD.wayland-only | 2 +- meson.build | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/PKGBUILD b/PKGBUILD index e8229fa..0b9b02c 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -1,5 +1,5 @@ pkgname=yambar -pkgver=1.5.0 +pkgver=1.6.0 pkgrel=1 pkgdesc="Simplistic and highly configurable status panel for X and Wayland" arch=('x86_64' 'aarch64') diff --git a/PKGBUILD.wayland-only b/PKGBUILD.wayland-only index 65c5864..9da0d18 100644 --- a/PKGBUILD.wayland-only +++ b/PKGBUILD.wayland-only @@ -1,5 +1,5 @@ pkgname=yambar-wayland -pkgver=1.5.0 +pkgver=1.6.0 pkgrel=1 pkgdesc="Simplistic and highly configurable status panel for Wayland" arch=('x86_64' 'aarch64') diff --git a/meson.build b/meson.build index 2209469..dd6b910 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('yambar', 'c', - version: '1.5.0', + version: '1.6.0', license: 'MIT', meson_version: '>=0.53.0', default_options: ['c_std=c18', From 946678c7ef6b69b104fd80be58aae32736a3dd6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 19 Dec 2020 12:25:18 +0100 Subject: [PATCH 083/611] =?UTF-8?q?changelog:=20add=20new=20=E2=80=98unrel?= =?UTF-8?q?eased=E2=80=99=20section?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cf0b774..c946292 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,20 @@ # Changelog +* [Unreleased](#Unreleased) * [1.6.0](#1-6-0) * [1.5.0](#1-5-0) +## Unreleased + +### Added +### Changed +### Deprecated +### Removed +### Fixed +### Security + + ## 1.6.0 ### Added From dee61e62398a5a0a5424e206e8e603265873fad8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 20 Dec 2020 12:48:06 +0100 Subject: [PATCH 084/611] config: fix rounding error when calculating background color with alpha MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We use pre-multiplied alpha color channels, but were having bad rounding errors due to the alpha divider being truncated to an integer. The algorithm for pre-multiplying a color channel is: alpha_divider = 0xffff / alpha pre_mult_color = color / alpha_divider In order to fix the rounding errors, we could turn ‘alpha_divider’ into a double. That however would introduce a performance penalty since now we’d need to do floating point math for each cell. The algorithm can be trivially converted to: pre_mult_color = color * alpha / 0xffff Since both color and alpa values are < 65536, the multiplication is “safe”; it will not overflow an uint32_t. --- config.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/config.c b/config.c index 743a1a3..989bc2e 100644 --- a/config.c +++ b/config.c @@ -53,16 +53,12 @@ conf_to_color(const struct yml_node *node) uint16_t blue = hex_byte(&hex[4]); uint16_t alpha = hex_byte(&hex[6]); - if (alpha == 0) - return (pixman_color_t){0, 0, 0, 0}; - alpha |= alpha << 8; - int alpha_div = 0xffff / alpha; return (pixman_color_t){ - .red = (red << 8 | red) / alpha_div, - .green = (green << 8 | green) / alpha_div, - .blue = (blue << 8 | blue) / alpha_div, + .red = (red << 8 | red) * alpha / 0xffff, + .green = (green << 8 | green) * alpha / 0xffff, + .blue = (blue << 8 | blue) * alpha / 0xffff, .alpha = alpha, }; } From 9c03bd887fec2cb8c01eb7acecc1970540f6a98e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 22 Dec 2020 10:58:45 +0100 Subject: [PATCH 085/611] readme: add repology packaging status badge --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 8052784..1619bac 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,8 @@ # Yambar +[![Packaging status](https://repology.org/badge/vertical-allrepos/yambar.svg)](https://repology.org/project/yambar/versions) + + ## Index 1. [Introduction](#introduction) From 2262a3d8372da814e3d7be89300b2bd152849325 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 25 Dec 2020 12:22:15 +0100 Subject: [PATCH 086/611] subprojects: use meson wrap files for tllist+fcft --- .gitignore | 3 ++- README.md | 12 ------------ subprojects/fcft.wrap | 3 +++ subprojects/tllist.wrap | 3 +++ 4 files changed, 8 insertions(+), 13 deletions(-) create mode 100644 subprojects/fcft.wrap create mode 100644 subprojects/tllist.wrap diff --git a/.gitignore b/.gitignore index 1e67045..6630775 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ /bld/ /pkg/ /src/ -/subprojects/ +/subprojects/* +!/subprojects/*.wrap diff --git a/README.md b/README.md index 1619bac..f61d195 100644 --- a/README.md +++ b/README.md @@ -90,18 +90,6 @@ Available modules: ## Installation -If you have not installed [tllist](https://codeberg.org/dnkl/tllist) -and [fcft](https://codeberg.org/dnkl/fcft) as system libraries, clone -them into the `subprojects` directory: - -```sh -mkdir -p subprojects -pushd subprojects -git clone https://codeberg.org/dnkl/tllist.git -git clone https://codeberg.org/dnkl/fcft.git -popd -``` - To build, first, create a build directory, and switch to it: ```sh mkdir -p bld/release && cd bld/release diff --git a/subprojects/fcft.wrap b/subprojects/fcft.wrap new file mode 100644 index 0000000..d2709d4 --- /dev/null +++ b/subprojects/fcft.wrap @@ -0,0 +1,3 @@ +[wrap-git] +url = https://codeberg.org/dnkl/fcft.git +revision = master diff --git a/subprojects/tllist.wrap b/subprojects/tllist.wrap new file mode 100644 index 0000000..75f395a --- /dev/null +++ b/subprojects/tllist.wrap @@ -0,0 +1,3 @@ +[wrap-git] +url = https://codeberg.org/dnkl/tllist.git +revision = master From 3b4d822888e481679a0d515af46df1f2f3fe265e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 25 Dec 2020 12:23:16 +0100 Subject: [PATCH 087/611] ci: gitlab: no need to clone subprojects - we use meson wrap files --- .gitlab-ci.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index cb34bae..6764b34 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -17,10 +17,6 @@ before_script: - apk add json-c-dev libmpdclient-dev alsa-lib-dev - apk add ttf-dejavu - apk add git - - mkdir -p subprojects && cd subprojects - - git clone https://codeberg.org/dnkl/tllist.git - - git clone https://codeberg.org/dnkl/fcft.git - - cd .. versions: stage: info From d3512f41d78f38d49a6896d6722d007092a33bf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 25 Dec 2020 12:24:16 +0100 Subject: [PATCH 088/611] ci: sr.ht: bring up to date * Source from codeberg, not git.sr.ht * No need to manually install gcovr --- .build.yml | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/.build.yml b/.build.yml index 22b6766..7d3b602 100644 --- a/.build.yml +++ b/.build.yml @@ -23,18 +23,14 @@ packages: - gcovr sources: - - https://git.sr.ht/~dnkl/yambar + - https://codeberg.org/dnkl/yambar -triggers: - - action: email - condition: failure - to: daniel@ekloef.se +# triggers: +# - action: email +# condition: failure +# to: tasks: - - install-gcovr: | - python2 -m ensurepip --user --upgrade - python2 -m pip install --user --upgrade pip - python2 -m pip install --user --upgrade setuptools - setup: | mkdir -p bld/debug bld/release bld/x11-only bld/wayland-only bld/plugs-are-shared meson --buildtype=debug -Db_coverage=true yambar bld/debug From 547423556c1cb9d5c80248c4810077a760846ade Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 25 Dec 2020 12:25:05 +0100 Subject: [PATCH 089/611] ci: sr.ht: move to .builds/alpine-x64.yml --- .build.yml => .builds/alpine-x64.yml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .build.yml => .builds/alpine-x64.yml (100%) diff --git a/.build.yml b/.builds/alpine-x64.yml similarity index 100% rename from .build.yml rename to .builds/alpine-x64.yml From 3d8bdfadb443338fe39a0c07be3547ba4dd9a093 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 25 Dec 2020 12:30:40 +0100 Subject: [PATCH 090/611] ci: new: codespell --- .builds/alpine-x64.yml | 6 ++++++ .gitlab-ci.yml | 9 +++++++++ 2 files changed, 15 insertions(+) diff --git a/.builds/alpine-x64.yml b/.builds/alpine-x64.yml index 7d3b602..a360bb0 100644 --- a/.builds/alpine-x64.yml +++ b/.builds/alpine-x64.yml @@ -21,6 +21,8 @@ packages: - alsa-lib-dev - ttf-dejavu - gcovr + - python3 + - py3-pip sources: - https://codeberg.org/dnkl/yambar @@ -31,6 +33,10 @@ sources: # to: tasks: + - codespell: | + pip install codespell + cd yambar + ~/.local/bin/codespell *.c *.h - setup: | mkdir -p bld/debug bld/release bld/x11-only bld/wayland-only bld/plugs-are-shared meson --buildtype=debug -Db_coverage=true yambar bld/debug diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 6764b34..93d62f3 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -88,3 +88,12 @@ plugins_as_shared_modules: - meson --buildtype=debug -Dcore-plugins-as-shared-libraries=true ../../ - ninja -k0 - meson test --print-errorlogs + +codespell: + image: alpine:edge + stage: build + script: + - apk add python3 + - apk add py3-pip + - pip install codespell + - codespell *.c *.h From 9cf00f8200a66478fa2f41566fb1a3585feaeb94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 25 Dec 2020 12:31:20 +0100 Subject: [PATCH 091/611] codespell: spelling fixes --- tag.c | 2 +- yml.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tag.c b/tag.c index 52750fe..02e0794 100644 --- a/tag.c +++ b/tag.c @@ -454,7 +454,7 @@ tags_expand_template(const char *template, const struct tag_set *tags) continue; } - /* Copy characters preceeding the tag (name) */ + /* Copy characters preceding the tag (name) */ sbuf_append_at_most(&formatted, template, begin - template); /* Parse arguments */ diff --git a/yml.c b/yml.c index 40ece40..bdf474a 100644 --- a/yml.c +++ b/yml.c @@ -228,7 +228,7 @@ post_process(struct yml_node *node) tll_push_back(node->dict.pairs, p); } - /* Destroy lits, but don't free (since its nodes + /* Destroy list, but don't free (since its nodes * have been moved to this node), *before* * destroying the key/value nodes. This ensures * the dict nodes aren't free:d in the From 761f2e0b21132d8c22280959e7c8255d2e4da3e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 26 Dec 2020 13:00:00 +0100 Subject: [PATCH 092/611] ci: run codespell on the man pages --- .builds/alpine-x64.yml | 2 +- .gitlab-ci.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.builds/alpine-x64.yml b/.builds/alpine-x64.yml index a360bb0..7ae7f1b 100644 --- a/.builds/alpine-x64.yml +++ b/.builds/alpine-x64.yml @@ -36,7 +36,7 @@ tasks: - codespell: | pip install codespell cd yambar - ~/.local/bin/codespell *.c *.h + ~/.local/bin/codespell *.c *.h doc/*.scd - setup: | mkdir -p bld/debug bld/release bld/x11-only bld/wayland-only bld/plugs-are-shared meson --buildtype=debug -Db_coverage=true yambar bld/debug diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 93d62f3..586e79c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -96,4 +96,4 @@ codespell: - apk add python3 - apk add py3-pip - pip install codespell - - codespell *.c *.h + - codespell *.c *.h doc/*.scd From b679e8ce9aead47de551fba74466e7a886e0c652 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 26 Dec 2020 13:01:21 +0100 Subject: [PATCH 093/611] doc: codespell fixes --- doc/yambar-modules.5.scd | 6 +++--- doc/yambar-particles.5.scd | 2 +- doc/yambar-tags.5.scd | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/yambar-modules.5.scd b/doc/yambar-modules.5.scd index 5bc15e1..14aed8f 100644 --- a/doc/yambar-modules.5.scd +++ b/doc/yambar-modules.5.scd @@ -539,7 +539,7 @@ address. | state : string : One of *unknown*, *not present*, *down*, *lower layers down*, - *testing*, *dormant* or *up*. You are probably interrested in *down* and *up*. + *testing*, *dormant* or *up*. You are probably interested in *down* and *up*. | mac : string : MAC address @@ -659,7 +659,7 @@ about the river tags. It has an interface similar to the i3/sway module. -The configuration for the river module speficies one _title_ particle, +The configuration for the river module specifies one _title_ particle, which will be instantiated with tags representing the currently active seat and the currently focused view's title. @@ -838,7 +838,7 @@ instantiated from this template, and represents an input device. :[ *Description* | id : string -: Input device indentifier +: Input device identifier | layout : string : The input device's currently active XKB layout diff --git a/doc/yambar-particles.5.scd b/doc/yambar-particles.5.scd index 6dbd6a1..08977c5 100644 --- a/doc/yambar-particles.5.scd +++ b/doc/yambar-particles.5.scd @@ -224,7 +224,7 @@ indicator. : list : yes : List of particles. Note that the tag value is *not* used as-is; its - minumum and maximum values are used to map the tag's range to the + minimum and maximum values are used to map the tag's range to the particle list's range. ## EXAMPLES diff --git a/doc/yambar-tags.5.scd b/doc/yambar-tags.5.scd index 32132cf..1e3c138 100644 --- a/doc/yambar-tags.5.scd +++ b/doc/yambar-tags.5.scd @@ -26,7 +26,7 @@ The available tag *types* are: : Value is an integer, with a minimum and maximum value associated with it. By default, the _string_ particle renders the value. The *:min* or *:max* suffixes may be added to instead render the - mininum or maximum value (_\"{tag_name:min}\"_). + minimum or maximum value (_\"{tag_name:min}\"_). | realtime : Value is an integer that changes in a predictable manner (in "realtime"). This allows the particle to update itself From 77ca2e8225de8b8841fb5d835d0d8f94b85715b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 26 Dec 2020 13:04:40 +0100 Subject: [PATCH 094/611] ci: run codespell on README.md --- .builds/alpine-x64.yml | 2 +- .gitlab-ci.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.builds/alpine-x64.yml b/.builds/alpine-x64.yml index 7ae7f1b..0a40bd2 100644 --- a/.builds/alpine-x64.yml +++ b/.builds/alpine-x64.yml @@ -36,7 +36,7 @@ tasks: - codespell: | pip install codespell cd yambar - ~/.local/bin/codespell *.c *.h doc/*.scd + ~/.local/bin/codespell README.md *.c *.h doc/*.scd - setup: | mkdir -p bld/debug bld/release bld/x11-only bld/wayland-only bld/plugs-are-shared meson --buildtype=debug -Db_coverage=true yambar bld/debug diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 586e79c..88d1fbe 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -96,4 +96,4 @@ codespell: - apk add python3 - apk add py3-pip - pip install codespell - - codespell *.c *.h doc/*.scd + - codespell README.md *.c *.h doc/*.scd From e2c2f48203b5cb9e7ca74b45976ec27089a2af78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 26 Dec 2020 13:08:42 +0100 Subject: [PATCH 095/611] ci: run codespell on CHANGELOG.md --- .builds/alpine-x64.yml | 2 +- .gitlab-ci.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.builds/alpine-x64.yml b/.builds/alpine-x64.yml index 0a40bd2..1e8961a 100644 --- a/.builds/alpine-x64.yml +++ b/.builds/alpine-x64.yml @@ -36,7 +36,7 @@ tasks: - codespell: | pip install codespell cd yambar - ~/.local/bin/codespell README.md *.c *.h doc/*.scd + ~/.local/bin/codespell README.md CHANGELOG.md *.c *.h doc/*.scd - setup: | mkdir -p bld/debug bld/release bld/x11-only bld/wayland-only bld/plugs-are-shared meson --buildtype=debug -Db_coverage=true yambar bld/debug diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 88d1fbe..e9446a6 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -96,4 +96,4 @@ codespell: - apk add python3 - apk add py3-pip - pip install codespell - - codespell README.md *.c *.h doc/*.scd + - codespell README.md CHANGELOG.md *.c *.h doc/*.scd From 3603ca982a94bc1ea12972c926e49298f6bc98be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 2 Jan 2021 12:53:24 +0100 Subject: [PATCH 096/611] config: fix asan signed integer overflow warning --- config.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/config.c b/config.c index 989bc2e..aaa62f9 100644 --- a/config.c +++ b/config.c @@ -56,9 +56,9 @@ conf_to_color(const struct yml_node *node) alpha |= alpha << 8; return (pixman_color_t){ - .red = (red << 8 | red) * alpha / 0xffff, - .green = (green << 8 | green) * alpha / 0xffff, - .blue = (blue << 8 | blue) * alpha / 0xffff, + .red = (uint32_t)(red << 8 | red) * alpha / 0xffff, + .green = (uint32_t)(green << 8 | green) * alpha / 0xffff, + .blue = (uint32_t)(blue << 8 | blue) * alpha / 0xffff, .alpha = alpha, }; } From 86cc3f0918429c30308780098041182015ec49f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 2 Jan 2021 13:09:12 +0100 Subject: [PATCH 097/611] examples: laptop: add i3-mode Closes #27 --- examples/configurations/laptop.conf | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/examples/configurations/laptop.conf b/examples/configurations/laptop.conf index d124e72..f6c1df5 100644 --- a/examples/configurations/laptop.conf +++ b/examples/configurations/laptop.conf @@ -45,6 +45,15 @@ bar: - urgent: &urgent foreground: 000000ff deco: {stack: [background: {color: bc2b3fff}, <<: *std_underline]} + - map: &i3_mode + tag: mode + default: + - string: + text: "{mode}" + deco: {background: {color: cc421dff}} + - empty: {right-margin: 7} + values: + default: {empty: {}} content: "": map: @@ -100,11 +109,14 @@ bar: left-margin: 7 tag: application values: - "": {string: {text: "{title}"}} + "": + - map: {<<: *i3_mode} + - string: {text: "{title}"} default: list: spacing: 0 items: + - map: {<<: *i3_mode} - string: {text: "{application}", max: 10, foreground: ffa0a0ff} - string: {text: ": "} - string: {text: "{title}", max: 35} From 87b6b986954564ca3f0beb8e021ef4587f29b5c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 2 Jan 2021 14:49:13 +0100 Subject: [PATCH 098/611] examples: laptop: add margin around i3 mode --- examples/configurations/laptop.conf | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/configurations/laptop.conf b/examples/configurations/laptop.conf index f6c1df5..b07ed8a 100644 --- a/examples/configurations/laptop.conf +++ b/examples/configurations/laptop.conf @@ -49,6 +49,7 @@ bar: tag: mode default: - string: + margin: 5 text: "{mode}" deco: {background: {color: cc421dff}} - empty: {right-margin: 7} From e85e3e8ced895d012f667ae57f93a93fc15af312 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 4 Jan 2021 20:01:46 +0100 Subject: [PATCH 099/611] module/mpd: increase tag count in tag set MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://codeberg.org/dnkl/yambar/commit/96d2d057e0197914dde9f37f37726bedb6a8715f added a new tag, ‘volume’, but didn’t bump the tag count. This meant the last tag in the set, ‘elapsed’ was never seen by anybody, and not free:d when the tag set was free:d. --- modules/mpd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mpd.c b/modules/mpd.c index ed80751..a501f76 100644 --- a/modules/mpd.c +++ b/modules/mpd.c @@ -173,7 +173,7 @@ content(struct module *mod) tag_new_int_realtime( mod, "elapsed", elapsed, 0, m->duration, realtime), }, - .count = 11, + .count = 12, }; mtx_unlock(&mod->lock); From 5bfa104935abc70b6834b0e0b37f67d7051c2ff9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 4 Jan 2021 20:16:30 +0100 Subject: [PATCH 100/611] module/i3: separate numerical workspace names from non-numerical When sorting workspaces in ascending order, put numerical workspaces *after* non-numerical ones. When sorting in descending order, put numerical workspaces *before* non-numerical. In both cases, sort numerical workspaces using a numerical comparison, rather that doing a lexicographical sorting. Closes #30 --- modules/i3.c | 59 ++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 50 insertions(+), 9 deletions(-) diff --git a/modules/i3.c b/modules/i3.c index c6975fb..e2f58de 100644 --- a/modules/i3.c +++ b/modules/i3.c @@ -30,6 +30,8 @@ struct ws_content { struct workspace { char *name; + int name_as_int; /* -1 is name is not a decimal number */ + char *output; bool visible; bool focused; @@ -78,8 +80,22 @@ workspace_from_json(const struct json_object *json, struct workspace *ws) json_object_object_get_ex(json, "focused", &focused); json_object_object_get_ex(json, "urgent", &urgent); + const char *name_as_string = json_object_get_string(name); + + int name_as_int = 0; + for (const char *p = name_as_string; *p != '\0'; p++) { + if (!(*p >= '0' && *p <= '9')) { + name_as_int = -1; + break; + } + + name_as_int *= 10; + name_as_int += *p - '0'; + } + *ws = (struct workspace) { - .name = strdup(json_object_get_string(name)), + .name = strdup(name_as_string), + .name_as_int = name_as_int, .output = strdup(json_object_get_string(output)), .visible = json_object_get_boolean(visible), .focused = json_object_get_boolean(focused), @@ -107,6 +123,7 @@ workspaces_free(struct private *m) tll_free(m->workspaces); } + static void workspace_add(struct private *m, struct workspace ws) { @@ -116,20 +133,44 @@ workspace_add(struct private *m, struct workspace ws) return; case SORT_ASCENDING: - tll_foreach(m->workspaces, it) { - if (strcoll(it->item.name, ws.name) > 0) { - tll_insert_before(m->workspaces, it, ws); - return; + if (ws.name_as_int >= 0) { + tll_foreach(m->workspaces, it) { + if (it->item.name_as_int < 0) + continue; + if (it->item.name_as_int > ws.name_as_int) { + tll_insert_before(m->workspaces, it, ws); + return; + } + } + } else { + tll_foreach(m->workspaces, it) { + if (strcoll(it->item.name, ws.name) > 0 || + it->item.name_as_int >= 0) + { + tll_insert_before(m->workspaces, it, ws); + return; + } } } tll_push_back(m->workspaces, ws); return; case SORT_DESCENDING: - tll_foreach(m->workspaces, it) { - if (strcoll(it->item.name, ws.name) < 0) { - tll_insert_before(m->workspaces, it, ws); - return; + if (ws.name_as_int >= 0) { + tll_foreach(m->workspaces, it) { + if (it->item.name_as_int < ws.name_as_int) { + tll_insert_before(m->workspaces, it, ws); + return; + } + } + } else { + tll_foreach(m->workspaces, it) { + if (it->item.name_as_int >= 0) + continue; + if (strcoll(it->item.name, ws.name) < 0) { + tll_insert_before(m->workspaces, it, ws); + return; + } } } tll_push_back(m->workspaces, ws); From 18e6f0e4bd00da8d86bee555fdd7041a846634c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 14 Jan 2021 11:02:45 +0100 Subject: [PATCH 101/611] changelog: mention fix for mpd tags --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c946292..1b712c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,10 @@ ### Deprecated ### Removed ### Fixed + +* mpd: `elapsed` tag not working (regression, introduced in 1.6.0). + + ### Security From 1e4a282fb514a45d02de1830a663e291bb1adfa1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 14 Jan 2021 11:04:13 +0100 Subject: [PATCH 102/611] changelog: mention fix for wrong background colors when not fully opaque --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b712c7..09d9be9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ ### Fixed * mpd: `elapsed` tag not working (regression, introduced in 1.6.0). +* Wrong background color for (semi-) transparent backgrounds. ### Security From c14d5b3b9c93542a2b67425030fa38cf4119eb6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 14 Jan 2021 11:06:33 +0100 Subject: [PATCH 103/611] changelog: i3: numerically names workspaces are sorted separately --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 09d9be9..1a88dd3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,12 @@ ### Added ### Changed + +* i3: workspaces with numerical names are sorted separately from + non-numerically named workspaces + (https://codeberg.org/dnkl/yambar/issues/30). + + ### Deprecated ### Removed ### Fixed From 8afa17fa8702ae5d1ec1e83c126b1d97ee8cf1aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 19 Dec 2020 12:25:18 +0100 Subject: [PATCH 104/611] =?UTF-8?q?changelog:=20add=20new=20=E2=80=98unrel?= =?UTF-8?q?eased=E2=80=99=20section?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cf0b774..c946292 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,20 @@ # Changelog +* [Unreleased](#Unreleased) * [1.6.0](#1-6-0) * [1.5.0](#1-5-0) +## Unreleased + +### Added +### Changed +### Deprecated +### Removed +### Fixed +### Security + + ## 1.6.0 ### Added From 492efdd10eafab85d5518a26f4bb4d62abb28cfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 20 Dec 2020 12:48:06 +0100 Subject: [PATCH 105/611] config: fix rounding error when calculating background color with alpha MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We use pre-multiplied alpha color channels, but were having bad rounding errors due to the alpha divider being truncated to an integer. The algorithm for pre-multiplying a color channel is: alpha_divider = 0xffff / alpha pre_mult_color = color / alpha_divider In order to fix the rounding errors, we could turn ‘alpha_divider’ into a double. That however would introduce a performance penalty since now we’d need to do floating point math for each cell. The algorithm can be trivially converted to: pre_mult_color = color * alpha / 0xffff Since both color and alpa values are < 65536, the multiplication is “safe”; it will not overflow an uint32_t. --- config.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/config.c b/config.c index 743a1a3..989bc2e 100644 --- a/config.c +++ b/config.c @@ -53,16 +53,12 @@ conf_to_color(const struct yml_node *node) uint16_t blue = hex_byte(&hex[4]); uint16_t alpha = hex_byte(&hex[6]); - if (alpha == 0) - return (pixman_color_t){0, 0, 0, 0}; - alpha |= alpha << 8; - int alpha_div = 0xffff / alpha; return (pixman_color_t){ - .red = (red << 8 | red) / alpha_div, - .green = (green << 8 | green) / alpha_div, - .blue = (blue << 8 | blue) / alpha_div, + .red = (red << 8 | red) * alpha / 0xffff, + .green = (green << 8 | green) * alpha / 0xffff, + .blue = (blue << 8 | blue) * alpha / 0xffff, .alpha = alpha, }; } From 536f9d22ec20dfb87bbeb4f5582157ed67a147a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 25 Dec 2020 12:31:20 +0100 Subject: [PATCH 106/611] codespell: spelling fixes --- tag.c | 2 +- yml.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tag.c b/tag.c index 52750fe..02e0794 100644 --- a/tag.c +++ b/tag.c @@ -454,7 +454,7 @@ tags_expand_template(const char *template, const struct tag_set *tags) continue; } - /* Copy characters preceeding the tag (name) */ + /* Copy characters preceding the tag (name) */ sbuf_append_at_most(&formatted, template, begin - template); /* Parse arguments */ diff --git a/yml.c b/yml.c index 40ece40..bdf474a 100644 --- a/yml.c +++ b/yml.c @@ -228,7 +228,7 @@ post_process(struct yml_node *node) tll_push_back(node->dict.pairs, p); } - /* Destroy lits, but don't free (since its nodes + /* Destroy list, but don't free (since its nodes * have been moved to this node), *before* * destroying the key/value nodes. This ensures * the dict nodes aren't free:d in the From 5b13b5315fee94b8536ef6a4419b68226fbe3866 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 26 Dec 2020 13:01:21 +0100 Subject: [PATCH 107/611] doc: codespell fixes --- doc/yambar-modules.5.scd | 6 +++--- doc/yambar-particles.5.scd | 2 +- doc/yambar-tags.5.scd | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/yambar-modules.5.scd b/doc/yambar-modules.5.scd index 5bc15e1..14aed8f 100644 --- a/doc/yambar-modules.5.scd +++ b/doc/yambar-modules.5.scd @@ -539,7 +539,7 @@ address. | state : string : One of *unknown*, *not present*, *down*, *lower layers down*, - *testing*, *dormant* or *up*. You are probably interrested in *down* and *up*. + *testing*, *dormant* or *up*. You are probably interested in *down* and *up*. | mac : string : MAC address @@ -659,7 +659,7 @@ about the river tags. It has an interface similar to the i3/sway module. -The configuration for the river module speficies one _title_ particle, +The configuration for the river module specifies one _title_ particle, which will be instantiated with tags representing the currently active seat and the currently focused view's title. @@ -838,7 +838,7 @@ instantiated from this template, and represents an input device. :[ *Description* | id : string -: Input device indentifier +: Input device identifier | layout : string : The input device's currently active XKB layout diff --git a/doc/yambar-particles.5.scd b/doc/yambar-particles.5.scd index 6dbd6a1..08977c5 100644 --- a/doc/yambar-particles.5.scd +++ b/doc/yambar-particles.5.scd @@ -224,7 +224,7 @@ indicator. : list : yes : List of particles. Note that the tag value is *not* used as-is; its - minumum and maximum values are used to map the tag's range to the + minimum and maximum values are used to map the tag's range to the particle list's range. ## EXAMPLES diff --git a/doc/yambar-tags.5.scd b/doc/yambar-tags.5.scd index 32132cf..1e3c138 100644 --- a/doc/yambar-tags.5.scd +++ b/doc/yambar-tags.5.scd @@ -26,7 +26,7 @@ The available tag *types* are: : Value is an integer, with a minimum and maximum value associated with it. By default, the _string_ particle renders the value. The *:min* or *:max* suffixes may be added to instead render the - mininum or maximum value (_\"{tag_name:min}\"_). + minimum or maximum value (_\"{tag_name:min}\"_). | realtime : Value is an integer that changes in a predictable manner (in "realtime"). This allows the particle to update itself From 47c42b507f06e87f87e77d6346191a2f5ae185f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 2 Jan 2021 12:53:24 +0100 Subject: [PATCH 108/611] config: fix asan signed integer overflow warning --- config.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/config.c b/config.c index 989bc2e..aaa62f9 100644 --- a/config.c +++ b/config.c @@ -56,9 +56,9 @@ conf_to_color(const struct yml_node *node) alpha |= alpha << 8; return (pixman_color_t){ - .red = (red << 8 | red) * alpha / 0xffff, - .green = (green << 8 | green) * alpha / 0xffff, - .blue = (blue << 8 | blue) * alpha / 0xffff, + .red = (uint32_t)(red << 8 | red) * alpha / 0xffff, + .green = (uint32_t)(green << 8 | green) * alpha / 0xffff, + .blue = (uint32_t)(blue << 8 | blue) * alpha / 0xffff, .alpha = alpha, }; } From f88f7a5046e425ad7d8195d4975486d105bcac62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 4 Jan 2021 20:01:46 +0100 Subject: [PATCH 109/611] module/mpd: increase tag count in tag set MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://codeberg.org/dnkl/yambar/commit/96d2d057e0197914dde9f37f37726bedb6a8715f added a new tag, ‘volume’, but didn’t bump the tag count. This meant the last tag in the set, ‘elapsed’ was never seen by anybody, and not free:d when the tag set was free:d. --- modules/mpd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mpd.c b/modules/mpd.c index ed80751..a501f76 100644 --- a/modules/mpd.c +++ b/modules/mpd.c @@ -173,7 +173,7 @@ content(struct module *mod) tag_new_int_realtime( mod, "elapsed", elapsed, 0, m->duration, realtime), }, - .count = 11, + .count = 12, }; mtx_unlock(&mod->lock); From 6cbf093af95d3f250d4334100387e8884c6a707b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 4 Jan 2021 20:16:30 +0100 Subject: [PATCH 110/611] module/i3: separate numerical workspace names from non-numerical When sorting workspaces in ascending order, put numerical workspaces *after* non-numerical ones. When sorting in descending order, put numerical workspaces *before* non-numerical. In both cases, sort numerical workspaces using a numerical comparison, rather that doing a lexicographical sorting. Closes #30 --- modules/i3.c | 59 ++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 50 insertions(+), 9 deletions(-) diff --git a/modules/i3.c b/modules/i3.c index c6975fb..e2f58de 100644 --- a/modules/i3.c +++ b/modules/i3.c @@ -30,6 +30,8 @@ struct ws_content { struct workspace { char *name; + int name_as_int; /* -1 is name is not a decimal number */ + char *output; bool visible; bool focused; @@ -78,8 +80,22 @@ workspace_from_json(const struct json_object *json, struct workspace *ws) json_object_object_get_ex(json, "focused", &focused); json_object_object_get_ex(json, "urgent", &urgent); + const char *name_as_string = json_object_get_string(name); + + int name_as_int = 0; + for (const char *p = name_as_string; *p != '\0'; p++) { + if (!(*p >= '0' && *p <= '9')) { + name_as_int = -1; + break; + } + + name_as_int *= 10; + name_as_int += *p - '0'; + } + *ws = (struct workspace) { - .name = strdup(json_object_get_string(name)), + .name = strdup(name_as_string), + .name_as_int = name_as_int, .output = strdup(json_object_get_string(output)), .visible = json_object_get_boolean(visible), .focused = json_object_get_boolean(focused), @@ -107,6 +123,7 @@ workspaces_free(struct private *m) tll_free(m->workspaces); } + static void workspace_add(struct private *m, struct workspace ws) { @@ -116,20 +133,44 @@ workspace_add(struct private *m, struct workspace ws) return; case SORT_ASCENDING: - tll_foreach(m->workspaces, it) { - if (strcoll(it->item.name, ws.name) > 0) { - tll_insert_before(m->workspaces, it, ws); - return; + if (ws.name_as_int >= 0) { + tll_foreach(m->workspaces, it) { + if (it->item.name_as_int < 0) + continue; + if (it->item.name_as_int > ws.name_as_int) { + tll_insert_before(m->workspaces, it, ws); + return; + } + } + } else { + tll_foreach(m->workspaces, it) { + if (strcoll(it->item.name, ws.name) > 0 || + it->item.name_as_int >= 0) + { + tll_insert_before(m->workspaces, it, ws); + return; + } } } tll_push_back(m->workspaces, ws); return; case SORT_DESCENDING: - tll_foreach(m->workspaces, it) { - if (strcoll(it->item.name, ws.name) < 0) { - tll_insert_before(m->workspaces, it, ws); - return; + if (ws.name_as_int >= 0) { + tll_foreach(m->workspaces, it) { + if (it->item.name_as_int < ws.name_as_int) { + tll_insert_before(m->workspaces, it, ws); + return; + } + } + } else { + tll_foreach(m->workspaces, it) { + if (it->item.name_as_int >= 0) + continue; + if (strcoll(it->item.name, ws.name) < 0) { + tll_insert_before(m->workspaces, it, ws); + return; + } } } tll_push_back(m->workspaces, ws); From cfeb5260dd403c34ac41cf53de281f1249f6772e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 2 Jan 2021 12:51:34 +0100 Subject: [PATCH 111/611] module/battery: re-open files on every update Closes #25 --- modules/battery.c | 67 ++++++++++++++++++++++++++++------------------- 1 file changed, 40 insertions(+), 27 deletions(-) diff --git a/modules/battery.c b/modules/battery.c index 99bad3c..b4da871 100644 --- a/modules/battery.c +++ b/modules/battery.c @@ -276,11 +276,29 @@ err: } static void -update_status(struct module *mod, int capacity_fd, int energy_fd, int power_fd, - int charge_fd, int current_fd, int status_fd, int time_to_empty_fd) +update_status(struct module *mod, int base_dir_fd) { struct private *m = mod->private; + int status_fd = openat(base_dir_fd, "status", O_RDONLY); + if (status_fd < 0) { + LOG_ERRNO("status: failed to open"); + return; + } + + int capacity_fd = openat(base_dir_fd, "capacity", O_RDONLY); + if (capacity_fd < 0) { + LOG_ERRNO("capacity: failed to open"); + close(status_fd); + return; + } + + int energy_fd = openat(base_dir_fd, "energy_now", O_RDONLY); + int power_fd = openat(base_dir_fd, "power_now", O_RDONLY); + int charge_fd = openat(base_dir_fd, "charge_now", O_RDONLY); + int current_fd = openat(base_dir_fd, "current_now", O_RDONLY); + int time_to_empty_fd = openat(base_dir_fd, "time_to_empty_now", O_RDONLY); + long capacity = readint_from_fd(capacity_fd); long energy = energy_fd >= 0 ? readint_from_fd(energy_fd) : -1; long power = power_fd >= 0 ? readint_from_fd(power_fd) : -1; @@ -289,6 +307,22 @@ update_status(struct module *mod, int capacity_fd, int energy_fd, int power_fd, long time_to_empty = time_to_empty_fd >= 0 ? readint_from_fd(time_to_empty_fd) : -1; const char *status = readline_from_fd(status_fd); + + if (status_fd >= 0) + close(status_fd); + if (capacity_fd >= 0) + close(capacity_fd); + if (energy_fd >= 0) + close(energy_fd); + if (power_fd >= 0) + close(power_fd); + if (charge_fd >= 0) + close(charge_fd); + if (current_fd >= 0) + close(current_fd); + if (time_to_empty_fd >= 0) + close(time_to_empty_fd); + enum state state; if (status == NULL) { @@ -322,6 +356,7 @@ update_status(struct module *mod, int capacity_fd, int energy_fd, int power_fd, m->current = current; m->time_to_empty = time_to_empty; mtx_unlock(&mod->lock); + } static int @@ -343,26 +378,17 @@ run(struct module *mod) : 0.0)); int ret = 1; - int status_fd = openat(base_dir_fd, "status", O_RDONLY); - int capacity_fd = openat(base_dir_fd, "capacity", O_RDONLY); - int energy_fd = openat(base_dir_fd, "energy_now", O_RDONLY); - int power_fd = openat(base_dir_fd, "power_now", O_RDONLY); - int charge_fd = openat(base_dir_fd, "charge_now", O_RDONLY); - int current_fd = openat(base_dir_fd, "current_now", O_RDONLY); - int time_to_empty_fd = openat(base_dir_fd, "time_to_empty_now", O_RDONLY); struct udev *udev = udev_new(); struct udev_monitor *mon = udev_monitor_new_from_netlink(udev, "udev"); - if (status_fd < 0 || capacity_fd < 0 || udev == NULL || mon == NULL) + if (udev == NULL || mon == NULL) goto out; udev_monitor_filter_add_match_subsystem_devtype(mon, "power_supply", NULL); udev_monitor_enable_receiving(mon); - update_status( - mod, capacity_fd, energy_fd, power_fd, - charge_fd, current_fd, status_fd, time_to_empty_fd); + update_status(mod, base_dir_fd); bar->refresh(bar); while (true) { @@ -388,9 +414,7 @@ run(struct module *mod) continue; } - update_status( - mod, capacity_fd, energy_fd, power_fd, - charge_fd, current_fd, status_fd, time_to_empty_fd); + update_status(mod, base_dir_fd); bar->refresh(bar); } @@ -400,17 +424,6 @@ out: if (udev != NULL) udev_unref(udev); - if (power_fd != -1) - close(power_fd); - if (energy_fd != -1) - close(energy_fd); - if (capacity_fd != -1) - close(capacity_fd); - if (status_fd != -1) - close(status_fd); - if (time_to_empty_fd != -1) - close(time_to_empty_fd); - close(base_dir_fd); return ret; } From 4f703466016851ec080a5128ebfa6f1da37c082a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 4 Jan 2021 19:59:58 +0100 Subject: [PATCH 112/611] module/battery: re-open /sys/class/power_supply// every update --- modules/battery.c | 61 +++++++++++++++++++++++++++++++---------------- 1 file changed, 40 insertions(+), 21 deletions(-) diff --git a/modules/battery.c b/modules/battery.c index b4da871..7dcfc45 100644 --- a/modules/battery.c +++ b/modules/battery.c @@ -171,21 +171,21 @@ readint_from_fd(int fd) return ret; } -static int +static bool initialize(struct private *m) { int pw_fd = open("/sys/class/power_supply", O_RDONLY); - if (pw_fd == -1) { + if (pw_fd < 0) { LOG_ERRNO("/sys/class/power_supply"); - return -1; + return false; } int base_dir_fd = openat(pw_fd, m->battery, O_RDONLY); close(pw_fd); - if (base_dir_fd == -1) { - LOG_ERRNO("%s", m->battery); - return -1; + if (base_dir_fd < 0) { + LOG_ERRNO("/sys/class/power_supply/%s", m->battery); + return false; } { @@ -268,29 +268,46 @@ initialize(struct private *m) m->charge_full = m->charge_full_design = -1; } - return base_dir_fd; + close(base_dir_fd); + return true; err: close(base_dir_fd); - return -1; + return false; } -static void -update_status(struct module *mod, int base_dir_fd) +static bool +update_status(struct module *mod) { struct private *m = mod->private; + int pw_fd = open("/sys/class/power_supply", O_RDONLY); + if (pw_fd < 0) { + LOG_ERRNO("/sys/class/power_supply"); + return false; + } + + int base_dir_fd = openat(pw_fd, m->battery, O_RDONLY); + close(pw_fd); + + if (base_dir_fd < 0) { + LOG_ERRNO("/sys/class/power_supply/%s", m->battery); + return false; + } + int status_fd = openat(base_dir_fd, "status", O_RDONLY); if (status_fd < 0) { - LOG_ERRNO("status: failed to open"); - return; + LOG_ERRNO("/sys/class/power_supply/%s/status", m->battery); + close(base_dir_fd); + return false; } int capacity_fd = openat(base_dir_fd, "capacity", O_RDONLY); if (capacity_fd < 0) { - LOG_ERRNO("capacity: failed to open"); + LOG_ERRNO("/sys/class/power_supply/%s/capacity", m->battery); close(status_fd); - return; + close(base_dir_fd); + return false; } int energy_fd = openat(base_dir_fd, "energy_now", O_RDONLY); @@ -322,6 +339,8 @@ update_status(struct module *mod, int base_dir_fd) close(current_fd); if (time_to_empty_fd >= 0) close(time_to_empty_fd); + if (base_dir_fd >= 0) + close(base_dir_fd); enum state state; @@ -356,7 +375,7 @@ update_status(struct module *mod, int base_dir_fd) m->current = current; m->time_to_empty = time_to_empty; mtx_unlock(&mod->lock); - + return true; } static int @@ -365,8 +384,7 @@ run(struct module *mod) const struct bar *bar = mod->bar; struct private *m = mod->private; - int base_dir_fd = initialize(m); - if (base_dir_fd == -1) + if (!initialize(m)) return -1; LOG_INFO("%s: %s %s (at %.1f%% of original capacity)", @@ -388,7 +406,9 @@ run(struct module *mod) udev_monitor_filter_add_match_subsystem_devtype(mon, "power_supply", NULL); udev_monitor_enable_receiving(mon); - update_status(mod, base_dir_fd); + if (!update_status(mod)) + goto out; + bar->refresh(bar); while (true) { @@ -414,7 +434,8 @@ run(struct module *mod) continue; } - update_status(mod, base_dir_fd); + if (!update_status(mod)) + break; bar->refresh(bar); } @@ -423,8 +444,6 @@ out: udev_monitor_unref(mon); if (udev != NULL) udev_unref(udev); - - close(base_dir_fd); return ret; } From d684dc04630cf5292c12d4a48bd91acbeafb675d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 14 Jan 2021 11:09:50 +0100 Subject: [PATCH 113/611] changelog: battery stats no longer getting stuck --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a88dd3..7175cd3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,8 @@ * mpd: `elapsed` tag not working (regression, introduced in 1.6.0). * Wrong background color for (semi-) transparent backgrounds. +* battery: stats sometimes getting stuck at 0, or impossibly large + values (https://codeberg.org/dnkl/yambar/issues/25). ### Security From 2f66ac75ba3c6902f910e6160629340482240a51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 14 Jan 2021 11:02:45 +0100 Subject: [PATCH 114/611] changelog: mention fix for mpd tags --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c946292..1b712c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,10 @@ ### Deprecated ### Removed ### Fixed + +* mpd: `elapsed` tag not working (regression, introduced in 1.6.0). + + ### Security From 72660b3c014ff0790edaac18d35da5ab8821483a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 14 Jan 2021 11:04:13 +0100 Subject: [PATCH 115/611] changelog: mention fix for wrong background colors when not fully opaque --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b712c7..09d9be9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ ### Fixed * mpd: `elapsed` tag not working (regression, introduced in 1.6.0). +* Wrong background color for (semi-) transparent backgrounds. ### Security From 972395bf2e20f356ffee2c3caa5e1f2fd97e4cdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 14 Jan 2021 11:06:33 +0100 Subject: [PATCH 116/611] changelog: i3: numerically names workspaces are sorted separately --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 09d9be9..1a88dd3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,12 @@ ### Added ### Changed + +* i3: workspaces with numerical names are sorted separately from + non-numerically named workspaces + (https://codeberg.org/dnkl/yambar/issues/30). + + ### Deprecated ### Removed ### Fixed From 8f3de369aca6b36eeef5f13bad0031f3f8f916a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 2 Jan 2021 12:51:34 +0100 Subject: [PATCH 117/611] module/battery: re-open files on every update Closes #25 --- modules/battery.c | 67 ++++++++++++++++++++++++++++------------------- 1 file changed, 40 insertions(+), 27 deletions(-) diff --git a/modules/battery.c b/modules/battery.c index 99bad3c..b4da871 100644 --- a/modules/battery.c +++ b/modules/battery.c @@ -276,11 +276,29 @@ err: } static void -update_status(struct module *mod, int capacity_fd, int energy_fd, int power_fd, - int charge_fd, int current_fd, int status_fd, int time_to_empty_fd) +update_status(struct module *mod, int base_dir_fd) { struct private *m = mod->private; + int status_fd = openat(base_dir_fd, "status", O_RDONLY); + if (status_fd < 0) { + LOG_ERRNO("status: failed to open"); + return; + } + + int capacity_fd = openat(base_dir_fd, "capacity", O_RDONLY); + if (capacity_fd < 0) { + LOG_ERRNO("capacity: failed to open"); + close(status_fd); + return; + } + + int energy_fd = openat(base_dir_fd, "energy_now", O_RDONLY); + int power_fd = openat(base_dir_fd, "power_now", O_RDONLY); + int charge_fd = openat(base_dir_fd, "charge_now", O_RDONLY); + int current_fd = openat(base_dir_fd, "current_now", O_RDONLY); + int time_to_empty_fd = openat(base_dir_fd, "time_to_empty_now", O_RDONLY); + long capacity = readint_from_fd(capacity_fd); long energy = energy_fd >= 0 ? readint_from_fd(energy_fd) : -1; long power = power_fd >= 0 ? readint_from_fd(power_fd) : -1; @@ -289,6 +307,22 @@ update_status(struct module *mod, int capacity_fd, int energy_fd, int power_fd, long time_to_empty = time_to_empty_fd >= 0 ? readint_from_fd(time_to_empty_fd) : -1; const char *status = readline_from_fd(status_fd); + + if (status_fd >= 0) + close(status_fd); + if (capacity_fd >= 0) + close(capacity_fd); + if (energy_fd >= 0) + close(energy_fd); + if (power_fd >= 0) + close(power_fd); + if (charge_fd >= 0) + close(charge_fd); + if (current_fd >= 0) + close(current_fd); + if (time_to_empty_fd >= 0) + close(time_to_empty_fd); + enum state state; if (status == NULL) { @@ -322,6 +356,7 @@ update_status(struct module *mod, int capacity_fd, int energy_fd, int power_fd, m->current = current; m->time_to_empty = time_to_empty; mtx_unlock(&mod->lock); + } static int @@ -343,26 +378,17 @@ run(struct module *mod) : 0.0)); int ret = 1; - int status_fd = openat(base_dir_fd, "status", O_RDONLY); - int capacity_fd = openat(base_dir_fd, "capacity", O_RDONLY); - int energy_fd = openat(base_dir_fd, "energy_now", O_RDONLY); - int power_fd = openat(base_dir_fd, "power_now", O_RDONLY); - int charge_fd = openat(base_dir_fd, "charge_now", O_RDONLY); - int current_fd = openat(base_dir_fd, "current_now", O_RDONLY); - int time_to_empty_fd = openat(base_dir_fd, "time_to_empty_now", O_RDONLY); struct udev *udev = udev_new(); struct udev_monitor *mon = udev_monitor_new_from_netlink(udev, "udev"); - if (status_fd < 0 || capacity_fd < 0 || udev == NULL || mon == NULL) + if (udev == NULL || mon == NULL) goto out; udev_monitor_filter_add_match_subsystem_devtype(mon, "power_supply", NULL); udev_monitor_enable_receiving(mon); - update_status( - mod, capacity_fd, energy_fd, power_fd, - charge_fd, current_fd, status_fd, time_to_empty_fd); + update_status(mod, base_dir_fd); bar->refresh(bar); while (true) { @@ -388,9 +414,7 @@ run(struct module *mod) continue; } - update_status( - mod, capacity_fd, energy_fd, power_fd, - charge_fd, current_fd, status_fd, time_to_empty_fd); + update_status(mod, base_dir_fd); bar->refresh(bar); } @@ -400,17 +424,6 @@ out: if (udev != NULL) udev_unref(udev); - if (power_fd != -1) - close(power_fd); - if (energy_fd != -1) - close(energy_fd); - if (capacity_fd != -1) - close(capacity_fd); - if (status_fd != -1) - close(status_fd); - if (time_to_empty_fd != -1) - close(time_to_empty_fd); - close(base_dir_fd); return ret; } From 524f93c997a6f733357c22c51893ddb5d8203f2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 4 Jan 2021 19:59:58 +0100 Subject: [PATCH 118/611] module/battery: re-open /sys/class/power_supply// every update --- modules/battery.c | 61 +++++++++++++++++++++++++++++++---------------- 1 file changed, 40 insertions(+), 21 deletions(-) diff --git a/modules/battery.c b/modules/battery.c index b4da871..7dcfc45 100644 --- a/modules/battery.c +++ b/modules/battery.c @@ -171,21 +171,21 @@ readint_from_fd(int fd) return ret; } -static int +static bool initialize(struct private *m) { int pw_fd = open("/sys/class/power_supply", O_RDONLY); - if (pw_fd == -1) { + if (pw_fd < 0) { LOG_ERRNO("/sys/class/power_supply"); - return -1; + return false; } int base_dir_fd = openat(pw_fd, m->battery, O_RDONLY); close(pw_fd); - if (base_dir_fd == -1) { - LOG_ERRNO("%s", m->battery); - return -1; + if (base_dir_fd < 0) { + LOG_ERRNO("/sys/class/power_supply/%s", m->battery); + return false; } { @@ -268,29 +268,46 @@ initialize(struct private *m) m->charge_full = m->charge_full_design = -1; } - return base_dir_fd; + close(base_dir_fd); + return true; err: close(base_dir_fd); - return -1; + return false; } -static void -update_status(struct module *mod, int base_dir_fd) +static bool +update_status(struct module *mod) { struct private *m = mod->private; + int pw_fd = open("/sys/class/power_supply", O_RDONLY); + if (pw_fd < 0) { + LOG_ERRNO("/sys/class/power_supply"); + return false; + } + + int base_dir_fd = openat(pw_fd, m->battery, O_RDONLY); + close(pw_fd); + + if (base_dir_fd < 0) { + LOG_ERRNO("/sys/class/power_supply/%s", m->battery); + return false; + } + int status_fd = openat(base_dir_fd, "status", O_RDONLY); if (status_fd < 0) { - LOG_ERRNO("status: failed to open"); - return; + LOG_ERRNO("/sys/class/power_supply/%s/status", m->battery); + close(base_dir_fd); + return false; } int capacity_fd = openat(base_dir_fd, "capacity", O_RDONLY); if (capacity_fd < 0) { - LOG_ERRNO("capacity: failed to open"); + LOG_ERRNO("/sys/class/power_supply/%s/capacity", m->battery); close(status_fd); - return; + close(base_dir_fd); + return false; } int energy_fd = openat(base_dir_fd, "energy_now", O_RDONLY); @@ -322,6 +339,8 @@ update_status(struct module *mod, int base_dir_fd) close(current_fd); if (time_to_empty_fd >= 0) close(time_to_empty_fd); + if (base_dir_fd >= 0) + close(base_dir_fd); enum state state; @@ -356,7 +375,7 @@ update_status(struct module *mod, int base_dir_fd) m->current = current; m->time_to_empty = time_to_empty; mtx_unlock(&mod->lock); - + return true; } static int @@ -365,8 +384,7 @@ run(struct module *mod) const struct bar *bar = mod->bar; struct private *m = mod->private; - int base_dir_fd = initialize(m); - if (base_dir_fd == -1) + if (!initialize(m)) return -1; LOG_INFO("%s: %s %s (at %.1f%% of original capacity)", @@ -388,7 +406,9 @@ run(struct module *mod) udev_monitor_filter_add_match_subsystem_devtype(mon, "power_supply", NULL); udev_monitor_enable_receiving(mon); - update_status(mod, base_dir_fd); + if (!update_status(mod)) + goto out; + bar->refresh(bar); while (true) { @@ -414,7 +434,8 @@ run(struct module *mod) continue; } - update_status(mod, base_dir_fd); + if (!update_status(mod)) + break; bar->refresh(bar); } @@ -423,8 +444,6 @@ out: udev_monitor_unref(mon); if (udev != NULL) udev_unref(udev); - - close(base_dir_fd); return ret; } From 90339a00a5f40a9070803197bfe4cdb4bf96ee6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 14 Jan 2021 11:09:50 +0100 Subject: [PATCH 119/611] changelog: battery stats no longer getting stuck --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a88dd3..7175cd3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,8 @@ * mpd: `elapsed` tag not working (regression, introduced in 1.6.0). * Wrong background color for (semi-) transparent backgrounds. +* battery: stats sometimes getting stuck at 0, or impossibly large + values (https://codeberg.org/dnkl/yambar/issues/25). ### Security From 686e57176efb0cec9ccc2510870d3edaf7b4ef47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 14 Jan 2021 11:12:56 +0100 Subject: [PATCH 120/611] changelog: prepare for 1.6.1 --- CHANGELOG.md | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7175cd3..c7d7c2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,13 +1,12 @@ # Changelog -* [Unreleased](#Unreleased) +* [1.6.1](#1-6-1) * [1.6.0](#1-6-0) * [1.5.0](#1-5-0) -## Unreleased +## 1.6.1 -### Added ### Changed * i3: workspaces with numerical names are sorted separately from @@ -15,8 +14,6 @@ (https://codeberg.org/dnkl/yambar/issues/30). -### Deprecated -### Removed ### Fixed * mpd: `elapsed` tag not working (regression, introduced in 1.6.0). @@ -25,9 +22,6 @@ values (https://codeberg.org/dnkl/yambar/issues/25). -### Security - - ## 1.6.0 ### Added From 2563a233d787422750660c6ba36f807e35e12d60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 14 Jan 2021 11:13:16 +0100 Subject: [PATCH 121/611] meson/PKGBUILD: bump to 1.6.1 --- PKGBUILD | 2 +- PKGBUILD.wayland-only | 2 +- meson.build | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/PKGBUILD b/PKGBUILD index 0b9b02c..12e6642 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -1,5 +1,5 @@ pkgname=yambar -pkgver=1.6.0 +pkgver=1.6.1 pkgrel=1 pkgdesc="Simplistic and highly configurable status panel for X and Wayland" arch=('x86_64' 'aarch64') diff --git a/PKGBUILD.wayland-only b/PKGBUILD.wayland-only index 9da0d18..10a44ea 100644 --- a/PKGBUILD.wayland-only +++ b/PKGBUILD.wayland-only @@ -1,5 +1,5 @@ pkgname=yambar-wayland -pkgver=1.6.0 +pkgver=1.6.1 pkgrel=1 pkgdesc="Simplistic and highly configurable status panel for Wayland" arch=('x86_64' 'aarch64') diff --git a/meson.build b/meson.build index dd6b910..9309064 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('yambar', 'c', - version: '1.6.0', + version: '1.6.1', license: 'MIT', meson_version: '>=0.53.0', default_options: ['c_std=c18', From 0bc0012c06d36b154b6eadf000612f579503a6f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 14 Jan 2021 11:20:02 +0100 Subject: [PATCH 122/611] =?UTF-8?q?changelog:=20add=20a=20new=20=E2=80=98u?= =?UTF-8?q?nreleased=E2=80=99=20section?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c7d7c2d..94cdf62 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,22 @@ # Changelog +* [Unreleased](#Unreleased) * [1.6.1](#1-6-1) * [1.6.0](#1-6-0) * [1.5.0](#1-5-0) +## Unreleased + +### Added +### Changed +### Deprecated +### Removed +### Fixed +### Security +### Contributors + + ## 1.6.1 ### Changed From 264c05123219755892e71c480a238eaaa64366b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 10 Feb 2021 16:15:49 +0100 Subject: [PATCH 123/611] module/script: fix typo in memcmp() Patch by Jan Beich --- modules/script.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/script.c b/modules/script.c index 766ea96..7e07365 100644 --- a/modules/script.c +++ b/modules/script.c @@ -148,7 +148,7 @@ process_line(struct module *mod, const char *line, size_t len) } else if ((type_len > 6 && memcmp(type, "range:", 6) == 0) || - (type_len > 9 && memcmp(type, "realtime:", 9 == 0))) + (type_len > 9 && memcmp(type, "realtime:", 9) == 0)) { const char *_start = type + 6; const char *split = memchr(_start, '-', type_len - 6); From fe252a1410f226718872cb54a1919850c1450c70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 10 Feb 2021 16:15:49 +0100 Subject: [PATCH 124/611] module/script: fix typo in memcmp() Patch by Jan Beich --- modules/script.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/script.c b/modules/script.c index 766ea96..7e07365 100644 --- a/modules/script.c +++ b/modules/script.c @@ -148,7 +148,7 @@ process_line(struct module *mod, const char *line, size_t len) } else if ((type_len > 6 && memcmp(type, "range:", 6) == 0) || - (type_len > 9 && memcmp(type, "realtime:", 9 == 0))) + (type_len > 9 && memcmp(type, "realtime:", 9) == 0)) { const char *_start = type + 6; const char *split = memchr(_start, '-', type_len - 6); From 98a4789e26a9162b3764ad2949c0fb8e706cbdba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 11 Feb 2021 19:02:14 +0100 Subject: [PATCH 125/611] =?UTF-8?q?yml:=20don=E2=80=99t=20crash=20when=20(?= =?UTF-8?q?trying=20to)=20merge=20anchors=20that=20aren=E2=80=99t=20dictio?= =?UTF-8?q?naries?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Up until now, we only asserted the value being merged in was a dictionary. Now we do a proper check and return a real error message instead. Closes #32 --- CHANGELOG.md | 5 +++++ yml.c | 48 +++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 44 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 94cdf62..e5a1acc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,11 @@ ### Deprecated ### Removed ### Fixed + +* Crash when merging non-dictionary anchors in the YAML configuration + (https://codeberg.org/dnkl/yambar/issues/32). + + ### Security ### Contributors diff --git a/yml.c b/yml.c index bdf474a..52370ef 100644 --- a/yml.c +++ b/yml.c @@ -177,13 +177,14 @@ add_anchor(struct yml_node *root, const char *anchor, root->root.anchor_count++; } -static void -post_process(struct yml_node *node) +static bool +post_process(struct yml_node *node, char **error) { switch (node->type) { case ROOT: if (node->root.root != NULL) - post_process(node->root.root); + if (!post_process(node->root.root, error)) + return false; break; case SCALAR: @@ -192,13 +193,17 @@ post_process(struct yml_node *node) case LIST: tll_foreach(node->list.values, it) - post_process(it->item); + if (!post_process(it->item, error)) + return false; break; case DICT: tll_foreach(node->dict.pairs, it) { - post_process(it->item.key); - post_process(it->item.value); + if (!post_process(it->item.key, error) || + !post_process(it->item.value, error)) + { + return false; + } } tll_foreach(node->dict.pairs, it) { @@ -214,7 +219,17 @@ post_process(struct yml_node *node) * e.g. <<: [*foo, *bar] */ tll_foreach(it->item.value->list.values, v_it) { - assert(v_it->item->type == DICT); + if (v_it->item->type != DICT) { + int cnt = snprintf( + NULL, 0, "%zu:%zu: cannot merge non-dictionary anchor", + v_it->item->line, v_it->item->column); + *error = malloc(cnt + 1); + snprintf( + *error, cnt + 1, "%zu:%zu: cannot merge non-dictionary anchor", + v_it->item->line, v_it->item->column); + return false; + } + tll_foreach(v_it->item->dict.pairs, vv_it) { struct dict_pair p = { .key = vv_it->item.key, @@ -240,7 +255,17 @@ post_process(struct yml_node *node) * Merge value is a dictionary only * e.g. <<: *foo */ - assert(it->item.value->type == DICT); + if (it->item.value->type != DICT) { + int cnt = snprintf( + NULL, 0, "%zu:%zu: cannot merge non-dictionary anchor", + it->item.value->line, it->item.value->column); + *error = malloc(cnt + 1); + snprintf( + *error, cnt + 1, "%zu:%zu: cannot merge non-dictionary anchor", + it->item.value->line, it->item.value->column); + return false; + } + tll_foreach(it->item.value->dict.pairs, v_it) { struct dict_pair p = { .key = v_it->item.key, @@ -269,6 +294,8 @@ post_process(struct yml_node *node) } break; } + + return true; } static const char * @@ -526,7 +553,10 @@ yml_load(FILE *yml, char **error) yaml_parser_delete(&yaml); - post_process(root); + if (!post_process(root, error)) { + yml_destroy(root); + return NULL; + } return root; err: From afe22813f32a945b0df741fe5538d26e92bc343a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 14 Jan 2021 11:20:02 +0100 Subject: [PATCH 126/611] =?UTF-8?q?changelog:=20add=20a=20new=20=E2=80=98u?= =?UTF-8?q?nreleased=E2=80=99=20section?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c7d7c2d..94cdf62 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,22 @@ # Changelog +* [Unreleased](#Unreleased) * [1.6.1](#1-6-1) * [1.6.0](#1-6-0) * [1.5.0](#1-5-0) +## Unreleased + +### Added +### Changed +### Deprecated +### Removed +### Fixed +### Security +### Contributors + + ## 1.6.1 ### Changed From 0855e5ff6371d6954c8d6b0dbcf18ea0252e24b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 11 Feb 2021 19:02:14 +0100 Subject: [PATCH 127/611] =?UTF-8?q?yml:=20don=E2=80=99t=20crash=20when=20(?= =?UTF-8?q?trying=20to)=20merge=20anchors=20that=20aren=E2=80=99t=20dictio?= =?UTF-8?q?naries?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Up until now, we only asserted the value being merged in was a dictionary. Now we do a proper check and return a real error message instead. Closes #32 --- CHANGELOG.md | 5 +++++ yml.c | 48 +++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 44 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 94cdf62..e5a1acc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,11 @@ ### Deprecated ### Removed ### Fixed + +* Crash when merging non-dictionary anchors in the YAML configuration + (https://codeberg.org/dnkl/yambar/issues/32). + + ### Security ### Contributors diff --git a/yml.c b/yml.c index bdf474a..52370ef 100644 --- a/yml.c +++ b/yml.c @@ -177,13 +177,14 @@ add_anchor(struct yml_node *root, const char *anchor, root->root.anchor_count++; } -static void -post_process(struct yml_node *node) +static bool +post_process(struct yml_node *node, char **error) { switch (node->type) { case ROOT: if (node->root.root != NULL) - post_process(node->root.root); + if (!post_process(node->root.root, error)) + return false; break; case SCALAR: @@ -192,13 +193,17 @@ post_process(struct yml_node *node) case LIST: tll_foreach(node->list.values, it) - post_process(it->item); + if (!post_process(it->item, error)) + return false; break; case DICT: tll_foreach(node->dict.pairs, it) { - post_process(it->item.key); - post_process(it->item.value); + if (!post_process(it->item.key, error) || + !post_process(it->item.value, error)) + { + return false; + } } tll_foreach(node->dict.pairs, it) { @@ -214,7 +219,17 @@ post_process(struct yml_node *node) * e.g. <<: [*foo, *bar] */ tll_foreach(it->item.value->list.values, v_it) { - assert(v_it->item->type == DICT); + if (v_it->item->type != DICT) { + int cnt = snprintf( + NULL, 0, "%zu:%zu: cannot merge non-dictionary anchor", + v_it->item->line, v_it->item->column); + *error = malloc(cnt + 1); + snprintf( + *error, cnt + 1, "%zu:%zu: cannot merge non-dictionary anchor", + v_it->item->line, v_it->item->column); + return false; + } + tll_foreach(v_it->item->dict.pairs, vv_it) { struct dict_pair p = { .key = vv_it->item.key, @@ -240,7 +255,17 @@ post_process(struct yml_node *node) * Merge value is a dictionary only * e.g. <<: *foo */ - assert(it->item.value->type == DICT); + if (it->item.value->type != DICT) { + int cnt = snprintf( + NULL, 0, "%zu:%zu: cannot merge non-dictionary anchor", + it->item.value->line, it->item.value->column); + *error = malloc(cnt + 1); + snprintf( + *error, cnt + 1, "%zu:%zu: cannot merge non-dictionary anchor", + it->item.value->line, it->item.value->column); + return false; + } + tll_foreach(it->item.value->dict.pairs, v_it) { struct dict_pair p = { .key = v_it->item.key, @@ -269,6 +294,8 @@ post_process(struct yml_node *node) } break; } + + return true; } static const char * @@ -526,7 +553,10 @@ yml_load(FILE *yml, char **error) yaml_parser_delete(&yaml); - post_process(root); + if (!post_process(root, error)) { + yml_destroy(root); + return NULL; + } return root; err: From 153d7a2ffa67ff5dd85b754f65e7cb842143094c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 21 Feb 2021 20:27:29 +0100 Subject: [PATCH 128/611] doc: yambar-modules: script: stress the importance of an empty line after a transaction Closes #34 --- doc/yambar-modules.5.scd | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/doc/yambar-modules.5.scd b/doc/yambar-modules.5.scd index 14aed8f..aae3268 100644 --- a/doc/yambar-modules.5.scd +++ b/doc/yambar-modules.5.scd @@ -738,8 +738,11 @@ terminated. Or, the script can continue executing and update yambar with new tag sets, either periodically, or when there is new data to feed to yambar. -Tag sets, or _transactions_, are separated by an empty line. Each -_tag_ is a single line on the format: +Tag sets, or _transactions_, are separated by an empty line +(e.g. *echo ""*). The empty line is required to commit (update) the +tag even for only one transaction. + +Each _tag_ is a single line on the format: ``` name|type|value @@ -754,14 +757,16 @@ Example: ``` var1|string|hello var2|int|13 - + var1|string|world var2|int|37 + ``` The example above consists of two transactions. Each transaction has two tags: one string tag and one integer tag. The second transaction -replaces the tags from the first transaction. +replaces the tags from the first transaction. Note that **both** +transactions need to be terminated with an empty line. Supported _types_ are: From db6e868011cc78e3e1c774c658666e069822a300 Mon Sep 17 00:00:00 2001 From: novakne Date: Mon, 22 Feb 2021 11:04:02 +0100 Subject: [PATCH 129/611] exemples/scripts: dwl-tags.sh: display info about dwl tags --- examples/scripts/dwl-tags.sh | 111 +++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100755 examples/scripts/dwl-tags.sh diff --git a/examples/scripts/dwl-tags.sh b/examples/scripts/dwl-tags.sh new file mode 100755 index 0000000..44ade8f --- /dev/null +++ b/examples/scripts/dwl-tags.sh @@ -0,0 +1,111 @@ +#!/usr/bin/env bash +# +# dwl-tags.sh - display dwl tags +# +# USAGE: dwl-tags.sh 1 +# +# REQUIREMENTS: +# - inotifywait ( 'inotify-tools' on arch ) +# - 2021/02/22 - dwl pull request: +# 'Interface to display tag information on status bar #91' +# https://github.com/djpohly/dwl/pull/91 +# +# TAGS: +# Name Type Return +# ------------------------------------- +# {dwltag} string dwl tags name/state +# +# Exemple configuration: +# - script: +# path: /absolute/path/to/dwl-tags.sh +# args: [1] +# content: {string: {text: "{dwltag}"}} + + +# Variables +declare titleline tagline title taginfo isactive ctags mtags layout +declare symbol_occupied_pre symbol_occupied_post symbol_focused_pre symbol_focused_post +declare -a tags name +readonly fname=/tmp/dwltags-"$WAYLAND_DISPLAY" + + +while true; do + + # Make sure the file exists + while [ ! -f "${fname}" ]; do + inotifywait -qqe create "$(dirname "${fname}")" + done; + + # Wait for dwl to close it after writing + inotifywait -qqe close_write "${fname}" + + # Get info from the file + titleline="$1" + tagline=$((titleline+1)) + title=$(sed "${titleline}!d" "${fname}") + taginfo=$(sed "${tagline}!d" "${fname}") + isactive=$(printf -- '%s\n' "${taginfo}" | cut -d ' ' -f 1) + ctags=$(printf -- '%s\n' "${taginfo}" | cut -d ' ' -f 2) + mtags=$(printf -- '%s\n' "${taginfo}" | cut -d ' ' -f 3) + layout=$(printf -- '%s\n' "${taginfo}" | cut -d ' ' -f 4-) + + tags=( "1" "2" "3" "4" "5" "6" "7" "8" "9" ) + + # Name of tag (optional) + # If there is no name, number are used + # + # Example: + # name=( "" "" "" "Media" ) + # -> return "" "" "" "Media" 5 6 7 8 9) + name=() + + # Symbol for occupied tags + # + # Format: "{symbol_occupied_pre}{TAGNAME}{symbol_occupied_post}" + # You can leave one empty if you don't want to surround the TAGNAME + symbol_occupied_pre="" + symbol_occupied_post="." + + # Symbol for the focused tag + # + # Format: "{symbol_focused_pre}{TAGNAME}{symbol_focused_post}" + # You can leave one empty if you don't want to surround the TAGNAME + symbol_focused_pre="[ " + symbol_focused_post=" ]" + + for i in {0..8}; do + mask=$((1< Date: Mon, 22 Feb 2021 11:18:55 +0100 Subject: [PATCH 130/611] exemples/scripts: pacman.sh: display number of pacman/aur updates available --- examples/scripts/pacman.sh | 79 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100755 examples/scripts/pacman.sh diff --git a/examples/scripts/pacman.sh b/examples/scripts/pacman.sh new file mode 100755 index 0000000..739dc23 --- /dev/null +++ b/examples/scripts/pacman.sh @@ -0,0 +1,79 @@ +#!/usr/bin/env bash +# +# pacman.sh - display number of packages update available +# by default check every hour +# +# USAGE: pacman.sh +# +# TAGS: +# Name Type Return +# ------------------------------------------- +# {pacman} string number of pacman packages +# {aur} string number of aur packages +# {pkg} string sum of both +# +# Exemple configuration: +# - script: +# path: /absolute/path/to/pacman.sh +# args: [] +# content: { string: { text: " {pacman} + {aur} = {pkg}" } } + + +declare interval no_update aur_helper pacman_num aur_num pkg_num + +# Error message in STDERR +_err() { + printf -- '%s\n' "[$(date +'%Y-%m-%d %H:%M:%S')]: $*" >&2 +} + + +while true; do + # Change interval + # NUMBER[SUFFIXE] + # Possible suffix: + # "s" seconds / "m" minutes / "h" hours / "d" days + interval="1h" + + # Change the message you want when there is no update + # Leave empty if you want a 0 instead of a string + # (e.g. no_update="") + no_update="no update" + + # Change your aur manager + aur_helper="paru" + + # Get number of packages to update + pacman_num=$(checkupdates | wc -l) + + if ! hash "${aur_helper}" >/dev/null 2>&1; then + _err "aur helper not found, change it in the script" + else + aur_num=$("${aur_helper}" -Qmu | wc -l) + fi + + pkg_num=$(( pacman_num + aur_num )) + + # Only display one if there is no update and multiple tags set + if [[ "${pacman_num}" == 0 && "${aur_num}" == 0 ]]; then + pacman_num="${no_update:-$pacman_num}" + aur_num="${no_update:-$aur_num}" + pkg_num="${no_update:-$pkg_num}" + + printf -- '%s\n' "pacman|string|" + printf -- '%s\n' "aur|string|" + printf -- '%s\n' "pkg|string|${pkg_num}" + printf -- '%s\n' "" + else + printf -- '%s\n' "pacman|string|${pacman_num}" + printf -- '%s\n' "aur|string|${aur_num}" + printf -- '%s\n' "pkg|string|${pkg_num}" + printf -- '%s\n' "" + fi + + sleep "${interval}" + +done + +unset -v interval no_update aur_helper pacman_num aur_num pkg_num +unset -f _err + From faa5f7f9f1061f99e31efc2b10fae8bb41000735 Mon Sep 17 00:00:00 2001 From: novakne Date: Tue, 23 Feb 2021 11:14:08 +0100 Subject: [PATCH 131/611] doc: split up yambar-modules Closes #15 --- doc/meson.build | 7 + doc/yambar-modules-alsa.5.scd | 50 ++ doc/yambar-modules-backlight.5.scd | 46 ++ doc/yambar-modules-battery.5.scd | 65 +++ doc/yambar-modules-clock.5.scd | 46 ++ doc/yambar-modules-i3.5.scd | 103 ++++ doc/yambar-modules-label.5.scd | 31 ++ doc/yambar-modules-mpd.5.scd | 79 +++ doc/yambar-modules-network.5.scd | 66 +++ doc/yambar-modules-removables.5.scd | 88 +++ doc/yambar-modules-river.5.scd | 84 +++ doc/yambar-modules-script.5.scd | 108 ++++ doc/yambar-modules-swayxkb.5.scd | 69 +++ doc/yambar-modules-xkb.5.scd | 51 ++ doc/yambar-modules-xwindow.5.scd | 44 ++ doc/yambar-modules.5.scd | 834 +--------------------------- 16 files changed, 953 insertions(+), 818 deletions(-) create mode 100644 doc/yambar-modules-alsa.5.scd create mode 100644 doc/yambar-modules-backlight.5.scd create mode 100644 doc/yambar-modules-battery.5.scd create mode 100644 doc/yambar-modules-clock.5.scd create mode 100644 doc/yambar-modules-i3.5.scd create mode 100644 doc/yambar-modules-label.5.scd create mode 100644 doc/yambar-modules-mpd.5.scd create mode 100644 doc/yambar-modules-network.5.scd create mode 100644 doc/yambar-modules-removables.5.scd create mode 100644 doc/yambar-modules-river.5.scd create mode 100644 doc/yambar-modules-script.5.scd create mode 100644 doc/yambar-modules-swayxkb.5.scd create mode 100644 doc/yambar-modules-xkb.5.scd create mode 100644 doc/yambar-modules-xwindow.5.scd diff --git a/doc/meson.build b/doc/meson.build index b4dccb3..8da6dca 100644 --- a/doc/meson.build +++ b/doc/meson.build @@ -4,6 +4,13 @@ scdoc = dependency('scdoc', native: true) scdoc_prog = find_program(scdoc.get_pkgconfig_variable('scdoc'), native: true) foreach man_src : ['yambar.1.scd', 'yambar.5.scd', 'yambar-decorations.5.scd', + 'yambar-modules-alsa.5.scd', 'yambar-modules-backlight.5.scd', + 'yambar-modules-battery.5.scd', 'yambar-modules-clock.5.scd', + 'yambar-modules-i3.5.scd', 'yambar-modules-label.5.scd', + 'yambar-modules-mpd.5.scd', 'yambar-modules-network.5.scd', + 'yambar-modules-removables.5.scd', 'yambar-modules-river.5.scd', + 'yambar-modules-script.5.scd', 'yambar-modules-swayxkb.5.scd', + 'yambar-modules-xkb.5.scd', 'yambar-modules-xwindow.5.scd', 'yambar-modules.5.scd', 'yambar-particles.5.scd', 'yambar-tags.5.scd'] parts = man_src.split('.') diff --git a/doc/yambar-modules-alsa.5.scd b/doc/yambar-modules-alsa.5.scd new file mode 100644 index 0000000..d660268 --- /dev/null +++ b/doc/yambar-modules-alsa.5.scd @@ -0,0 +1,50 @@ +yambar-modules-alsa(5) + +# NAME +alsa - Monitors an alsa soundcard for volume and mute/unmute changes + +# TAGS + +[[ *Name* +:[ *Type* +:[ *Description* +| volume +: range +: Volume level, with min and max as start and end range values +| percent +: range +: Volume level, as a percentage +| muted +: bool +: True if muted, otherwise false + + +# CONFIGURATION + +[[ *Name* +:[ *Type* +:[ *Req* +:[ *Description* +| card +: string +: yes +: The soundcard name. _Default_ might work. +| mixer +: string +: yes +: Mixer channel to monitor. _Master_ might work. + +# EXAMPLES + +``` +bar: + left: + - alsa: + card: hw:PCH + mixer: Master + content: {string: {text: "{volume}"}} +``` + +# SEE ALSO + +*yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5) diff --git a/doc/yambar-modules-backlight.5.scd b/doc/yambar-modules-backlight.5.scd new file mode 100644 index 0000000..bc80ef9 --- /dev/null +++ b/doc/yambar-modules-backlight.5.scd @@ -0,0 +1,46 @@ +yambar-modules-backlight(5) + +# NAME +backlight - This module reads monitor backlight status + +# DESCRIPTION +This module reads monitor backlight status from +_/sys/class/backlight_, and uses *udev* to monitor for changes. + +# TAGS + +[[ *Name* +:[ *Type* +:[ *Description* +| brightness +: range +: The current brightness level, in absolute value +| percent +: range +: The current brightness level, in percent + +# CONFIGURATION + +[[ *Name* +:[ *Type* +:[ *Req* +:[ *Description* +| name +: string +: yes +: The backlight device's name (one of the names in */sys/class/backlight*) + +# EXAMPLES + +``` +bar: + left: + - backlight: + name: intel_backlight + content: + string: {text: "backlight: {percent}%"} +``` + +# SEE ALSO + +*yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5) diff --git a/doc/yambar-modules-battery.5.scd b/doc/yambar-modules-battery.5.scd new file mode 100644 index 0000000..2591e60 --- /dev/null +++ b/doc/yambar-modules-battery.5.scd @@ -0,0 +1,65 @@ +yambar-modules-battery(5) + +# NAME +battery - This module reads battery status + +# DESCRIPTION + +This module reads battery status from _/sys/class/power_supply_ and +uses *udev* to monitor for changes. + +# TAGS + +[[ *Name* +:[ *Type* +:[ *Description* +| name +: string +: Battery device name +| manufacturer +: string +: Name of the battery manufacturer +| model +: string +: Battery model name +| state +: string +: One of *full*, *charging*, *discharging* or *unknown* +| capacity +: range +: capacity left, in percent +| estimate +: string +: Estimated time left (to empty while discharging, or to full while + charging), formatted as HH:MM. + +# CONFIGURATION + +[[ *Name* +:[ *Type* +:[ *Req* +:[ *Description* +| name +: string +: yes +: Battery device name (one of the names in */sys/class/power_supply*) +| poll-interval +: int +: no +: How often, in seconds, to poll for capacity changes (default=*60*). Set to `0` to disable polling (*warning*: many batteries do not support asynchronous reporting). + +# EXAMPLES + +``` +bar: + left: + - battery: + name: BAT0 + poll-interval: 30 + content: + string: {text: "BAT: {capacity}% {estimate}"} +``` + +# SEE ALSO + +*yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5) diff --git a/doc/yambar-modules-clock.5.scd b/doc/yambar-modules-clock.5.scd new file mode 100644 index 0000000..34995fa --- /dev/null +++ b/doc/yambar-modules-clock.5.scd @@ -0,0 +1,46 @@ +yambar-modules-clock(5) + +# NAME +clock - This module provides the current date and time + +# TAGS + +[[ *Name* +:[ *Type* +:[ *Description* +| time +: string +: Current time, formatted using the _time-format_ attribute +| date +: string +: Current date, formatted using the _date-format_ attribute + +# CONFIGURATION + +[[ *Name* +:[ *Type* +:[ *Req* +:[ *Description* +| time-format +: string +: no +: *strftime* formatter for the _time_ tag (default=*%H:%M*) +| date-format +: string +: no +: *strftime* formatter for the _date_ date (default=*%x*) + +# EXAMPLES + +``` +bar: + left: + - clock: + time-format: "%H:%M %Z" + content: + string: {text: "{date} {time}"} +``` + +# SEE ALSO + +*yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5) diff --git a/doc/yambar-modules-i3.5.scd b/doc/yambar-modules-i3.5.scd new file mode 100644 index 0000000..88b04f4 --- /dev/null +++ b/doc/yambar-modules-i3.5.scd @@ -0,0 +1,103 @@ +yambar-modules-i3(5) + +# NAME +i3 - This module monitors i3 and sway workspaces + +# DESCRIPTION + +Unlike other modules where the _content_ attribute is just a single +*particle*, the i3 module's _content_ is an associative array mapping +i3/sway workspace names to a particle. + +You can add an empty workspace name, *""*, as a catch-all workspace +particle. The *i3* module will fallback to this entry if it cannot +find the workspace name in the _content_ map. + +It also recognizes the special name *current*, which always represents +the currently focused workspace. On Sway, this can be used together +with the _application_ and _title_ tags to replace the X11-only +*xwindow* module. + +# TAGS + +[[ *Name* +:[ *Type* +:[ *Description* +| name +: string +: The workspace name +| visible +: bool +: True if the workspace is currently visible (on any output) +| focused +: bool +: True if the workspace is currently focused +| urgent +: bool +: True if the workspace has the urgent flag set +| state +: string +: One of *urgent*, *focused*, *unfocused* or *invisible* (note: + *unfocused* is when it is visible, but neither focused nor urgent). +| application +: string +: Name of application currently focused on this workspace (Sway only - use the *xwindow* module in i3) +| title +: string +: This workspace's focused window's title +| mode +: string +: The name of the current mode + +# CONFIGURATION + +[[ *Name* +:[ *Type* +:[ *Req* +:[ *Description* +| content +: associative array +: yes +: Unlike other modules, _content_ is an associative array mapping + workspace names to particles. Use *""* to specify a default + fallback particle, or *current* for the currently active workspace. +| sort +: enum +: no +: How to sort the list of workspaces; one of _none_, _ascending_ or _descending_, defaults to _none_. +| left-spacing +: int +: no +: Space, in pixels, on the left-side of each rendered workspace particle +| right-spacing +: int +: no +: Space, in pixels, on the right-side of each rendered workspace particle +| spacing +: int +: no +: Short-hand for setting both _left-spacing_ and _right-spacing_ + +# EXAMPLES + +This renders all workspace names, with an *\** indicating the +currently focused one. It also renders the currently focused +application name and window title. + +``` +bar: + left: + - i3: + content: + "": + map: + tag: state + default: {string: {text: "{name}"}} + values: + focused: {string: {text: "{name}*"}} + current: { string: {text: "{application}: {title}"}} +``` + +# SEE ALSO + +*yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5) diff --git a/doc/yambar-modules-label.5.scd b/doc/yambar-modules-label.5.scd new file mode 100644 index 0000000..99b85d1 --- /dev/null +++ b/doc/yambar-modules-label.5.scd @@ -0,0 +1,31 @@ +yambar-modules-label(5) + +# NAME +label - This module renders the provided _content_ particle + +# DESCRIPTION + +This module renders the provided _content_ particle, but provides no +additional data. + +# TAGS + +None + +# CONFIGURATION + +No additional attributes supported, only the generic ones (see +*GENERIC CONFIGURATION* in *yambar-modules*(5)) + +# EXAMPLES + +``` +bar: + left: + - label: + content: {string: {text: hello world}} +``` + +# SEE ALSO + +*yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5) diff --git a/doc/yambar-modules-mpd.5.scd b/doc/yambar-modules-mpd.5.scd new file mode 100644 index 0000000..f67fc3c --- /dev/null +++ b/doc/yambar-modules-mpd.5.scd @@ -0,0 +1,79 @@ +yambar-modules-mpd(5) + +# NAME +mpd - This module provides MPD status such as currently playing artist/album/song + +# TAGS + +[[ *Name* +:[ *Type* +:[ *Description* +| state +: string +: One of *offline*, *stopped*, *paused* or *playing* +| repeat +: bool +: True if the *repeat* flag is set +| random +: bool +: True if the *random* flag is set +| consume +: bool +: True if the *consume* flag is set +| volume +: range +: Volume of MPD in percentage +| album +: string +: Currently playing album (also valid in *paused* state) +| artist +: string +: Artist of currently playing song (also valid in *paused* state) +| title +: string +: Title of currently playing song (also valid in *paused* state) +| pos +: string +: *%M:%S*-formatted string describing the song's current position + (also see _elapsed_) +| end +: string +: *%M:%S*-formatted string describing the song's total length (also + see _duration_) +| elapsed +: realtime +: Position in currently playing song, in milliseconds. Can be used + with a _progress-bar_ particle. +| duration +: int +: Length of currently playing song, in milliseconds + +# CONFIGURATION + +[[ *Name* +:[ *Type* +:[ *Req* +:[ *Description* +| host +: string +: yes +: Hostname/IP/unix-socket to connect to +| port +: int +: no +: TCP port to connect to + +# EXAMPLES + +``` +bar: + left: + - mpd: + host: /run/mpd/socket + content: + string: {text: "{artist} - {album} - {title} ({end})"} +``` + +# SEE ALSO + +*yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5) diff --git a/doc/yambar-modules-network.5.scd b/doc/yambar-modules-network.5.scd new file mode 100644 index 0000000..1a9a280 --- /dev/null +++ b/doc/yambar-modules-network.5.scd @@ -0,0 +1,66 @@ +yambar-modules-network(5) + +# NAME +network - This module monitors network connection state + +# DESCRIPTION + +This module monitors network connection state; disconnected/connected +state and MAC/IP addresses. + +Note: while the module internally tracks all assigned IPv4/IPv6 +addresses, it currently exposes only a single IPv4 and a single IPv6 +address. + +# TAGS + +[[ *Name* +:[ *Type* +:[ *Description* +| name +: string +: Network interface name +| index +: int +: Network interface index +| carrier +: bool +: True if the interface has CARRIER. That is, if it is physically connected. +| state +: string +: One of *unknown*, *not present*, *down*, *lower layers down*, + *testing*, *dormant* or *up*. You are probably interested in *down* and *up*. +| mac +: string +: MAC address +| ipv4 +: string +: IPv4 address assigned to the interface, or *""* if none +| ipv6 +: string +: IPv6 address assigned to the interface, or *""* if none + +# CONFIGURATION + +[[ *Name* +:[ *Type* +:[ *Req* +:[ *Description* +| name +: string +: Name of network interface to monitor + +# EXAMPLES + +``` +bar: + left: + - network: + name: wlp3s0 + content: + string: {text: "{name}: {state} ({ipv4})"} +``` + +# SEE ALSO + +*yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5) diff --git a/doc/yambar-modules-removables.5.scd b/doc/yambar-modules-removables.5.scd new file mode 100644 index 0000000..4628109 --- /dev/null +++ b/doc/yambar-modules-removables.5.scd @@ -0,0 +1,88 @@ +yambar-modules-removables(5) + +# NAME +removables - This module detects removable drives + +# DESCRIPTION + +This module detects removable drives (USB sticks, CD-ROMs) and +instantiates the provided _content_ particle for each detected drive. + +# TAGS + +[[ *Name* +:[ *Type* +:[ *Description* +| vendor +: string +: Name of the drive vendor +| model +: string +: Drive model name +| optical +: bool +: True if the drive is an optical drive (CD-ROM, DVD-ROM etc) +| device +: string +: Volume device name (typically */dev/sd?*) +| size +: range +: The volume's size, in bytes. The tag's maximum value is set to the + underlying block device's size +| label +: string +: The volume's label, or its size if it has no label +| mounted +: bool +: True if the volume is mounted +| mount_point +: string +: Path where the volume is mounted, or *""* if it is not mounted + +# CONFIGURATION + +[[ *Name* +:[ *Type* +:[ *Req* +:[ *Description* +| left-spacing +: int +: no +: Space, in pixels, in the left side of each rendered volume +| right-spacing +: int +: no +: Space, in pixels, on the right side of each rendered volume +| spacing +: int +: no +: Short-hand for setting both _left-spacing_ and _right-spacing_ +| ignore +: list of strings +: no +: List of device paths that should be ignored (e.g. /dev/mmcblk0, or /dev/mmcblk0p1) + +# EXAMPLES + +``` +bar: + right: + - removables: + content: + map: + tag: mounted + values: + false: + string: + on-click: udisksctl mount -b {device} + text: "{label}" + true: + string: + on-click: udisksctl unmount -b {device} + text: "{label}" + deco: {underline: {size: 2, color: ffffffff}} +``` + +# SEE ALSO + +*yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5) diff --git a/doc/yambar-modules-river.5.scd b/doc/yambar-modules-river.5.scd new file mode 100644 index 0000000..448159c --- /dev/null +++ b/doc/yambar-modules-river.5.scd @@ -0,0 +1,84 @@ +yambar-modules-river(5) + +# NAME +river - This module provide information about the river tags + +# DESCRIPTION + +This module uses river's (https://github.com/ifreund/river, a dynamic +tiling Wayland compositor) status protocol to provide information +about the river tags. + +It has an interface similar to the i3/sway module. + +The configuration for the river module specifies one _title_ particle, +which will be instantiated with tags representing the currently active +seat and the currently focused view's title. + +It also specifies a _content_ template particle, which is instantiated +once for all 32 river tags. This means you probably want to use a +*map* particle to hide unused river tags. + +# TAGS + +[[ *Name* +:[ *Type* +:[ *Description* +| id +: int +: River tag number +| visible +: bool +: True if the river tag is focused by at least one output (i.e. visible on at least one monitor). +| focused +: bool +: True if the river tag is _visible_ and has keyboard focus. +| occupied +: bool +: True if the river tag has views (i.e. windows). +| state +: string +: Set to *focused* if _focused_ is true, *unfocused* if _visible_ is true, but _focused_ is false, or *invisible* if the river tag is not visible on any monitors. +| seat +: string +: The name of the currently active seat (*title* particle only, see CONFIGURATION) +| title +: string +: The focused view's title (*title* particle only, see CONFIGURATION) + +# CONFIGURATION + +[[ *Name* +:[ *Type* +:[ *Req* +:[ *Description* +| title +: particle +: no +: Particle that will be instantiated with the _seat_ and _title_ tags. +| content +: particle +: yes +: Template particle that will be instantiated once for all of the 32 river tags. + +# EXAMPLES + +``` +bar: + left: + - river: + title: {string: { text: "{seat} - {title}" }} + content: + map: + tag: occupied + values: + false: {empty: {}} + true: + string: + margin: 5 + text: "{id}: {state}" +``` + +# SEE ALSO + +*yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5) diff --git a/doc/yambar-modules-script.5.scd b/doc/yambar-modules-script.5.scd new file mode 100644 index 0000000..849b60a --- /dev/null +++ b/doc/yambar-modules-script.5.scd @@ -0,0 +1,108 @@ +yambar-modules-script(5) + +# NAME +script - This module executes a user-provided script (or binary!) + +# DESCRIPTION + +This module executes a user-provided script (or binary!) that writes +tags on its stdout. + +The script can either exit immediately after writing a set of tags, in +which case yambar will display those tags until yambar is +terminated. Or, the script can continue executing and update yambar +with new tag sets, either periodically, or when there is new data to +feed to yambar. + +Tag sets, or _transactions_, are separated by an empty line +(e.g. *echo ""*). The empty line is required to commit (update) the +tag even for only one transaction. + +Each _tag_ is a single line on the format: + +``` +name|type|value +``` + +Where _name_ is what you also use to refer to the tag in the yambar +configuration, _type_ is one of the tag types defined in +*yambar-tags*(5), and _value_ is the tag’s value. + +Example: + +``` +var1|string|hello +var2|int|13 + +var1|string|world +var2|int|37 + +``` + +The example above consists of two transactions. Each transaction has +two tags: one string tag and one integer tag. The second transaction +replaces the tags from the first transaction. Note that **both** +transactions need to be terminated with an empty line. + +Supported _types_ are: + +- string +- int +- bool +- float +- range:n-m (e.g. *var|range:0-100|57*) + +# TAGS + +User defined. + +# CONFIGURATION + +[[ *Name* +:[ *Type* +:[ *Req* +:[ *Description* +| path +: string +: yes +: Path to script/binary to execute. Must be an absolute path. +| args +: list of strings +: no +: Arguments to pass to the script/binary. + +# EXAMPLES + +Here is an "hello world" example script: + +``` +#!/bin/sh + +while true; do + echo "test|string|hello" + echo "" + sleep 3 + + echo "test|string|world" + echo "" + sleep 3 +done +``` + +This script will emit a single string tag, _test_, and alternate its +value between *hello* and *world* every three seconds. + +A corresponding yambar configuration could look like this: + +``` +bar: + left: + - script: + path: /path/to/script.sh + args: [] + content: {string: {text: "{test}"}} +``` + +# SEE ALSO + +*yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5) diff --git a/doc/yambar-modules-swayxkb.5.scd b/doc/yambar-modules-swayxkb.5.scd new file mode 100644 index 0000000..fffe8b7 --- /dev/null +++ b/doc/yambar-modules-swayxkb.5.scd @@ -0,0 +1,69 @@ +yambar-modules-swayxkb(5) + +# NAME +sway-xkb - This module monitor input devices' active XKB layout + +# DESCRIPTION + +This module uses *Sway* extensions to the I3 IPC API to monitor input +devices' active XKB layout. As such, it requires Sway to be running. + +*Note* that the _content_ configuration option is a *template*; +*sway-xkb* will instantiate a particle list, where each item is +instantiated from this template, and represents an input device. + +# TAGS + +[[ *Name* +:[ *Type* +:[ *Description* +| id +: string +: Input device identifier +| layout +: string +: The input device's currently active XKB layout + +# CONFIGURATION + +[[ *Name* +:[ *Type* +:[ *Req* +:[ *Description* +| identifiers +: list of strings +: yes +: Identifiers of input devices to monitor. Use _swaymsg -t get_inputs_ to see available devices. +| content +: particle +: yes +: A particle template; each existing input device will be instantiated with this template. +| left-spacing +: int +: no +: Space, in pixels, in the left side of each rendered input device +| right-spacing +: int +: no +: Space, in pixels, on the right side of each rendered input device +| spacing +: int +: no +: Short-hand for setting both _left-spacing_ and _right-spacing_ + +# EXAMPLES + +``` +bar: + left: + - sway-xkb: + identifiers: + - 1523:7:HID_05f3:0007 + - 7247:2:USB_USB_Keykoard + spacing: 5 + content: {string: {text: "{id}: {layout}"}} +``` + +# SEE ALSO + +*yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5) diff --git a/doc/yambar-modules-xkb.5.scd b/doc/yambar-modules-xkb.5.scd new file mode 100644 index 0000000..ac6d121 --- /dev/null +++ b/doc/yambar-modules-xkb.5.scd @@ -0,0 +1,51 @@ +yambar-modules-xkb(5) + +# NAME +xkb - This module monitors the currently active XKB keyboard layout + +# DESCRIPTION + +This module monitors the currently active XKB keyboard layout and +lock-key states. + +Note: this module is X11 only. It does not work in Wayland. + +# TAGS + +[[ *Name* +:[ *Type* +:[ *Description* +| name +: string +: Name of currently selected layout, long version (e.g. "English (US)") +| symbol +: string +: Name of currently selected layout, short version (e.g. "us") +| caps_lock +: bool +: True if *CapsLock* is enabled +| num_lock +: bool +: True if *NumLock* is enabled +| scroll_lock +: bool +: True if *ScrollLock* is enabled + +# CONFIGURATION + +No additional attributes supported, only the generic ones (see +*GENERIC CONFIGURATION* in *yambar-modules*(5)) + +# EXAMPLES + +``` +bar: + left: + - xkb: + content: + string: {text: "{symbol}"} +``` + +# SEE ALSO + +*yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5) diff --git a/doc/yambar-modules-xwindow.5.scd b/doc/yambar-modules-xwindow.5.scd new file mode 100644 index 0000000..f8abf30 --- /dev/null +++ b/doc/yambar-modules-xwindow.5.scd @@ -0,0 +1,44 @@ +yambar-modules-xwindow(5) + +# NAME +xwindow - This module provides the application name and window title + +# DESCRIPTION + +This module provides the application name and window title of the +currently focused window. + +Note: this module is X11 only. It does not work in Wayland. If you are +running Sway, take a look at the *i3* module and its _application_ and +_title_ tags. + +# TAGS + +[[ *Name* +:[ *Type* +:[ *Description* +| application +: string +: Name of the application that owns the currently focused window +| title +: string +: The title of the currently focused window + +# CONFIGURATION + +No additional attributes supported, only the generic ones (see +*GENERIC CONFIGURATION* in *yambar-modules*(5)) + +# EXAMPLES + +``` +bar: + left: + - xwindow: + content: + string: {text: "{application}: {title}"} +``` + +# SEE ALSO + +*yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5) diff --git a/doc/yambar-modules.5.scd b/doc/yambar-modules.5.scd index aae3268..e345004 100644 --- a/doc/yambar-modules.5.scd +++ b/doc/yambar-modules.5.scd @@ -133,839 +133,37 @@ following attributes are supported by all modules: : Foreground (text) color of the content particle. This is an inherited attribute. -# ALSA +# BUILT-IN MODULES -Monitors an alsa soundcard for volume and mute/unmute changes. +Available modules have their own pages: -## TAGS +*yambar-modules-alsa*(5) -[[ *Name* -:[ *Type* -:[ *Description* -| volume -: range -: Volume level, with min and max as start and end range values -| percent -: range -: Volume level, as a percentage -| muted -: bool -: True if muted, otherwise false +*yambar-modules-backlight*(5) +*yambar-modules-battery*(5) -## CONFIGURATION +*yambar-modules-clock*(5) -[[ *Name* -:[ *Type* -:[ *Req* -:[ *Description* -| card -: string -: yes -: The soundcard name. _Default_ might work. -| mixer -: string -: yes -: Mixer channel to monitor. _Master_ might work. +*yambar-modules-i3*(5) -## EXAMPLES +*yambar-modules-label*(5) -``` -bar: - left: - - alsa: - card: hw:PCH - mixer: Master - content: {string: {text: "{volume}"}} -``` +*yambar-modules-mpd*(5) -# BACKLIGHT +*yambar-modules-network*(5) -This module reads monitor backlight status from -_/sys/class/backlight_, and uses *udev* to monitor for changes. +*yambar-modules-removables*(5) -## TAGS +*yambar-modules-river*(5) -[[ *Name* -:[ *Type* -:[ *Description* -| brightness -: range -: The current brightness level, in absolute value -| percent -: range -: The current brightness level, in percent +*yambar-modules-script*(5) -## CONFIGURATION +*yambar-modules-swayxkb*(5) -[[ *Name* -:[ *Type* -:[ *Req* -:[ *Description* -| name -: string -: yes -: The backlight device's name (one of the names in */sys/class/backlight*) +*yambar-modules-xkb*(5) -## EXAMPLES - -``` -bar: - left: - - backlight: - name: intel_backlight - content: - string: {text: "backlight: {percent}%"} -``` - -# BATTERY - -This module reads battery status from _/sys/class/power_supply_ and -uses *udev* to monitor for changes. - -## TAGS - -[[ *Name* -:[ *Type* -:[ *Description* -| name -: string -: Battery device name -| manufacturer -: string -: Name of the battery manufacturer -| model -: string -: Battery model name -| state -: string -: One of *full*, *charging*, *discharging* or *unknown* -| capacity -: range -: capacity left, in percent -| estimate -: string -: Estimated time left (to empty while discharging, or to full while - charging), formatted as HH:MM. - -## CONFIGURATION - -[[ *Name* -:[ *Type* -:[ *Req* -:[ *Description* -| name -: string -: yes -: Battery device name (one of the names in */sys/class/power_supply*) -| poll-interval -: int -: no -: How often, in seconds, to poll for capacity changes (default=*60*). Set to `0` to disable polling (*warning*: many batteries do not support asynchronous reporting). - -## EXAMPLES - -``` -bar: - left: - - battery: - name: BAT0 - poll-interval: 30 - content: - string: {text: "BAT: {capacity}% {estimate}"} -``` - -# CLOCK - -This module provides the current date and time. - -## TAGS - -[[ *Name* -:[ *Type* -:[ *Description* -| time -: string -: Current time, formatted using the _time-format_ attribute -| date -: string -: Current date, formatted using the _date-format_ attribute - -## CONFIGURATION - -[[ *Name* -:[ *Type* -:[ *Req* -:[ *Description* -| time-format -: string -: no -: *strftime* formatter for the _time_ tag (default=*%H:%M*) -| date-format -: string -: no -: *strftime* formatter for the _date_ date (default=*%x*) - -## EXAMPLES - -``` -bar: - left: - - clock: - time-format: "%H:%M %Z" - content: - string: {text: "{date} {time}"} -``` - -# I3 (and Sway) - -This module monitors i3 and sway workspaces. - -Unlike other modules where the _content_ attribute is just a single -*particle*, the i3 module's _content_ is an associative array mapping -i3/sway workspace names to a particle. - -You can add an empty workspace name, *""*, as a catch-all workspace -particle. The *i3* module will fallback to this entry if it cannot -find the workspace name in the _content_ map. - -It also recognizes the special name *current*, which always represents -the currently focused workspace. On Sway, this can be used together -with the _application_ and _title_ tags to replace the X11-only -*xwindow* module. - -## TAGS - -[[ *Name* -:[ *Type* -:[ *Description* -| name -: string -: The workspace name -| visible -: bool -: True if the workspace is currently visible (on any output) -| focused -: bool -: True if the workspace is currently focused -| urgent -: bool -: True if the workspace has the urgent flag set -| state -: string -: One of *urgent*, *focused*, *unfocused* or *invisible* (note: - *unfocused* is when it is visible, but neither focused nor urgent). -| application -: string -: Name of application currently focused on this workspace (Sway only - use the *xwindow* module in i3) -| title -: string -: This workspace's focused window's title -| mode -: string -: The name of the current mode - -## CONFIGURATION - -[[ *Name* -:[ *Type* -:[ *Req* -:[ *Description* -| content -: associative array -: yes -: Unlike other modules, _content_ is an associative array mapping - workspace names to particles. Use *""* to specify a default - fallback particle, or *current* for the currently active workspace. -| sort -: enum -: no -: How to sort the list of workspaces; one of _none_, _ascending_ or _descending_, defaults to _none_. -| left-spacing -: int -: no -: Space, in pixels, on the left-side of each rendered workspace particle -| right-spacing -: int -: no -: Space, in pixels, on the right-side of each rendered workspace particle -| spacing -: int -: no -: Short-hand for setting both _left-spacing_ and _right-spacing_ - -## EXAMPLES - -This renders all workspace names, with an *\** indicating the -currently focused one. It also renders the currently focused -application name and window title. - -``` -bar: - left: - - i3: - content: - "": - map: - tag: state - default: {string: {text: "{name}"}} - values: - focused: {string: {text: "{name}*"}} - current: { string: {text: "{application}: {title}"}} -``` - -# LABEL - -This module renders the provided _content_ particle, but provides no -additional data. - -## TAGS - -None - -## CONFIGURATION - -No additional attributes supported, only the generic ones (see -*GENERIC CONFIGURATION*) - -## EXAMPLES - -``` -bar: - left: - - label: - content: {string: {text: hello world}} -``` - -# MPD - -This module provides MPD status such as currently playing -artist/album/song. - -## TAGS - -[[ *Name* -:[ *Type* -:[ *Description* -| state -: string -: One of *offline*, *stopped*, *paused* or *playing* -| repeat -: bool -: True if the *repeat* flag is set -| random -: bool -: True if the *random* flag is set -| consume -: bool -: True if the *consume* flag is set -| volume -: range -: Volume of MPD in percentage -| album -: string -: Currently playing album (also valid in *paused* state) -| artist -: string -: Artist of currently playing song (also valid in *paused* state) -| title -: string -: Title of currently playing song (also valid in *paused* state) -| pos -: string -: *%M:%S*-formatted string describing the song's current position - (also see _elapsed_) -| end -: string -: *%M:%S*-formatted string describing the song's total length (also - see _duration_) -| elapsed -: realtime -: Position in currently playing song, in milliseconds. Can be used - with a _progress-bar_ particle. -| duration -: int -: Length of currently playing song, in milliseconds - -## CONFIGURATION - -[[ *Name* -:[ *Type* -:[ *Req* -:[ *Description* -| host -: string -: yes -: Hostname/IP/unix-socket to connect to -| port -: int -: no -: TCP port to connect to - -## EXAMPLES - -``` -bar: - left: - - mpd: - host: /run/mpd/socket - content: - string: {text: "{artist} - {album} - {title} ({end})"} -``` - -# NETWORK - -This module monitors network connection state; disconnected/connected -state and MAC/IP addresses. - -Note: while the module internally tracks all assigned IPv4/IPv6 -addresses, it currently exposes only a single IPv4 and a single IPv6 -address. - -## TAGS - -[[ *Name* -:[ *Type* -:[ *Description* -| name -: string -: Network interface name -| index -: int -: Network interface index -| carrier -: bool -: True if the interface has CARRIER. That is, if it is physically connected. -| state -: string -: One of *unknown*, *not present*, *down*, *lower layers down*, - *testing*, *dormant* or *up*. You are probably interested in *down* and *up*. -| mac -: string -: MAC address -| ipv4 -: string -: IPv4 address assigned to the interface, or *""* if none -| ipv6 -: string -: IPv6 address assigned to the interface, or *""* if none - -## CONFIGURATION - -[[ *Name* -:[ *Type* -:[ *Req* -:[ *Description* -| name -: string -: Name of network interface to monitor - -## EXAMPLES - -``` -bar: - left: - - network: - name: wlp3s0 - content: - string: {text: "{name}: {state} ({ipv4})"} -``` - -# REMOVABLES - -This module detects removable drives (USB sticks, CD-ROMs) and -instantiates the provided _content_ particle for each detected drive. - -## TAGS - -[[ *Name* -:[ *Type* -:[ *Description* -| vendor -: string -: Name of the drive vendor -| model -: string -: Drive model name -| optical -: bool -: True if the drive is an optical drive (CD-ROM, DVD-ROM etc) -| device -: string -: Volume device name (typically */dev/sd?*) -| size -: range -: The volume's size, in bytes. The tag's maximum value is set to the - underlying block device's size -| label -: string -: The volume's label, or its size if it has no label -| mounted -: bool -: True if the volume is mounted -| mount_point -: string -: Path where the volume is mounted, or *""* if it is not mounted - -## CONFIGURATION - -[[ *Name* -:[ *Type* -:[ *Req* -:[ *Description* -| left-spacing -: int -: no -: Space, in pixels, in the left side of each rendered volume -| right-spacing -: int -: no -: Space, in pixels, on the right side of each rendered volume -| spacing -: int -: no -: Short-hand for setting both _left-spacing_ and _right-spacing_ -| ignore -: list of strings -: no -: List of device paths that should be ignored (e.g. /dev/mmcblk0, or /dev/mmcblk0p1) - -## EXAMPLES - -``` -bar: - right: - - removables: - content: - map: - tag: mounted - values: - false: - string: - on-click: udisksctl mount -b {device} - text: "{label}" - true: - string: - on-click: udisksctl unmount -b {device} - text: "{label}" - deco: {underline: {size: 2, color: ffffffff}} -``` - -# RIVER - -This module uses river's (https://github.com/ifreund/river, a dynamic -tiling Wayland compositor) status protocol to provide information -about the river tags. - -It has an interface similar to the i3/sway module. - -The configuration for the river module specifies one _title_ particle, -which will be instantiated with tags representing the currently active -seat and the currently focused view's title. - -It also specifies a _content_ template particle, which is instantiated -once for all 32 river tags. This means you probably want to use a -*map* particle to hide unused river tags. - -## TAGS - -[[ *Name* -:[ *Type* -:[ *Description* -| id -: int -: River tag number -| visible -: bool -: True if the river tag is focused by at least one output (i.e. visible on at least one monitor). -| focused -: bool -: True if the river tag is _visible_ and has keyboard focus. -| occupied -: bool -: True if the river tag has views (i.e. windows). -| state -: string -: Set to *focused* if _focused_ is true, *unfocused* if _visible_ is true, but _focused_ is false, or *invisible* if the river tag is not visible on any monitors. -| seat -: string -: The name of the currently active seat (*title* particle only, see CONFIGURATION) -| title -: string -: The focused view's title (*title* particle only, see CONFIGURATION) - -## CONFIGURATION - -[[ *Name* -:[ *Type* -:[ *Req* -:[ *Description* -| title -: particle -: no -: Particle that will be instantiated with the _seat_ and _title_ tags. -| content -: particle -: yes -: Template particle that will be instantiated once for all of the 32 river tags. - -## EXAMPLES - -``` -bar: - left: - - river: - title: {string: { text: "{seat} - {title}" }} - content: - map: - tag: occupied - values: - false: {empty: {}} - true: - string: - margin: 5 - text: "{id}: {state}" -``` - -# SCRIPT - -This module executes a user-provided script (or binary!) that writes -tags on its stdout. - -The script can either exit immediately after writing a set of tags, in -which case yambar will display those tags until yambar is -terminated. Or, the script can continue executing and update yambar -with new tag sets, either periodically, or when there is new data to -feed to yambar. - -Tag sets, or _transactions_, are separated by an empty line -(e.g. *echo ""*). The empty line is required to commit (update) the -tag even for only one transaction. - -Each _tag_ is a single line on the format: - -``` -name|type|value -``` - -Where _name_ is what you also use to refer to the tag in the yambar -configuration, _type_ is one of the tag types defined in -*yambar-tags*(5), and _value_ is the tag’s value. - -Example: - -``` -var1|string|hello -var2|int|13 - -var1|string|world -var2|int|37 - -``` - -The example above consists of two transactions. Each transaction has -two tags: one string tag and one integer tag. The second transaction -replaces the tags from the first transaction. Note that **both** -transactions need to be terminated with an empty line. - -Supported _types_ are: - -- string -- int -- bool -- float -- range:n-m (e.g. *var|range:0-100|57*) - -## TAGS - -User defined. - -## CONFIGURATION - -[[ *Name* -:[ *Type* -:[ *Req* -:[ *Description* -| path -: string -: yes -: Path to script/binary to execute. Must be an absolute path. -| args -: list of strings -: no -: Arguments to pass to the script/binary. - -## EXAMPLES - -Here is an "hello world" example script: - -``` -#!/bin/sh - -while true; do - echo "test|string|hello" - echo "" - sleep 3 - - echo "test|string|world" - echo "" - sleep 3 -done -``` - -This script will emit a single string tag, _test_, and alternate its -value between *hello* and *world* every three seconds. - -A corresponding yambar configuration could look like this: - -``` -bar: - left: - - script: - path: /path/to/script.sh - args: [] - content: {string: {text: "{test}"}} -``` - -# SWAY-XKB - -This module uses *Sway* extensions to the I3 IPC API to monitor input -devices' active XKB layout. As such, it requires Sway to be running. - -*Note* that the _content_ configuration option is a *template*; -*sway-xkb* will instantiate a particle list, where each item is -instantiated from this template, and represents an input device. - -## TAGS - -[[ *Name* -:[ *Type* -:[ *Description* -| id -: string -: Input device identifier -| layout -: string -: The input device's currently active XKB layout - -## CONFIGURATION - -[[ *Name* -:[ *Type* -:[ *Req* -:[ *Description* -| identifiers -: list of strings -: yes -: Identifiers of input devices to monitor. Use _swaymsg -t get_inputs_ to see available devices. -| content -: particle -: yes -: A particle template; each existing input device will be instantiated with this template. -| left-spacing -: int -: no -: Space, in pixels, in the left side of each rendered input device -| right-spacing -: int -: no -: Space, in pixels, on the right side of each rendered input device -| spacing -: int -: no -: Short-hand for setting both _left-spacing_ and _right-spacing_ - -## EXAMPLES - -``` -bar: - left: - - sway-xkb: - identifiers: - - 1523:7:HID_05f3:0007 - - 7247:2:USB_USB_Keykoard - spacing: 5 - content: {string: {text: "{id}: {layout}"}} -``` - -# XKB - -This module monitors the currently active XKB keyboard layout and -lock-key states. - -Note: this module is X11 only. It does not work in Wayland. - -## TAGS - -[[ *Name* -:[ *Type* -:[ *Description* -| name -: string -: Name of currently selected layout, long version (e.g. "English (US)") -| symbol -: string -: Name of currently selected layout, short version (e.g. "us") -| caps_lock -: bool -: True if *CapsLock* is enabled -| num_lock -: bool -: True if *NumLock* is enabled -| scroll_lock -: bool -: True if *ScrollLock* is enabled - -## CONFIGURATION - -No additional attributes supported, only the generic ones (see -*GENERIC CONFIGURATION*) - -## EXAMPLES - -``` -bar: - left: - - xkb: - content: - string: {text: "{symbol}"} -``` - -# XWINDOW - -This module provides the application name and window title of the -currently focused window. - -Note: this module is X11 only. It does not work in Wayland. If you are -running Sway, take a look at the *i3* module and its _application_ and -_title_ tags. - -## TAGS - -[[ *Name* -:[ *Type* -:[ *Description* -| application -: string -: Name of the application that owns the currently focused window -| title -: string -: The title of the currently focused window - -## CONFIGURATION - -No additional attributes supported, only the generic ones (see -*GENERIC CONFIGURATION*) - -## EXAMPLES - -``` -bar: - left: - - xwindow: - content: - string: {text: "{application}: {title}"} -``` +*yambar-modules-xwindow*(5) # SEE ALSO From 0f1c3548aea4f5d7014c02bd92d8365ba246e5a0 Mon Sep 17 00:00:00 2001 From: novakne Date: Tue, 23 Feb 2021 12:00:56 +0100 Subject: [PATCH 132/611] exemples/scripts: pacman.sh: handle no update in yambar config change type to int --- examples/scripts/pacman.sh | 57 +++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 31 deletions(-) diff --git a/examples/scripts/pacman.sh b/examples/scripts/pacman.sh index 739dc23..53fddb6 100755 --- a/examples/scripts/pacman.sh +++ b/examples/scripts/pacman.sh @@ -6,20 +6,31 @@ # USAGE: pacman.sh # # TAGS: -# Name Type Return +# Name Type Return # ------------------------------------------- -# {pacman} string number of pacman packages -# {aur} string number of aur packages -# {pkg} string sum of both +# {pacman} int number of pacman packages +# {aur} int number of aur packages +# {pkg} int sum of both # -# Exemple configuration: +# Exemples configuration: # - script: -# path: /absolute/path/to/pacman.sh -# args: [] -# content: { string: { text: " {pacman} + {aur} = {pkg}" } } +# path: /absolute/path/to/pacman.sh +# args: [] +# content: { string: { text: "{pacman} + {aur} = {pkg}" } } +# +# To display a message when there is no update: +# - script: +# path: /absolute/path/to/pacman.sh +# args: [] +# content: +# map: +# tag: pkg +# default: { string: { text: "{pacman} + {aur} = {pkg}" } } +# values: +# 0: {string: {text: no updates}} -declare interval no_update aur_helper pacman_num aur_num pkg_num +declare interval aur_helper pacman_num aur_num pkg_num # Error message in STDERR _err() { @@ -34,11 +45,6 @@ while true; do # "s" seconds / "m" minutes / "h" hours / "d" days interval="1h" - # Change the message you want when there is no update - # Leave empty if you want a 0 instead of a string - # (e.g. no_update="") - no_update="no update" - # Change your aur manager aur_helper="paru" @@ -53,27 +59,16 @@ while true; do pkg_num=$(( pacman_num + aur_num )) - # Only display one if there is no update and multiple tags set - if [[ "${pacman_num}" == 0 && "${aur_num}" == 0 ]]; then - pacman_num="${no_update:-$pacman_num}" - aur_num="${no_update:-$aur_num}" - pkg_num="${no_update:-$pkg_num}" - - printf -- '%s\n' "pacman|string|" - printf -- '%s\n' "aur|string|" - printf -- '%s\n' "pkg|string|${pkg_num}" - printf -- '%s\n' "" - else - printf -- '%s\n' "pacman|string|${pacman_num}" - printf -- '%s\n' "aur|string|${aur_num}" - printf -- '%s\n' "pkg|string|${pkg_num}" - printf -- '%s\n' "" - fi + printf -- '%s\n' "pacman|int|${pacman_num}" + printf -- '%s\n' "aur|int|${aur_num}" + printf -- '%s\n' "pkg|int|${pkg_num}" + printf -- '%s\n' "" sleep "${interval}" done -unset -v interval no_update aur_helper pacman_num aur_num pkg_num +unset -v interval aur_helper pacman_num aur_num pkg_num unset -f _err + From db15c63c909f6a44692e64a4d7e94c3f06d9f4ac Mon Sep 17 00:00:00 2001 From: novakne Date: Tue, 23 Feb 2021 13:24:19 +0100 Subject: [PATCH 133/611] doc: rename -swayxkb to -sway-xkb add a yambar-modules-sway pages --- doc/meson.build | 8 ++++---- ...s-swayxkb.5.scd => yambar-modules-sway-xkb.5.scd} | 2 +- doc/yambar-modules-sway.5.scd | 12 ++++++++++++ doc/yambar-modules.5.scd | 4 +++- 4 files changed, 20 insertions(+), 6 deletions(-) rename doc/{yambar-modules-swayxkb.5.scd => yambar-modules-sway-xkb.5.scd} (98%) create mode 100644 doc/yambar-modules-sway.5.scd diff --git a/doc/meson.build b/doc/meson.build index 8da6dca..4598ab2 100644 --- a/doc/meson.build +++ b/doc/meson.build @@ -9,10 +9,10 @@ foreach man_src : ['yambar.1.scd', 'yambar.5.scd', 'yambar-decorations.5.scd', 'yambar-modules-i3.5.scd', 'yambar-modules-label.5.scd', 'yambar-modules-mpd.5.scd', 'yambar-modules-network.5.scd', 'yambar-modules-removables.5.scd', 'yambar-modules-river.5.scd', - 'yambar-modules-script.5.scd', 'yambar-modules-swayxkb.5.scd', - 'yambar-modules-xkb.5.scd', 'yambar-modules-xwindow.5.scd', - 'yambar-modules.5.scd', 'yambar-particles.5.scd', - 'yambar-tags.5.scd'] + 'yambar-modules-script.5.scd', 'yambar-modules-sway-xkb.5.scd', + 'yambar-modules-sway.5.scd', 'yambar-modules-xkb.5.scd', + 'yambar-modules-xwindow.5.scd', 'yambar-modules.5.scd', + 'yambar-particles.5.scd', 'yambar-tags.5.scd'] parts = man_src.split('.') name = parts[-3] section = parts[-2] diff --git a/doc/yambar-modules-swayxkb.5.scd b/doc/yambar-modules-sway-xkb.5.scd similarity index 98% rename from doc/yambar-modules-swayxkb.5.scd rename to doc/yambar-modules-sway-xkb.5.scd index fffe8b7..eee7859 100644 --- a/doc/yambar-modules-swayxkb.5.scd +++ b/doc/yambar-modules-sway-xkb.5.scd @@ -1,4 +1,4 @@ -yambar-modules-swayxkb(5) +yambar-modules-sway-xkb(5) # NAME sway-xkb - This module monitor input devices' active XKB layout diff --git a/doc/yambar-modules-sway.5.scd b/doc/yambar-modules-sway.5.scd new file mode 100644 index 0000000..a14b57f --- /dev/null +++ b/doc/yambar-modules-sway.5.scd @@ -0,0 +1,12 @@ +yambar-modules-sway(5) + +# NAME +i3 - This module monitors i3 and sway workspaces + +# DESCRIPTION + +This module use the same configuration than i3 (see *yambar-modules-i3*(5)) + +# SEE ALSO + +*yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5) diff --git a/doc/yambar-modules.5.scd b/doc/yambar-modules.5.scd index e345004..98857d8 100644 --- a/doc/yambar-modules.5.scd +++ b/doc/yambar-modules.5.scd @@ -159,7 +159,9 @@ Available modules have their own pages: *yambar-modules-script*(5) -*yambar-modules-swayxkb*(5) +*yambar-modules-sway-xkb*(5) + +*yambar-modules-sway*(5) *yambar-modules-xkb*(5) From 075ddf3f50731ca5e8d576e269a19389c2ea279d Mon Sep 17 00:00:00 2001 From: novakne Date: Tue, 23 Feb 2021 13:44:39 +0100 Subject: [PATCH 134/611] exemples/scripts: pacman.sh: display tags early --- examples/scripts/pacman.sh | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/examples/scripts/pacman.sh b/examples/scripts/pacman.sh index 53fddb6..a20fd6b 100755 --- a/examples/scripts/pacman.sh +++ b/examples/scripts/pacman.sh @@ -37,6 +37,12 @@ _err() { printf -- '%s\n' "[$(date +'%Y-%m-%d %H:%M:%S')]: $*" >&2 } +# Display tags before yambar fetch the updates number +printf -- '%s\n' "pacman|int|0" +printf -- '%s\n' "aur|int|0" +printf -- '%s\n' "pkg|int|0" +printf -- '%s\n' "" + while true; do # Change interval @@ -53,6 +59,7 @@ while true; do if ! hash "${aur_helper}" >/dev/null 2>&1; then _err "aur helper not found, change it in the script" + exit 1 else aur_num=$("${aur_helper}" -Qmu | wc -l) fi @@ -71,4 +78,3 @@ done unset -v interval aur_helper pacman_num aur_num pkg_num unset -f _err - From 646ad0b0eb65dc0771c9e255d07535645b0fc118 Mon Sep 17 00:00:00 2001 From: novakne Date: Tue, 23 Feb 2021 16:25:29 +0100 Subject: [PATCH 135/611] doc: add empty line a the end of files fix yambar-modules-sway fix reference in *-xkb files --- doc/yambar-modules-alsa.5.scd | 1 + doc/yambar-modules-backlight.5.scd | 1 + doc/yambar-modules-battery.5.scd | 1 + doc/yambar-modules-clock.5.scd | 1 + doc/yambar-modules-i3.5.scd | 1 + doc/yambar-modules-label.5.scd | 1 + doc/yambar-modules-mpd.5.scd | 1 + doc/yambar-modules-network.5.scd | 1 + doc/yambar-modules-removables.5.scd | 1 + doc/yambar-modules-river.5.scd | 1 + doc/yambar-modules-script.5.scd | 1 + doc/yambar-modules-sway-xkb.5.scd | 3 ++- doc/yambar-modules-sway.5.scd | 8 +++----- doc/yambar-modules-xkb.5.scd | 3 ++- doc/yambar-modules-xwindow.5.scd | 1 + doc/yambar-modules.5.scd | 1 + 16 files changed, 20 insertions(+), 7 deletions(-) diff --git a/doc/yambar-modules-alsa.5.scd b/doc/yambar-modules-alsa.5.scd index d660268..a7560b3 100644 --- a/doc/yambar-modules-alsa.5.scd +++ b/doc/yambar-modules-alsa.5.scd @@ -48,3 +48,4 @@ bar: # SEE ALSO *yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5) + diff --git a/doc/yambar-modules-backlight.5.scd b/doc/yambar-modules-backlight.5.scd index bc80ef9..7c1e6c6 100644 --- a/doc/yambar-modules-backlight.5.scd +++ b/doc/yambar-modules-backlight.5.scd @@ -44,3 +44,4 @@ bar: # SEE ALSO *yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5) + diff --git a/doc/yambar-modules-battery.5.scd b/doc/yambar-modules-battery.5.scd index 2591e60..58fa6c6 100644 --- a/doc/yambar-modules-battery.5.scd +++ b/doc/yambar-modules-battery.5.scd @@ -63,3 +63,4 @@ bar: # SEE ALSO *yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5) + diff --git a/doc/yambar-modules-clock.5.scd b/doc/yambar-modules-clock.5.scd index 34995fa..05e18fc 100644 --- a/doc/yambar-modules-clock.5.scd +++ b/doc/yambar-modules-clock.5.scd @@ -44,3 +44,4 @@ bar: # SEE ALSO *yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5) + diff --git a/doc/yambar-modules-i3.5.scd b/doc/yambar-modules-i3.5.scd index 88b04f4..d61ebda 100644 --- a/doc/yambar-modules-i3.5.scd +++ b/doc/yambar-modules-i3.5.scd @@ -101,3 +101,4 @@ bar: # SEE ALSO *yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5) + diff --git a/doc/yambar-modules-label.5.scd b/doc/yambar-modules-label.5.scd index 99b85d1..a6516f1 100644 --- a/doc/yambar-modules-label.5.scd +++ b/doc/yambar-modules-label.5.scd @@ -29,3 +29,4 @@ bar: # SEE ALSO *yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5) + diff --git a/doc/yambar-modules-mpd.5.scd b/doc/yambar-modules-mpd.5.scd index f67fc3c..93e776b 100644 --- a/doc/yambar-modules-mpd.5.scd +++ b/doc/yambar-modules-mpd.5.scd @@ -77,3 +77,4 @@ bar: # SEE ALSO *yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5) + diff --git a/doc/yambar-modules-network.5.scd b/doc/yambar-modules-network.5.scd index 1a9a280..cb78809 100644 --- a/doc/yambar-modules-network.5.scd +++ b/doc/yambar-modules-network.5.scd @@ -64,3 +64,4 @@ bar: # SEE ALSO *yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5) + diff --git a/doc/yambar-modules-removables.5.scd b/doc/yambar-modules-removables.5.scd index 4628109..4b984fa 100644 --- a/doc/yambar-modules-removables.5.scd +++ b/doc/yambar-modules-removables.5.scd @@ -86,3 +86,4 @@ bar: # SEE ALSO *yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5) + diff --git a/doc/yambar-modules-river.5.scd b/doc/yambar-modules-river.5.scd index 448159c..46607a2 100644 --- a/doc/yambar-modules-river.5.scd +++ b/doc/yambar-modules-river.5.scd @@ -82,3 +82,4 @@ bar: # SEE ALSO *yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5) + diff --git a/doc/yambar-modules-script.5.scd b/doc/yambar-modules-script.5.scd index 849b60a..6ba1384 100644 --- a/doc/yambar-modules-script.5.scd +++ b/doc/yambar-modules-script.5.scd @@ -106,3 +106,4 @@ bar: # SEE ALSO *yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5) + diff --git a/doc/yambar-modules-sway-xkb.5.scd b/doc/yambar-modules-sway-xkb.5.scd index eee7859..567f11a 100644 --- a/doc/yambar-modules-sway-xkb.5.scd +++ b/doc/yambar-modules-sway-xkb.5.scd @@ -66,4 +66,5 @@ bar: # SEE ALSO -*yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5) +*yambar-modules-xkb*(5), *yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5) + diff --git a/doc/yambar-modules-sway.5.scd b/doc/yambar-modules-sway.5.scd index a14b57f..c440322 100644 --- a/doc/yambar-modules-sway.5.scd +++ b/doc/yambar-modules-sway.5.scd @@ -1,12 +1,10 @@ yambar-modules-sway(5) -# NAME -i3 - This module monitors i3 and sway workspaces - # DESCRIPTION -This module use the same configuration than i3 (see *yambar-modules-i3*(5)) +Please use the i3 (*yambar-modules-i3*(5)) module, as it is fully compatible with Sway # SEE ALSO -*yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5) +*yambar-modules*(5), *yambar-modules-i3*(5) + diff --git a/doc/yambar-modules-xkb.5.scd b/doc/yambar-modules-xkb.5.scd index ac6d121..cb9b81c 100644 --- a/doc/yambar-modules-xkb.5.scd +++ b/doc/yambar-modules-xkb.5.scd @@ -48,4 +48,5 @@ bar: # SEE ALSO -*yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5) +*yambar-modules-sway-xkb*(5), *yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5) + diff --git a/doc/yambar-modules-xwindow.5.scd b/doc/yambar-modules-xwindow.5.scd index f8abf30..b4c8e66 100644 --- a/doc/yambar-modules-xwindow.5.scd +++ b/doc/yambar-modules-xwindow.5.scd @@ -42,3 +42,4 @@ bar: # SEE ALSO *yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5) + diff --git a/doc/yambar-modules.5.scd b/doc/yambar-modules.5.scd index 98857d8..266d9b7 100644 --- a/doc/yambar-modules.5.scd +++ b/doc/yambar-modules.5.scd @@ -170,3 +170,4 @@ Available modules have their own pages: # SEE ALSO *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5) + From da0edab3fc7b96b09a8ffe6eebe8ded6e8308e69 Mon Sep 17 00:00:00 2001 From: novakne Date: Tue, 23 Feb 2021 17:10:59 +0100 Subject: [PATCH 136/611] changelog: update Contributors --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e5a1acc..2962ff4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ ### Security ### Contributors +* [novakane](https://codeberg.org/novakane) ## 1.6.1 From 8920413e12613aa5b90208df0563e26863f297ac Mon Sep 17 00:00:00 2001 From: novakne Date: Tue, 23 Feb 2021 18:04:35 +0100 Subject: [PATCH 137/611] changelog: add split up yambar-modules to changed --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2962ff4..6bfeb48 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,9 @@ ### Added ### Changed + +* doc: split up yambar-modules(5) into multiple man pages, one for each module + ### Deprecated ### Removed ### Fixed From eb76bb4830f544773b94710964541d1f9af014cc Mon Sep 17 00:00:00 2001 From: novakne Date: Tue, 23 Feb 2021 18:33:29 +0100 Subject: [PATCH 138/611] changelog: add reference to the issue --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6bfeb48..e0a478a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,8 @@ ### Added ### Changed -* doc: split up yambar-modules(5) into multiple man pages, one for each module +* doc: split up **yambar-modules**(5) into multiple man pages, one for + each module (https://codeberg.org/dnkl/yambar/issues/15). ### Deprecated ### Removed From db342546778c806833f949cf58e2c9bd69557f13 Mon Sep 17 00:00:00 2001 From: novakne Date: Wed, 24 Feb 2021 19:57:01 +0100 Subject: [PATCH 139/611] exemples/scripts: dwl-tags.sh: use yambar capacities --- examples/scripts/dwl-tags.sh | 315 ++++++++++++++++++++++++++++------- 1 file changed, 251 insertions(+), 64 deletions(-) diff --git a/examples/scripts/dwl-tags.sh b/examples/scripts/dwl-tags.sh index 44ade8f..ea1bad6 100755 --- a/examples/scripts/dwl-tags.sh +++ b/examples/scripts/dwl-tags.sh @@ -11,26 +11,266 @@ # https://github.com/djpohly/dwl/pull/91 # # TAGS: -# Name Type Return -# ------------------------------------- -# {dwltag} string dwl tags name/state +# Name Type Return +# ---------------------------------------------------- +# {dwltag_N} string dwl tags name +# {dwltag_N_occupied} bool dwl tags state occupied +# {dwltag_N_focused} bool dwl tags state focused +# {dwl_layout} string dwl layout +# {dwl_title} string client title +# +# Now the fun part # # Exemple configuration: -# - script: -# path: /absolute/path/to/dwl-tags.sh -# args: [1] -# content: {string: {text: "{dwltag}"}} +# +# - script: +# path: /absolute/path/to/dwl-tags.sh +# args: [1] +# anchors: +# - occupied: &occupied {foreground: 57bbf4ff} +# - focused: &focused {foreground: fc65b0ff} +# - default: &default {foreground: d2ccd6ff} +# content: +# - map: +# margin: 4 +# tag: dwltag_0_occupied +# values: +# true: +# map: +# tag: dwltag_0_focused +# values: +# true: +# - string: {text: "{dwltag_0}", <<: *focused} +# false: +# - string: {text: "{dwltag_0}", <<: *occupied} +# false: +# map: +# tag: dwltag_0_focused +# values: +# true: +# - string: {text: "{dwltag_0}", <<: *focused} +# false: +# - string: {text: "{dwltag_0}", <<: *default} +# - map: +# margin: 4 +# tag: dwltag_1_occupied +# values: +# true: +# map: +# tag: dwltag_1_focused +# values: +# true: +# - string: {text: "{dwltag_1}", <<: *focused} +# false: +# - string: {text: "{dwltag_1}", <<: *occupied} +# false: +# map: +# tag: dwltag_1_focused +# values: +# true: +# - string: {text: "{dwltag_1}", <<: *focused} +# false: +# - string: {text: "{dwltag_1}", <<: *default} +# - map: +# margin: 4 +# tag: dwltag_2_occupied +# values: +# true: +# map: +# tag: dwltag_2_focused +# values: +# true: +# - string: {text: "{dwltag_2}", <<: *focused} +# false: +# - string: {text: "{dwltag_2}", <<: *occupied} +# false: +# map: +# tag: dwltag_2_focused +# values: +# true: +# - string: {text: "{dwltag_2}", <<: *focused} +# false: +# - string: {text: "{dwltag_2}", <<: *default} +# - map: +# margin: 4 +# tag: dwltag_3_occupied +# values: +# true: +# map: +# tag: dwltag_3_focused +# values: +# true: +# - string: {text: "{dwltag_3}", <<: *focused} +# false: +# - string: {text: "{dwltag_3}", <<: *occupied} +# false: +# map: +# tag: dwltag_3_focused +# values: +# true: +# - string: {text: "{dwltag_3}", <<: *focused} +# false: +# - string: {text: "{dwltag_3}", <<: *default} +# - map: +# margin: 4 +# tag: dwltag_4_occupied +# values: +# true: +# map: +# tag: dwltag_4_focused +# values: +# true: +# - string: {text: "{dwltag_4}", <<: *focused} +# false: +# - string: {text: "{dwltag_4}", <<: *occupied} +# false: +# map: +# tag: dwltag_4_focused +# values: +# true: +# - string: {text: "{dwltag_4}", <<: *focused} +# false: +# - string: {text: "{dwltag_4}", <<: *default} +# - map: +# margin: 4 +# tag: dwltag_5_occupied +# values: +# true: +# map: +# tag: dwltag_5_focused +# values: +# true: +# - string: {text: "{dwltag_5}", <<: *focused} +# false: +# - string: {text: "{dwltag_5}", <<: *occupied} +# false: +# map: +# tag: dwltag_5_focused +# values: +# true: +# - string: {text: "{dwltag_5}", <<: *focused} +# false: +# - string: {text: "{dwltag_5}", <<: *default} +# - map: +# margin: 4 +# tag: dwltag_6_occupied +# values: +# true: +# map: +# tag: dwltag_6_focused +# values: +# true: +# - string: {text: "{dwltag_6}", <<: *focused} +# false: +# - string: {text: "{dwltag_6}", <<: *occupied} +# false: +# map: +# tag: dwltag_6_focused +# values: +# true: +# - string: {text: "{dwltag_6}", <<: *focused} +# false: +# - string: {text: "{dwltag_6}", <<: *default} +# - map: +# margin: 4 +# tag: dwltag_7_occupied +# values: +# true: +# map: +# tag: dwltag_7_focused +# values: +# true: +# - string: {text: "{dwltag_7}", <<: *focused} +# false: +# - string: {text: "{dwltag_7}", <<: *occupied} +# false: +# map: +# tag: dwltag_7_focused +# values: +# true: +# - string: {text: "{dwltag_7}", <<: *focused} +# false: +# - string: {text: "{dwltag_7}", <<: *default} +# - map: +# margin: 4 +# tag: dwltag_8_occupied +# values: +# true: +# map: +# tag: dwltag_8_focused +# values: +# true: +# - string: {text: "{dwltag_8}", <<: *focused} +# false: +# - string: {text: "{dwltag_8}", <<: *occupied} +# false: +# map: +# tag: dwltag_8_focused +# values: +# true: +# - string: {text: "{dwltag_8}", <<: *focused} +# false: +# - string: {text: "{dwltag_8}", <<: *default} +# - list: +# spacing: 3 +# items: +# - string: {text: "{dwl_layout}"} +# - string: {text: "{dwl_title}"} # Variables declare titleline tagline title taginfo isactive ctags mtags layout -declare symbol_occupied_pre symbol_occupied_post symbol_focused_pre symbol_focused_post declare -a tags name readonly fname=/tmp/dwltags-"$WAYLAND_DISPLAY" +_cycle() { + tags=( "1" "2" "3" "4" "5" "6" "7" "8" "9" ) + + # Name of tag (optional) + # If there is no name, number are used + # + # Example: + # name=( "" "" "" "Media" ) + # -> return "" "" "" "Media" 5 6 7 8 9) + name=() + + for tag in "${!tags[@]}"; do + mask=$((1< return "" "" "" "Media" 5 6 7 8 9) - name=() - - # Symbol for occupied tags - # - # Format: "{symbol_occupied_pre}{TAGNAME}{symbol_occupied_post}" - # You can leave one empty if you don't want to surround the TAGNAME - symbol_occupied_pre="" - symbol_occupied_post="." - - # Symbol for the focused tag - # - # Format: "{symbol_focused_pre}{TAGNAME}{symbol_focused_post}" - # You can leave one empty if you don't want to surround the TAGNAME - symbol_focused_pre="[ " - symbol_focused_post=" ]" - - for i in {0..8}; do - mask=$((1< Date: Thu, 25 Feb 2021 08:26:10 +0100 Subject: [PATCH 140/611] exemples/scripts: dwl-tags.sh: fix exemple length rename yambar tags --- examples/scripts/dwl-tags.sh | 205 +++++------------------------------ 1 file changed, 30 insertions(+), 175 deletions(-) diff --git a/examples/scripts/dwl-tags.sh b/examples/scripts/dwl-tags.sh index ea1bad6..7e68257 100755 --- a/examples/scripts/dwl-tags.sh +++ b/examples/scripts/dwl-tags.sh @@ -6,18 +6,18 @@ # # REQUIREMENTS: # - inotifywait ( 'inotify-tools' on arch ) -# - 2021/02/22 - dwl pull request: +# - 2021/02/25 - dwl pull request: # 'Interface to display tag information on status bar #91' # https://github.com/djpohly/dwl/pull/91 # # TAGS: -# Name Type Return +# Name Type Return # ---------------------------------------------------- -# {dwltag_N} string dwl tags name -# {dwltag_N_occupied} bool dwl tags state occupied -# {dwltag_N_focused} bool dwl tags state focused -# {dwl_layout} string dwl layout -# {dwl_title} string client title +# {tag_N} string dwl tags name +# {tag_N_occupied} bool dwl tags state occupied +# {tag_N_focused} bool dwl tags state focused +# {layout} string dwl layout +# {title} string client title # # Now the fun part # @@ -33,189 +33,44 @@ # content: # - map: # margin: 4 -# tag: dwltag_0_occupied +# tag: tag_0_occupied # values: # true: # map: -# tag: dwltag_0_focused +# tag: tag_0_focused # values: -# true: -# - string: {text: "{dwltag_0}", <<: *focused} -# false: -# - string: {text: "{dwltag_0}", <<: *occupied} +# true: {string: {text: "{tag_0}", <<: *focused}} +# false: {string: {text: "{tag_0}", <<: *occupied}} # false: # map: -# tag: dwltag_0_focused +# tag: tag_0_focused # values: -# true: -# - string: {text: "{dwltag_0}", <<: *focused} -# false: -# - string: {text: "{dwltag_0}", <<: *default} +# true: {string: {text: "{tag_0}", <<: *focused}} +# false: {string: {text: "{tag_0}", <<: *default}} +# ... +# ... +# ... # - map: # margin: 4 -# tag: dwltag_1_occupied +# tag: tag_8_occupied # values: # true: # map: -# tag: dwltag_1_focused +# tag: tag_8_focused # values: -# true: -# - string: {text: "{dwltag_1}", <<: *focused} -# false: -# - string: {text: "{dwltag_1}", <<: *occupied} +# true: {string: {text: "{tag_8}", <<: *focused}} +# false: {string: {text: "{tag_8}", <<: *occupied}} # false: # map: -# tag: dwltag_1_focused +# tag: tag_8_focused # values: -# true: -# - string: {text: "{dwltag_1}", <<: *focused} -# false: -# - string: {text: "{dwltag_1}", <<: *default} -# - map: -# margin: 4 -# tag: dwltag_2_occupied -# values: -# true: -# map: -# tag: dwltag_2_focused -# values: -# true: -# - string: {text: "{dwltag_2}", <<: *focused} -# false: -# - string: {text: "{dwltag_2}", <<: *occupied} -# false: -# map: -# tag: dwltag_2_focused -# values: -# true: -# - string: {text: "{dwltag_2}", <<: *focused} -# false: -# - string: {text: "{dwltag_2}", <<: *default} -# - map: -# margin: 4 -# tag: dwltag_3_occupied -# values: -# true: -# map: -# tag: dwltag_3_focused -# values: -# true: -# - string: {text: "{dwltag_3}", <<: *focused} -# false: -# - string: {text: "{dwltag_3}", <<: *occupied} -# false: -# map: -# tag: dwltag_3_focused -# values: -# true: -# - string: {text: "{dwltag_3}", <<: *focused} -# false: -# - string: {text: "{dwltag_3}", <<: *default} -# - map: -# margin: 4 -# tag: dwltag_4_occupied -# values: -# true: -# map: -# tag: dwltag_4_focused -# values: -# true: -# - string: {text: "{dwltag_4}", <<: *focused} -# false: -# - string: {text: "{dwltag_4}", <<: *occupied} -# false: -# map: -# tag: dwltag_4_focused -# values: -# true: -# - string: {text: "{dwltag_4}", <<: *focused} -# false: -# - string: {text: "{dwltag_4}", <<: *default} -# - map: -# margin: 4 -# tag: dwltag_5_occupied -# values: -# true: -# map: -# tag: dwltag_5_focused -# values: -# true: -# - string: {text: "{dwltag_5}", <<: *focused} -# false: -# - string: {text: "{dwltag_5}", <<: *occupied} -# false: -# map: -# tag: dwltag_5_focused -# values: -# true: -# - string: {text: "{dwltag_5}", <<: *focused} -# false: -# - string: {text: "{dwltag_5}", <<: *default} -# - map: -# margin: 4 -# tag: dwltag_6_occupied -# values: -# true: -# map: -# tag: dwltag_6_focused -# values: -# true: -# - string: {text: "{dwltag_6}", <<: *focused} -# false: -# - string: {text: "{dwltag_6}", <<: *occupied} -# false: -# map: -# tag: dwltag_6_focused -# values: -# true: -# - string: {text: "{dwltag_6}", <<: *focused} -# false: -# - string: {text: "{dwltag_6}", <<: *default} -# - map: -# margin: 4 -# tag: dwltag_7_occupied -# values: -# true: -# map: -# tag: dwltag_7_focused -# values: -# true: -# - string: {text: "{dwltag_7}", <<: *focused} -# false: -# - string: {text: "{dwltag_7}", <<: *occupied} -# false: -# map: -# tag: dwltag_7_focused -# values: -# true: -# - string: {text: "{dwltag_7}", <<: *focused} -# false: -# - string: {text: "{dwltag_7}", <<: *default} -# - map: -# margin: 4 -# tag: dwltag_8_occupied -# values: -# true: -# map: -# tag: dwltag_8_focused -# values: -# true: -# - string: {text: "{dwltag_8}", <<: *focused} -# false: -# - string: {text: "{dwltag_8}", <<: *occupied} -# false: -# map: -# tag: dwltag_8_focused -# values: -# true: -# - string: {text: "{dwltag_8}", <<: *focused} -# false: -# - string: {text: "{dwltag_8}", <<: *default} +# true: {string: {text: "{tag_8}", <<: *focused}} +# false: {string: {text: "{tag_8}", <<: *default}} # - list: # spacing: 3 # items: -# - string: {text: "{dwl_layout}"} -# - string: {text: "{dwl_title}"} +# - string: {text: "{layout}"} +# - string: {text: "{title}"} # Variables @@ -238,7 +93,7 @@ _cycle() { for tag in "${!tags[@]}"; do mask=$((1< Date: Fri, 26 Feb 2021 10:59:11 +0100 Subject: [PATCH 141/611] doc: yambar-modules-alsa: fix soundcard name --- doc/yambar-modules-alsa.5.scd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/yambar-modules-alsa.5.scd b/doc/yambar-modules-alsa.5.scd index a7560b3..5ae5df8 100644 --- a/doc/yambar-modules-alsa.5.scd +++ b/doc/yambar-modules-alsa.5.scd @@ -28,7 +28,7 @@ alsa - Monitors an alsa soundcard for volume and mute/unmute changes | card : string : yes -: The soundcard name. _Default_ might work. +: The soundcard name. _default_ might work. | mixer : string : yes From f12db421126350f94be78eb1f76d85e6e9500db8 Mon Sep 17 00:00:00 2001 From: novakne Date: Fri, 26 Feb 2021 11:30:37 +0100 Subject: [PATCH 142/611] doc: yambar-modules-alsa: change highlighting fron _ to * --- doc/yambar-modules-alsa.5.scd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/yambar-modules-alsa.5.scd b/doc/yambar-modules-alsa.5.scd index 5ae5df8..23f3291 100644 --- a/doc/yambar-modules-alsa.5.scd +++ b/doc/yambar-modules-alsa.5.scd @@ -28,7 +28,7 @@ alsa - Monitors an alsa soundcard for volume and mute/unmute changes | card : string : yes -: The soundcard name. _default_ might work. +: The soundcard name. *default* might work. | mixer : string : yes From 21a84aed72f9f1f9e6527c0e90ef3dbd7d8612cb Mon Sep 17 00:00:00 2001 From: novakne Date: Sun, 28 Mar 2021 12:50:45 +0200 Subject: [PATCH 143/611] exemples/script: Update dwl-tags Update dwl-tags to works with dwl main branch now that the pull request was merged --- examples/scripts/dwl-tags.sh | 54 +++++++++++++++--------------------- 1 file changed, 23 insertions(+), 31 deletions(-) diff --git a/examples/scripts/dwl-tags.sh b/examples/scripts/dwl-tags.sh index 7e68257..395a790 100755 --- a/examples/scripts/dwl-tags.sh +++ b/examples/scripts/dwl-tags.sh @@ -6,9 +6,7 @@ # # REQUIREMENTS: # - inotifywait ( 'inotify-tools' on arch ) -# - 2021/02/25 - dwl pull request: -# 'Interface to display tag information on status bar #91' -# https://github.com/djpohly/dwl/pull/91 +# - Launch dwl with `dwl > ~.cache/dwltags` or change $fname # # TAGS: # Name Type Return @@ -74,9 +72,9 @@ # Variables -declare titleline tagline title taginfo isactive ctags mtags layout +declare output title layout activetags selectedtags declare -a tags name -readonly fname=/tmp/dwltags-"$WAYLAND_DISPLAY" +readonly fname="$HOME"/.cache/dwltags _cycle() { @@ -99,21 +97,18 @@ _cycle() { printf -- '%s\n' "${tag_name}_${tag}|string|${name[tag]}" - # Occupied - if (( "${ctags}" & mask )); then + if (( "${selectedtags}" & mask )) 2>/dev/null; then + printf -- '%s\n' "${tag_name}_${tag}_focused|bool|true" + printf -- '%s\n' "title|string|${title}" + else + printf '%s\n' "${tag_name}_${tag}_focused|bool|false" + fi + + if (( "${activetags}" & mask )) 2>/dev/null; then printf -- '%s\n' "${tag_name}_${tag}_occupied|bool|true" else printf -- '%s\n' "${tag_name}_${tag}_occupied|bool|false" fi - - # Focused - if (( "${mtags}" & mask )); then - printf -- '%s\n' "${tag_name}_${tag}_focused|bool|true" - printf -- '%s\n' "title|string|${title}" - else - printf -- '%s\n' "${tag_name}_${tag}_focused|bool|false" - fi - done printf -- '%s\n' "layout|string|${layout}" @@ -126,28 +121,25 @@ _cycle while true; do - # Make sure the file exists - while [ ! -f "${fname}" ]; do - inotifywait -qqe create "$(dirname "${fname}")" - done; + [[ ! -f "${fname}" ]] && printf -- '%s\n' \ + "You need to redirect dwl stdout to ~/.cache/dwltags" >&2 - # Wait for dwl to close it after writing - inotifywait -qqe close_write "${fname}" + inotifywait -qq --event modify "${fname}" # Get info from the file - titleline="$1" - tagline=$((titleline+1)) - title=$(sed "${titleline}!d" "${fname}") - taginfo=$(sed "${tagline}!d" "${fname}") - isactive=$(printf -- '%s\n' "${taginfo}" | cut -d ' ' -f 1) - ctags=$(printf -- '%s\n' "${taginfo}" | cut -d ' ' -f 2) - mtags=$(printf -- '%s\n' "${taginfo}" | cut -d ' ' -f 3) - layout=$(printf -- '%s\n' "${taginfo}" | cut -d ' ' -f 4-) + output="$(tail -n4 "${fname}")" + title="$(echo "${output}" | grep title | cut -d ' ' -f 3- )" + #selmon="$(echo "${output}" | grep 'selmon')" + layout="$(echo "${output}" | grep layout | cut -d ' ' -f 3- )" + + # Get the tag bit mask as a decimal + activetags="$(echo "${output}" | grep tags | awk '{print $3}')" + selectedtags="$(echo "${output}" | grep tags | awk '{print $4}')" _cycle done -unset -v titleline tagline title taginfo isactive ctags mtags layout +unset -v output title layout activetags selectedtags unset -v tags name From 8c93b48146762091959152f6757b40ad74bbbe5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 7 Apr 2021 07:58:16 +0200 Subject: [PATCH 144/611] bar/wayland: xdg_handle_output_name(): free previous monitor name --- bar/wayland.c | 1 + 1 file changed, 1 insertion(+) diff --git a/bar/wayland.c b/bar/wayland.c index f4b3cce..2b218d7 100644 --- a/bar/wayland.c +++ b/bar/wayland.c @@ -465,6 +465,7 @@ xdg_output_handle_name(void *data, struct zxdg_output_v1 *xdg_output, const char *name) { struct monitor *mon = data; + free(mon->name); mon->name = strdup(name); } From 9a6f691493ccccb320c6609725b894b1153f56b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 7 Apr 2021 21:21:19 +0200 Subject: [PATCH 145/611] ci: build on alpine/latest, not edge --- .builds/alpine-x64.yml | 2 +- .gitlab-ci.yml | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.builds/alpine-x64.yml b/.builds/alpine-x64.yml index 1e8961a..a5d4e9c 100644 --- a/.builds/alpine-x64.yml +++ b/.builds/alpine-x64.yml @@ -1,4 +1,4 @@ -image: alpine/edge +image: alpine/latest packages: - musl-dev - eudev-libs diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e9446a6..06df201 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,4 +1,4 @@ -image: alpine:edge +image: alpine:latest stages: - info @@ -8,7 +8,6 @@ variables: GIT_SUBMODULE_STRATEGY: normal before_script: - - echo "http://dl-cdn.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories - apk update - apk add musl-dev eudev-libs eudev-dev linux-headers meson ninja gcc scdoc - apk add pixman-dev freetype-dev fontconfig-dev @@ -90,7 +89,7 @@ plugins_as_shared_modules: - meson test --print-errorlogs codespell: - image: alpine:edge + image: alpine:latest stage: build script: - apk add python3 From db7a4af80a9c2d432358ab4d3c95dea4649c309b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 17 Apr 2021 20:35:48 +0200 Subject: [PATCH 146/611] main: call fcft_log_init(). Note that this requires fcft >= 2.3.90 --- main.c | 9 ++++++++- meson.build | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/main.c b/main.c index f8a8453..cab5f9e 100644 --- a/main.c +++ b/main.c @@ -273,7 +273,14 @@ main(int argc, char *const *argv) } } - log_init(log_colorize, log_syslog, LOG_FACILITY_DAEMON, LOG_CLASS_WARNING); + log_init(log_colorize, log_syslog, LOG_FACILITY_DAEMON, LOG_CLASS_INFO); + + _Static_assert(LOG_CLASS_ERROR + 1 == FCFT_LOG_CLASS_ERROR, + "fcft log level enum offset"); + _Static_assert((int)LOG_COLORIZE_ALWAYS == (int)FCFT_LOG_COLORIZE_ALWAYS, + "fcft colorize enum mismatch"); + fcft_log_init( + (enum fcft_log_colorize)log_colorize, log_syslog, FCFT_LOG_CLASS_INFO); const struct sigaction sa = {.sa_handler = &signal_handler}; sigaction(SIGINT, &sa, NULL); diff --git a/meson.build b/meson.build index 9309064..fb92e5e 100644 --- a/meson.build +++ b/meson.build @@ -65,7 +65,7 @@ backend_wayland = wayland_client.found() and wayland_cursor.found() # "My" dependencies, fallback to subproject tllist = dependency('tllist', version: '>=1.0.1', fallback: 'tllist') -fcft = dependency('fcft', version: ['>=2.0.0', '<3.0.0'], fallback: 'fcft') +fcft = dependency('fcft', version: ['>=2.3.90', '<3.0.0'], fallback: 'fcft') add_project_arguments( ['-D_GNU_SOURCE'] + From f9dad99db855a6891968abd71366ceb17b44a2c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 22 Apr 2021 11:44:09 +0200 Subject: [PATCH 147/611] particle/ramp: clamp min/max/value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make sure that: * min <= max * min <= value <= max Fixes a crash when the tag’s value was out-of-bounds. Closes #45 --- CHANGELOG.md | 4 +++- particles/ramp.c | 23 +++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e0a478a..1f41f9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ ### Changed * doc: split up **yambar-modules**(5) into multiple man pages, one for - each module (https://codeberg.org/dnkl/yambar/issues/15). + each module (https://codeberg.org/dnkl/yambar/issues/15). ### Deprecated ### Removed @@ -20,6 +20,8 @@ * Crash when merging non-dictionary anchors in the YAML configuration (https://codeberg.org/dnkl/yambar/issues/32). +* Crash in the `ramp` particle when the tag’s value was out-of-bounds + (https://codeberg.org/dnkl/yambar/issues/45). ### Security diff --git a/particles/ramp.c b/particles/ramp.c index 45cc277..9a7f06e 100644 --- a/particles/ramp.c +++ b/particles/ramp.c @@ -4,6 +4,9 @@ #include +#define LOG_MODULE "ramp" +#define LOG_ENABLE_DBG 0 +#include "../log.h" #include "../config.h" #include "../config-verify.h" #include "../particle.h" @@ -102,6 +105,26 @@ instantiate(const struct particle *particle, const struct tag_set *tags) long min = tag != NULL ? tag->min(tag) : 0; long max = tag != NULL ? tag->max(tag) : 0; + if (min > max) { + LOG_WARN( + "tag's minimum value is greater than its maximum: " + "tag=\"%s\", min=%ld, max=%ld", p->tag, min, max); + min = max; + } + + if (value < min) { + LOG_WARN( + "tag's value is less than its minimum value: " + "tag=\"%s\", min=%ld, value=%ld", p->tag, min, value); + value = min; + } + if (value > max) { + LOG_WARN( + "tag's value is greater than its maximum value: " + "tag=\"%s\", max=%ld, value=%ld", p->tag, max, value); + value = max; + } + assert(value >= min && value <= max); assert(max >= min); From 18a0920ed9d2311ad137023a6ec2b9d7a7bca024 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 4 May 2021 13:46:20 +0200 Subject: [PATCH 148/611] meson: version.sh: SOURCE_DIR is not valid in custom_targets() --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index fb92e5e..74418af 100644 --- a/meson.build +++ b/meson.build @@ -100,7 +100,7 @@ version = custom_target( 'generate_version', build_always_stale: true, output: 'version.h', - command: [generate_version_sh, meson.project_version(), '@SOURCE_DIR@', '@OUTPUT@']) + command: [generate_version_sh, meson.project_version(), '@SOURCE_ROOT@', '@OUTPUT@']) yambar = executable( 'yambar', From a5bbf0b7693db2fcd3c7e3b07dcb3e4276f9abfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 24 May 2021 17:38:43 +0200 Subject: [PATCH 149/611] particle/string: use fcft_text_run_rasterize() when available MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This enables support for text shaping, and is required to render e.g. 👩‍👩‍👧‍👧 correctly. Since text-shaping is a fairly expensive operation, and since many times the text is unchanged, we cache the last *rendered* string. That is, we hash the instantiated string, and cache it along with the text-run from fcft in the *particle* object (i.e. not the exposable). This means two things: * we only need to call fcft_text_run_rasterize() once per string * if the string is the same as last time, we don’t have to call it at all. --- meson.build | 2 +- particles/string.c | 87 +++++++++++++++++++++++++++++++++++++++------- 2 files changed, 75 insertions(+), 14 deletions(-) diff --git a/meson.build b/meson.build index 74418af..a1e4fa1 100644 --- a/meson.build +++ b/meson.build @@ -65,7 +65,7 @@ backend_wayland = wayland_client.found() and wayland_cursor.found() # "My" dependencies, fallback to subproject tllist = dependency('tllist', version: '>=1.0.1', fallback: 'tllist') -fcft = dependency('fcft', version: ['>=2.3.90', '<3.0.0'], fallback: 'fcft') +fcft = dependency('fcft', version: ['>=2.4.0', '<3.0.0'], fallback: 'fcft') add_project_arguments( ['-D_GNU_SOURCE'] + diff --git a/particles/string.c b/particles/string.c index 5e98132..abb581e 100644 --- a/particles/string.c +++ b/particles/string.c @@ -13,6 +13,12 @@ struct private { char *text; size_t max_len; + + struct { + uint64_t hash; + struct fcft_text_run *run; + int width; + } cached; }; struct eprivate { @@ -20,17 +26,31 @@ struct eprivate { char *text; const struct fcft_glyph **glyphs; + const struct fcft_glyph **allocated_glyphs; long *kern_x; int num_glyphs; }; +static uint64_t +sdbm_hash(const char *s) +{ + uint64_t hash = 0; + + for (; *s != '\0'; s++) { + int c = *s; + hash = c + (hash << 6) + (hash << 16) - hash; + } + + return hash; +} + static void exposable_destroy(struct exposable *exposable) { struct eprivate *e = exposable->private; free(e->text); - free(e->glyphs); + free(e->allocated_glyphs); free(e->kern_x); free(e); exposable_default_destroy(exposable); @@ -40,33 +60,72 @@ static int begin_expose(struct exposable *exposable) { struct eprivate *e = exposable->private; + struct private *p = exposable->particle->private; struct fcft_font *font = exposable->particle->font; - e->glyphs = NULL; + e->glyphs = e->allocated_glyphs = NULL; e->num_glyphs = 0; + e->kern_x = NULL; + + uint64_t hash = sdbm_hash(e->text); + + if (p->cached.hash == hash) { + e->glyphs = p->cached.run->glyphs; + e->num_glyphs = p->cached.run->count; + e->kern_x = calloc(p->cached.run->count, sizeof(e->kern_x[0])); + + exposable->width = + exposable->particle->left_margin + + p->cached.width + + exposable->particle->right_margin; + + return exposable->width; + } size_t chars = mbstowcs(NULL, e->text, 0); if (chars != (size_t)-1) { wchar_t wtext[chars + 1]; mbstowcs(wtext, e->text, chars + 1); - e->glyphs = malloc(chars * sizeof(e->glyphs[0])); e->kern_x = calloc(chars, sizeof(e->kern_x[0])); - /* Convert text to glyph masks/images. */ - for (size_t i = 0; i < chars; i++) { - const struct fcft_glyph *glyph = fcft_glyph_rasterize( - font, wtext[i], FCFT_SUBPIXEL_NONE); + if (fcft_capabilities() & FCFT_CAPABILITY_TEXT_RUN_SHAPING) { + struct fcft_text_run *run = fcft_text_run_rasterize(font, chars, wtext, FCFT_SUBPIXEL_NONE); + if (run != NULL) { + int w = 0; + for (size_t i = 0; i < run->count; i++) + w += run->glyphs[i]->advance.x; - if (glyph == NULL) - continue; + fcft_text_run_destroy(p->cached.run); + p->cached.hash = hash; + p->cached.run = run; + p->cached.width = w; - e->glyphs[e->num_glyphs++] = glyph; + e->num_glyphs = run->count; + e->glyphs = run->glyphs; + } + } - if (i == 0) - continue; + if (e->glyphs == NULL) { + e->allocated_glyphs = malloc(chars * sizeof(e->glyphs[0])); - fcft_kerning(font, wtext[i - 1], wtext[i], &e->kern_x[i], NULL); + /* Convert text to glyph masks/images. */ + for (size_t i = 0; i < chars; i++) { + const struct fcft_glyph *glyph = fcft_glyph_rasterize( + font, wtext[i], FCFT_SUBPIXEL_NONE); + + if (glyph == NULL) + continue; + + e->allocated_glyphs[e->num_glyphs++] = glyph; + + if (i == 0) + continue; + + fcft_kerning(font, wtext[i - 1], wtext[i], &e->kern_x[i], NULL); + } + + e->glyphs = e->allocated_glyphs; } } @@ -188,6 +247,7 @@ static void particle_destroy(struct particle *particle) { struct private *p = particle->private; + fcft_text_run_destroy(p->cached.run); free(p->text); free(p); particle_default_destroy(particle); @@ -199,6 +259,7 @@ string_new(struct particle *common, const char *text, size_t max_len) struct private *p = calloc(1, sizeof(*p)); p->text = strdup(text); p->max_len = max_len; + p->cached.hash = -1; common->private = p; common->destroy = &particle_destroy; From cb45e53cb4cb4c7f9e6aca23f9e948dcdacf8ad5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 24 May 2021 17:42:39 +0200 Subject: [PATCH 150/611] changelog: text shaping support --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f41f9c..a02a7ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,10 +9,16 @@ ## Unreleased ### Added + +* Text shaping support. + + ### Changed * doc: split up **yambar-modules**(5) into multiple man pages, one for each module (https://codeberg.org/dnkl/yambar/issues/15). +* fcft >= 2.4.0 is now required. + ### Deprecated ### Removed From 5c4ae642f2f5ce5bc78deed40a87315f01068bf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 25 May 2021 17:34:09 +0200 Subject: [PATCH 151/611] =?UTF-8?q?tag:=20fix=20crash=20on=20empty=20tag?= =?UTF-8?q?=20specifier,=20=E2=80=9C{}=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For strings with empty tag specifiers, “{}”, we ended up calling tag_for_name() with a NULL pointer for name. This caused us to crash. Closes #48 --- CHANGELOG.md | 2 ++ tag.c | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a02a7ba..c36baa8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,8 @@ (https://codeberg.org/dnkl/yambar/issues/32). * Crash in the `ramp` particle when the tag’s value was out-of-bounds (https://codeberg.org/dnkl/yambar/issues/45). +* Crash when a string particle contained `{}` + (https://codeberg.org/dnkl/yambar/issues/48). ### Security diff --git a/tag.c b/tag.c index 02e0794..5b00218 100644 --- a/tag.c +++ b/tag.c @@ -446,8 +446,9 @@ tags_expand_template(const char *template, const struct tag_set *tags) } /* Lookup tag */ - const struct tag *tag = tag_for_name(tags, tag_name); - if (tag == NULL) { + const struct tag *tag = NULL; + + if (tag_name == NULL || (tag = tag_for_name(tags, tag_name)) == NULL) { /* No such tag, copy as-is instead */ sbuf_append_at_most(&formatted, template, begin - template + 1); template = begin + 1; From 0e9c96e6b31a8a510fb8132268a3d0138b06618d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 25 May 2021 19:00:31 +0200 Subject: [PATCH 152/611] pkgbuild: bump fcft requirement to 2.4.0 --- PKGBUILD | 2 +- PKGBUILD.wayland-only | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/PKGBUILD b/PKGBUILD index 12e6642..66e3efe 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -15,7 +15,7 @@ depends=( 'libudev.so' 'json-c' 'libmpdclient' - 'fcft>=2.0.0') + 'fcft>=2.4.0') optdepends=('xcb-util-errors: better X error messages') source=() diff --git a/PKGBUILD.wayland-only b/PKGBUILD.wayland-only index 10a44ea..34dd8ae 100644 --- a/PKGBUILD.wayland-only +++ b/PKGBUILD.wayland-only @@ -16,7 +16,7 @@ depends=( 'libudev.so' 'json-c' 'libmpdclient' - 'fcft>=2.0.0') + 'fcft>=2.4.0') source=() pkgver() { From 15ed0e043bf4c811b6a5d13b80edf4db77e84b86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 25 May 2021 17:36:55 +0200 Subject: [PATCH 153/611] =?UTF-8?q?particle/string:=20don=E2=80=99t=20thra?= =?UTF-8?q?sh=20the=20text-run=20cache?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit a5bbf0b7693db2fcd3c7e3b07dcb3e4276f9abfd introduced text-run shaping. Do avoid having to re-shape non-changing strings every time the bar is refreshed, the *particle* (i.e. not the exposable) caches the last shaped text-run. Then, in expose(), it then assumes that that cached text-run is the *same* text-run as returned from begin_expose(). This is true in most cases, but *not* when a single particle is re-used to instantiate multiple exposables, as is commonly done by modules generating dynlists. For example, the i3/sway module. This fixes it, by making the cache growable, and by adding a “lock” to each cache entry. The lock is set in begin_expose(), to indicate that this particular cache entry is needed in expose(). If we can’t find a matching cache entry, we first try to find a free “slot” by searching for either unused, or used-but-not-locked cache entries. If that fails, we grow the cache and add a new entry. In expose(), we unset the lock. Closes #47 --- particles/string.c | 81 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 61 insertions(+), 20 deletions(-) diff --git a/particles/string.c b/particles/string.c index abb581e..653c8ef 100644 --- a/particles/string.c +++ b/particles/string.c @@ -10,21 +10,26 @@ #include "../particle.h" #include "../plugin.h" +struct text_run_cache { + uint64_t hash; + struct fcft_text_run *run; + int width; + bool in_use; +}; + struct private { char *text; size_t max_len; - struct { - uint64_t hash; - struct fcft_text_run *run; - int width; - } cached; + size_t cache_size; + struct text_run_cache *cache; }; struct eprivate { /* Set when instantiating */ char *text; + ssize_t cache_idx; const struct fcft_glyph **glyphs; const struct fcft_glyph **allocated_glyphs; long *kern_x; @@ -66,20 +71,27 @@ begin_expose(struct exposable *exposable) e->glyphs = e->allocated_glyphs = NULL; e->num_glyphs = 0; e->kern_x = NULL; + e->cache_idx = -1; uint64_t hash = sdbm_hash(e->text); - if (p->cached.hash == hash) { - e->glyphs = p->cached.run->glyphs; - e->num_glyphs = p->cached.run->count; - e->kern_x = calloc(p->cached.run->count, sizeof(e->kern_x[0])); + for (size_t i = 0; i < p->cache_size; i++) { + if (p->cache[i].hash == hash) { + assert(p->cache[i].run != NULL); - exposable->width = - exposable->particle->left_margin + - p->cached.width + - exposable->particle->right_margin; + p->cache[i].in_use = true; + e->cache_idx = i; + e->glyphs = p->cache[i].run->glyphs; + e->num_glyphs = p->cache[i].run->count; + e->kern_x = calloc(p->cache[i].run->count, sizeof(e->kern_x[0])); - return exposable->width; + exposable->width = + exposable->particle->left_margin + + p->cache[i].width + + exposable->particle->right_margin; + + return exposable->width; + } } size_t chars = mbstowcs(NULL, e->text, 0); @@ -96,11 +108,32 @@ begin_expose(struct exposable *exposable) for (size_t i = 0; i < run->count; i++) w += run->glyphs[i]->advance.x; - fcft_text_run_destroy(p->cached.run); - p->cached.hash = hash; - p->cached.run = run; - p->cached.width = w; + ssize_t cache_idx = -1; + for (size_t i = 0; i < p->cache_size; i++) { + if (p->cache[i].run == NULL || !p->cache[i].in_use) { + fcft_text_run_destroy(p->cache[i].run); + cache_idx = i; + break; + } + } + if (cache_idx < 0) { + size_t new_size = p->cache_size + 1; + struct text_run_cache *new_cache = realloc( + p->cache, new_size * sizeof(new_cache[0])); + + p->cache_size = new_size; + p->cache = new_cache; + cache_idx = new_size - 1; + } + + assert(cache_idx >= 0 && cache_idx < p->cache_size); + p->cache[cache_idx].hash = hash; + p->cache[cache_idx].run = run; + p->cache[cache_idx].width = w; + p->cache[cache_idx].in_use = true; + + e->cache_idx = cache_idx; e->num_glyphs = run->count; e->glyphs = run->glyphs; } @@ -147,6 +180,11 @@ expose(const struct exposable *exposable, pixman_image_t *pix, int x, int y, int const struct eprivate *e = exposable->private; const struct fcft_font *font = exposable->particle->font; + if (e->cache_idx >= 0) { + struct private *priv = exposable->particle->private; + priv->cache[e->cache_idx].in_use = false; + } + if (e->num_glyphs == 0) return; @@ -247,7 +285,9 @@ static void particle_destroy(struct particle *particle) { struct private *p = particle->private; - fcft_text_run_destroy(p->cached.run); + for (size_t i = 0; i < p->cache_size; i++) + fcft_text_run_destroy(p->cache[i].run); + free(p->cache); free(p->text); free(p); particle_default_destroy(particle); @@ -259,7 +299,8 @@ string_new(struct particle *common, const char *text, size_t max_len) struct private *p = calloc(1, sizeof(*p)); p->text = strdup(text); p->max_len = max_len; - p->cached.hash = -1; + p->cache_size = 0; + p->cache = NULL; common->private = p; common->destroy = &particle_destroy; From 463f1ea75d9f1860f1064afba78aec635b0751da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 26 May 2021 21:11:53 +0200 Subject: [PATCH 154/611] module/sway-xkb: ignore non-keyboard inputs Closes #51 --- CHANGELOG.md | 2 ++ modules/sway-xkb.c | 9 +++++++++ 2 files changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c36baa8..8910271 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,8 @@ * doc: split up **yambar-modules**(5) into multiple man pages, one for each module (https://codeberg.org/dnkl/yambar/issues/15). * fcft >= 2.4.0 is now required. +* sway-xkb: non-keyboard inputs are now ignored + (https://codeberg.org/dnkl/yambar/issues/51). ### Deprecated diff --git a/modules/sway-xkb.c b/modules/sway-xkb.c index 114a361..83d4d10 100644 --- a/modules/sway-xkb.c +++ b/modules/sway-xkb.c @@ -99,6 +99,15 @@ handle_input_reply(int type, const struct json_object *json, void *_mod) return false; const char *id = json_object_get_string(identifier); + + struct json_object *type; + if (!json_object_object_get_ex(obj, "type", &type)) + return false; + if (strcmp(json_object_get_string(type), "keyboard") != 0) { + LOG_DBG("ignoring non-keyboard input '%s'", id); + continue; + } + struct input *input = NULL; for (size_t i = 0; i < m->num_inputs; i++) { struct input *maybe_input = &m->inputs[i]; From 8153e40f2aae132601fb3f3f69f75a71a456a67f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 1 Jun 2021 17:40:59 +0200 Subject: [PATCH 155/611] module/sway-xkb: ignore non-keyboards in input event handler This is already being done in the initial query response. Not doing it in the input event handler too leads to an assertion if there are multiple devices with the same ID. Hopefully fixes #54 --- modules/sway-xkb.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/modules/sway-xkb.c b/modules/sway-xkb.c index 83d4d10..d6960d9 100644 --- a/modules/sway-xkb.c +++ b/modules/sway-xkb.c @@ -175,6 +175,15 @@ handle_input_event(int type, const struct json_object *json, void *_mod) return false; const char *id = json_object_get_string(identifier); + + struct json_object *input_type; + if (!json_object_object_get_ex(obj, "type", &input_type)) + return false; + if (strcmp(json_object_get_string(input_type), "keyboard") != 0) { + LOG_DBG("ignoring non-keyboard input '%s'", id); + return true; + } + struct input *input = NULL; for (size_t i = 0; i < m->num_inputs; i++) { struct input *maybe_input = &m->inputs[i]; From aadb1b22b37d9f5f41c1cb2a27970bf2e0435174 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 1 May 2021 11:33:04 +0200 Subject: [PATCH 156/611] =?UTF-8?q?module/battery:=20don=E2=80=99t=20termi?= =?UTF-8?q?nate=20when=20failing=20to=20update?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some battery drivers will remove their sysfs directory when the battery goes from charging to discharging, or vice verse. This caused yambar’s battery module to terminate, resulting in the last known battery state to “freeze”. With this patch, failure to read the battery directory the *first* time is still considered a hard failure, resulting in an error message and then termination. However, subsequent failures, i.e. while polling the battery state, is *not* considered fatal; we simply don’t update the bar, and retry again the next poll interval. Error messages are still logged however. Closes #44 --- CHANGELOG.md | 3 +++ modules/battery.c | 5 ++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8910271..f2c7f53 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,9 @@ * fcft >= 2.4.0 is now required. * sway-xkb: non-keyboard inputs are now ignored (https://codeberg.org/dnkl/yambar/issues/51). +* battery: don’t terminate (causing last status to “freeze”) when + failing to update; retry again later + (https://codeberg.org/dnkl/yambar/issues/44). ### Deprecated diff --git a/modules/battery.c b/modules/battery.c index 7dcfc45..7b8ff5e 100644 --- a/modules/battery.c +++ b/modules/battery.c @@ -434,9 +434,8 @@ run(struct module *mod) continue; } - if (!update_status(mod)) - break; - bar->refresh(bar); + if (update_status(mod)) + bar->refresh(bar); } out: From 35e69435319939cac339a72e0dedc6fbebcfc9fb Mon Sep 17 00:00:00 2001 From: mz Date: Mon, 14 Jun 2021 19:02:01 +0200 Subject: [PATCH 157/611] Differentiate "Not Charging" and "Discharging" in state tag of battery module. Some batteries support charge thresholds and when the upper limit is set to a number less than 100 percent and it reaches that limit and it is connected to the charger the battery state will be "Not charging". It doesn't charge anymore despite it's not full. --- modules/battery.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/modules/battery.c b/modules/battery.c index 7b8ff5e..f5cc0f8 100644 --- a/modules/battery.c +++ b/modules/battery.c @@ -20,7 +20,7 @@ #include "../config-verify.h" #include "../plugin.h" -enum state { STATE_FULL, STATE_CHARGING, STATE_DISCHARGING }; +enum state { STATE_FULL, STATE_NOTCHARGING, STATE_CHARGING, STATE_DISCHARGING }; struct private { struct particle *label; @@ -65,6 +65,7 @@ content(struct module *mod) mtx_lock(&mod->lock); assert(m->state == STATE_FULL || + m->state == STATE_NOTCHARGING || m->state == STATE_CHARGING || m->state == STATE_DISCHARGING); @@ -79,7 +80,7 @@ content(struct module *mod) ? m->energy_full - m->energy : m->energy; double hours_as_float; - if (m->state == STATE_FULL) + if (m->state == STATE_FULL || m->state == STATE_NOTCHARGING) hours_as_float = 0.0; else if (m->power > 0) hours_as_float = (double)energy / m->power; @@ -93,7 +94,7 @@ content(struct module *mod) ? m->charge_full - m->charge : m->charge; double hours_as_float; - if (m->state == STATE_FULL) + if (m->state == STATE_FULL || m->state == STATE_NOTCHARGING) hours_as_float = 0.0; else if (m->current > 0) hours_as_float = (double)charge / m->current; @@ -117,6 +118,7 @@ content(struct module *mod) tag_new_string(mod, "model", m->model), tag_new_string(mod, "state", m->state == STATE_FULL ? "full" : + m->state == STATE_NOTCHARGING ? "not charging" : m->state == STATE_CHARGING ? "charging" : m->state == STATE_DISCHARGING ? "discharging" : "unknown"), @@ -349,12 +351,12 @@ update_status(struct module *mod) state = STATE_DISCHARGING; } else if (strcmp(status, "Full") == 0) state = STATE_FULL; + else if (strcmp(status, "Not charging") == 0) + state = STATE_NOTCHARGING; else if (strcmp(status, "Charging") == 0) state = STATE_CHARGING; else if (strcmp(status, "Discharging") == 0) state = STATE_DISCHARGING; - else if (strcmp(status, "Not charging") == 0) - state = STATE_DISCHARGING; else if (strcmp(status, "Unknown") == 0) state = STATE_DISCHARGING; else { From e2f3df87a3610e4246dc32902bc949be35260471 Mon Sep 17 00:00:00 2001 From: mzeinali Date: Mon, 14 Jun 2021 22:42:22 +0430 Subject: [PATCH 158/611] add changes in response to PR #58 comments --- CHANGELOG.md | 4 ++++ doc/yambar-modules-battery.5.scd | 2 +- examples/configurations/laptop.conf | 15 +++++++++++++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f2c7f53..fa0cc0b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,9 @@ * battery: don’t terminate (causing last status to “freeze”) when failing to update; retry again later (https://codeberg.org/dnkl/yambar/issues/44). +* battery: differentiate "Not Charging" and "Discharging" in state + tag of battery module. + (https://codeberg.org/dnkl/yambar/issues/57). ### Deprecated @@ -41,6 +44,7 @@ ### Contributors * [novakane](https://codeberg.org/novakane) +* [mz](https://codeberg.org/mz) ## 1.6.1 diff --git a/doc/yambar-modules-battery.5.scd b/doc/yambar-modules-battery.5.scd index 58fa6c6..42b5e00 100644 --- a/doc/yambar-modules-battery.5.scd +++ b/doc/yambar-modules-battery.5.scd @@ -24,7 +24,7 @@ uses *udev* to monitor for changes. : Battery model name | state : string -: One of *full*, *charging*, *discharging* or *unknown* +: One of *full*, *not charging*, *charging*, *discharging* or *unknown* | capacity : range : capacity left, in percent diff --git a/examples/configurations/laptop.conf b/examples/configurations/laptop.conf index b07ed8a..593d9f3 100644 --- a/examples/configurations/laptop.conf +++ b/examples/configurations/laptop.conf @@ -271,6 +271,21 @@ bar: full: - string: {text: , foreground: 00ff00ff, font: *awesome} - string: {text: "{capacity}% full"} + not charging: + - ramp: + tag: capacity + items: + - string: {text:  , foreground: ff0000ff, font: *awesome} + - string: {text:  , foreground: ffa600ff, font: *awesome} + - string: {text:  , foreground: 00ff00ff, font: *awesome} + - string: {text:  , foreground: 00ff00ff, font: *awesome} + - string: {text:  , foreground: 00ff00ff, font: *awesome} + - string: {text:  , foreground: 00ff00ff, font: *awesome} + - string: {text:  , foreground: 00ff00ff, font: *awesome} + - string: {text:  , foreground: 00ff00ff, font: *awesome} + - string: {text:  , foreground: 00ff00ff, font: *awesome} + - string: {text:  , foreground: 00ff00ff, font: *awesome} + - string: {text: "{capacity}%"} - clock: time-format: "%H:%M %Z" content: From 60ee992a733ac57d1ee466e1da658450341acedc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 19 Jun 2021 14:19:31 +0200 Subject: [PATCH 159/611] =?UTF-8?q?module/script:=20=E2=80=989=E2=80=99=20?= =?UTF-8?q?is=20a=20valid=20digit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The script module incorrectly rejected range tag end values containing the digit ‘9’. Closes #60 --- CHANGELOG.md | 2 ++ modules/script.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fa0cc0b..c78a2a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,8 @@ (https://codeberg.org/dnkl/yambar/issues/45). * Crash when a string particle contained `{}` (https://codeberg.org/dnkl/yambar/issues/48). +* `script` module rejecting range tag end values containing the digit + `9` (https://codeberg.org/dnkl/yambar/issues/60). ### Security diff --git a/modules/script.c b/modules/script.c index 7e07365..a938f7d 100644 --- a/modules/script.c +++ b/modules/script.c @@ -180,7 +180,7 @@ process_line(struct module *mod, const char *line, size_t len) long end = 0; for (size_t i = 0; i < end_len; i++) { - if (!(_end[i] >= '0' && _end[i] < '9')) { + if (!(_end[i] >= '0' && _end[i] <= '9')) { LOG_ERR( "tag range end is not an integer: %.*s", (int)end_len, _end); From d0dd65cef54e2d779f05c9a9ae5cea67d672b23a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 20 Jun 2021 21:14:12 +0200 Subject: [PATCH 160/611] =?UTF-8?q?module:=20add=20=E2=80=98description()?= =?UTF-8?q?=E2=80=99=20to=20the=20module=20interface?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This function is intended to return a description of this particular module instance. --- module.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/module.h b/module.h index b757a04..e525c87 100644 --- a/module.h +++ b/module.h @@ -26,6 +26,8 @@ struct module { /* refresh_in() should schedule a module content refresh after the * specified number of milliseconds */ bool (*refresh_in)(struct module *mod, long milli_seconds); + + const char *(*description)(struct module *mod); }; struct module *module_common_new(void); From 97d5570daf7f219d0a5469d4584ef6a173e6bf82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 20 Jun 2021 21:14:58 +0200 Subject: [PATCH 161/611] bar: set module thread titles using the new mod->description() --- bar/bar.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/bar/bar.c b/bar/bar.c index 526f80f..285adf0 100644 --- a/bar/bar.c +++ b/bar/bar.c @@ -2,12 +2,14 @@ #include "private.h" #include +#include #include #include #include #include #include #include +#include #include @@ -213,6 +215,20 @@ on_mouse(struct bar *_bar, enum mouse_event event, int x, int y) set_cursor(_bar, "left_ptr"); } +static void +set_module_thread_name(thrd_t id, struct module *mod) +{ + char title[16]; + if (mod->description != NULL) + snprintf(title, sizeof(title), "mod:%s", mod->description(mod)); + else + strncpy(title, "mod:", sizeof(title)); + + title[15] = '\0'; + + if (pthread_setname_np(id, title) < 0) + LOG_ERRNO("failed to set thread title"); +} static int run(struct bar *_bar) @@ -240,18 +256,21 @@ run(struct bar *_bar) mod->abort_fd = _bar->abort_fd; thrd_create(&thrd_left[i], (int (*)(void *))bar->left.mods[i]->run, mod); + set_module_thread_name(thrd_left[i], mod); } for (size_t i = 0; i < bar->center.count; i++) { struct module *mod = bar->center.mods[i]; mod->abort_fd = _bar->abort_fd; thrd_create(&thrd_center[i], (int (*)(void *))bar->center.mods[i]->run, mod); + set_module_thread_name(thrd_center[i], mod); } for (size_t i = 0; i < bar->right.count; i++) { struct module *mod = bar->right.mods[i]; mod->abort_fd = _bar->abort_fd; thrd_create(&thrd_right[i], (int (*)(void *))bar->right.mods[i]->run, mod); + set_module_thread_name(thrd_right[i], mod); } LOG_DBG("all modules started"); From ed2b8c48746165b7ed3ab2078d49d3b2b7b74d8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 20 Jun 2021 21:15:24 +0200 Subject: [PATCH 162/611] modules: implement description() --- modules/alsa.c | 10 ++++++++++ modules/backlight.c | 7 +++++++ modules/battery.c | 10 ++++++++++ modules/clock.c | 8 ++++++++ modules/i3.c | 7 +++++++ modules/label.c | 7 +++++++ modules/mpd.c | 7 +++++++ modules/network.c | 11 +++++++++++ modules/removables.c | 7 +++++++ modules/river.c | 7 +++++++ modules/script.c | 14 ++++++++++++++ modules/sway-xkb.c | 7 +++++++ modules/xkb.c | 7 +++++++ modules/xwindow.c | 7 +++++++ 14 files changed, 116 insertions(+) diff --git a/modules/alsa.c b/modules/alsa.c index 9e410df..3b71a25 100644 --- a/modules/alsa.c +++ b/modules/alsa.c @@ -39,6 +39,15 @@ destroy(struct module *mod) module_default_destroy(mod); } +static const char * +description(struct module *mod) +{ + static char desc[32]; + struct private *m = mod->private; + snprintf(desc, sizeof(desc), "alsa(%s)", m->card); + return desc; +} + static struct exposable * content(struct module *mod) { @@ -287,6 +296,7 @@ alsa_new(const char *card, const char *mixer, struct particle *label) mod->run = &run; mod->destroy = &destroy; mod->content = &content; + mod->description = &description; return mod; } diff --git a/modules/backlight.c b/modules/backlight.c index 053a998..e7bca2e 100644 --- a/modules/backlight.c +++ b/modules/backlight.c @@ -38,6 +38,12 @@ destroy(struct module *mod) module_default_destroy(mod); } +static const char * +description(struct module *mod) +{ + return "backlight"; +} + static struct exposable * content(struct module *mod) { @@ -216,6 +222,7 @@ backlight_new(const char *device, struct particle *label) mod->run = &run; mod->destroy = &destroy; mod->content = &content; + mod->description = &description; return mod; } diff --git a/modules/battery.c b/modules/battery.c index f5cc0f8..4a55141 100644 --- a/modules/battery.c +++ b/modules/battery.c @@ -57,6 +57,15 @@ destroy(struct module *mod) module_default_destroy(mod); } +static const char * +description(struct module *mod) +{ + static char desc[32]; + struct private *m = mod->private; + snprintf(desc, sizeof(desc), "bat(%s)", m->battery); + return desc; +} + static struct exposable * content(struct module *mod) { @@ -462,6 +471,7 @@ battery_new(const char *battery, struct particle *label, int poll_interval_secs) mod->run = &run; mod->destroy = &destroy; mod->content = &content; + mod->description = &description; return mod; } diff --git a/modules/clock.c b/modules/clock.c index e9f1694..b0db44e 100644 --- a/modules/clock.c +++ b/modules/clock.c @@ -35,6 +35,12 @@ destroy(struct module *mod) module_default_destroy(mod); } +static const char * +description(struct module *mod) +{ + return "clock"; +} + static struct exposable * content(struct module *mod) { @@ -60,6 +66,7 @@ content(struct module *mod) return exposable; } +#include static int run(struct module *mod) { @@ -161,6 +168,7 @@ clock_new(struct particle *label, const char *date_format, const char *time_form mod->run = &run; mod->destroy = &destroy; mod->content = &content; + mod->description = &description; return mod; } diff --git a/modules/i3.c b/modules/i3.c index e2f58de..5aa3d3b 100644 --- a/modules/i3.c +++ b/modules/i3.c @@ -608,6 +608,12 @@ ws_content_for_name(struct private *m, const char *name) return NULL; } +static const char * +description(struct module *mod) +{ + return "i3/sway"; +} + static struct exposable * content(struct module *mod) { @@ -710,6 +716,7 @@ i3_new(struct i3_workspaces workspaces[], size_t workspace_count, mod->run = &run; mod->destroy = &destroy; mod->content = &content; + mod->description = &description; return mod; } diff --git a/modules/label.c b/modules/label.c index a29b6bd..01fce76 100644 --- a/modules/label.c +++ b/modules/label.c @@ -21,6 +21,12 @@ destroy(struct module *mod) module_default_destroy(mod); } +static const char * +description(struct module *mod) +{ + return "label"; +} + static struct exposable * content(struct module *mod) { @@ -45,6 +51,7 @@ label_new(struct particle *label) mod->run = &run; mod->destroy = &destroy; mod->content = &content; + mod->description = &description; return mod; } diff --git a/modules/mpd.c b/modules/mpd.c index a501f76..bebd401 100644 --- a/modules/mpd.c +++ b/modules/mpd.c @@ -83,6 +83,12 @@ destroy(struct module *mod) module_default_destroy(mod); } +static const char * +description(struct module *mod) +{ + return "mpd"; +} + static uint64_t timespec_diff_milli_seconds(const struct timespec *a, const struct timespec *b) { @@ -588,6 +594,7 @@ mpd_new(const char *host, uint16_t port, struct particle *label) mod->destroy = &destroy; mod->content = &content; mod->refresh_in = &refresh_in; + mod->description = &description; return mod; } diff --git a/modules/network.c b/modules/network.c index 51839bf..c6b32af 100644 --- a/modules/network.c +++ b/modules/network.c @@ -66,6 +66,16 @@ destroy(struct module *mod) module_default_destroy(mod); } +static const char * +description(struct module *mod) +{ + static char desc[32]; + struct private *m = mod->private; + + snprintf(desc, sizeof(desc), "net(%s)", m->iface); + return desc; +} + static struct exposable * content(struct module *mod) { @@ -526,6 +536,7 @@ network_new(const char *iface, struct particle *label) mod->run = &run; mod->destroy = &destroy; mod->content = &content; + mod->description = &description; return mod; } diff --git a/modules/removables.c b/modules/removables.c index 498824b..4446f3d 100644 --- a/modules/removables.c +++ b/modules/removables.c @@ -97,6 +97,12 @@ destroy(struct module *mod) module_default_destroy(mod); } +static const char * +description(struct module *mod) +{ + return "removables"; +} + static struct exposable * content(struct module *mod) { @@ -596,6 +602,7 @@ removables_new(struct particle *label, int left_spacing, int right_spacing, mod->run = &run; mod->destroy = &destroy; mod->content = &content; + mod->description = &description; return mod; } diff --git a/modules/river.c b/modules/river.c index b6e3db1..95ed8d3 100644 --- a/modules/river.c +++ b/modules/river.c @@ -65,6 +65,12 @@ destroy(struct module *mod) module_default_destroy(mod); } +static const char * +description(struct module *mod) +{ + return "river"; +} + static struct exposable * content(struct module *mod) { @@ -645,6 +651,7 @@ river_new(struct particle *template, struct particle *title) mod->run = &run; mod->destroy = &destroy; mod->content = &content; + mod->description = &description; m->mod = mod; return mod; } diff --git a/modules/script.c b/modules/script.c index a938f7d..a034910 100644 --- a/modules/script.c +++ b/modules/script.c @@ -56,6 +56,19 @@ destroy(struct module *mod) module_default_destroy(mod); } +static const char * +description(struct module *mod) +{ + static char desc[32]; + struct private *m = mod->private; + + char *path = strdup(m->path); + snprintf(desc, sizeof(desc), "script(%s)", basename(path)); + + free(path); + return desc; +} + static struct exposable * content(struct module *mod) { @@ -569,6 +582,7 @@ script_new(const char *path, size_t argc, const char *const argv[static argc], mod->run = &run; mod->destroy = &destroy; mod->content = &content; + mod->description = &description; return mod; } diff --git a/modules/sway-xkb.c b/modules/sway-xkb.c index d6960d9..295b976 100644 --- a/modules/sway-xkb.c +++ b/modules/sway-xkb.c @@ -52,6 +52,12 @@ destroy(struct module *mod) module_default_destroy(mod); } +static const char * +description(struct module *mod) +{ + return "sway-xkb"; +} + static struct exposable * content(struct module *mod) { @@ -307,6 +313,7 @@ sway_xkb_new(struct particle *template, const char *identifiers[], mod->run = &run; mod->destroy = &destroy; mod->content = &content; + mod->description = &description; return mod; } diff --git a/modules/xkb.c b/modules/xkb.c index 16cc864..5c2c1f9 100644 --- a/modules/xkb.c +++ b/modules/xkb.c @@ -72,6 +72,12 @@ destroy(struct module *mod) module_default_destroy(mod); } +static const char * +description(struct module *mod) +{ + return "xkb"; +} + static struct exposable * content(struct module *mod) { @@ -650,6 +656,7 @@ xkb_new(struct particle *label) mod->run = &run; mod->destroy = &destroy; mod->content = &content; + mod->description = &description; return mod; } diff --git a/modules/xwindow.c b/modules/xwindow.c index ad856a4..3c8655e 100644 --- a/modules/xwindow.c +++ b/modules/xwindow.c @@ -36,6 +36,12 @@ struct private { xcb_window_t active_win; }; +static const char * +description(struct module *mod) +{ + return "xwindow"; +} + static void update_active_window(struct private *m) { @@ -332,6 +338,7 @@ xwindow_new(struct particle *label) mod->run = &run; mod->destroy = &destroy; mod->content = &content; + mod->description = &description; return mod; } From 371bfb4065f8c1f017855a8930c5c350f77f608c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 20 Jun 2021 21:21:59 +0200 Subject: [PATCH 163/611] =?UTF-8?q?bar:=20don=E2=80=99t=20prepend=20?= =?UTF-8?q?=E2=80=98mod:=E2=80=99=20to=20module=20thread=20titles?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bar/bar.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bar/bar.c b/bar/bar.c index 285adf0..0e51608 100644 --- a/bar/bar.c +++ b/bar/bar.c @@ -220,7 +220,7 @@ set_module_thread_name(thrd_t id, struct module *mod) { char title[16]; if (mod->description != NULL) - snprintf(title, sizeof(title), "mod:%s", mod->description(mod)); + strncpy(title, mod->description(mod), sizeof(title)); else strncpy(title, "mod:", sizeof(title)); From 08fa56a0f476eb688f8beed411871425d9e0ca2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 21 Jun 2021 18:16:22 +0200 Subject: [PATCH 164/611] =?UTF-8?q?bar:=20set=20thread=20name=20of=20the?= =?UTF-8?q?=20bar=E2=80=99s=20own=20thread?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bar/wayland.c | 3 +++ bar/xcb.c | 3 +++ 2 files changed, 6 insertions(+) diff --git a/bar/wayland.c b/bar/wayland.c index 2b218d7..1a72577 100644 --- a/bar/wayland.c +++ b/bar/wayland.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -1015,6 +1016,8 @@ loop(struct bar *_bar, struct private *bar = _bar->private; struct wayland_backend *backend = bar->backend.data; + pthread_setname_np(pthread_self(), "bar(wayland)"); + backend->bar_expose = expose; backend->bar_on_mouse = on_mouse; diff --git a/bar/xcb.c b/bar/xcb.c index f7a3ee1..99b787d 100644 --- a/bar/xcb.c +++ b/bar/xcb.c @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -316,6 +317,8 @@ loop(struct bar *_bar, struct private *bar = _bar->private; struct xcb_backend *backend = bar->backend.data; + pthread_setname_np(pthread_self(), "bar(xcb)"); + const int fd = xcb_get_file_descriptor(backend->conn); while (true) { From f5903112cd4db77c2c95890149af809daf8a4f8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 27 Jun 2021 10:51:32 +0200 Subject: [PATCH 165/611] external: wlr-protocols: bump --- external/wlr-layer-shell-unstable-v1.xml | 97 +++++++++++++++++++++--- external/wlr-protocols | 2 +- 2 files changed, 89 insertions(+), 10 deletions(-) diff --git a/external/wlr-layer-shell-unstable-v1.xml b/external/wlr-layer-shell-unstable-v1.xml index fa67001..d62fd51 100644 --- a/external/wlr-layer-shell-unstable-v1.xml +++ b/external/wlr-layer-shell-unstable-v1.xml @@ -25,7 +25,7 @@ THIS SOFTWARE. - + Clients can use this interface to assign the surface_layer role to wl_surfaces. Such surfaces are assigned to a "layer" of the output and @@ -47,6 +47,12 @@ or manipulate a buffer prior to the first layer_surface.configure call must also be treated as errors. + After creating a layer_surface object and setting it up, the client + must perform an initial commit without any buffer attached. + The compositor will reply with a layer_surface.configure event. + The client must acknowledge it and is then allowed to attach a buffer + to map the surface. + You may pass NULL for output to allow the compositor to decide which output to use. Generally this will be the one that the user most recently interacted with. @@ -94,7 +100,7 @@ - + An interface that may be implemented by a wl_surface, for surfaces that are designed to be rendered as a layer of a stacked desktop-like @@ -103,6 +109,14 @@ Layer surface state (layer, size, anchor, exclusive zone, margin, interactivity) is double-buffered, and will be applied at the time wl_surface.commit of the corresponding wl_surface is called. + + Attaching a null buffer to a layer surface unmaps it. + + Unmapping a layer_surface means that the surface cannot be shown by the + compositor until it is explicitly mapped again. The layer_surface + returns to the state it had right after layer_shell.get_layer_surface. + The client can re-map the surface by performing a commit without any + buffer attached, waiting for a configure event and handling it as usual. @@ -189,21 +203,85 @@ + + + Types of keyboard interaction possible for layer shell surfaces. The + rationale for this is twofold: (1) some applications are not interested + in keyboard events and not allowing them to be focused can improve the + desktop experience; (2) some applications will want to take exclusive + keyboard focus. + + + + + This value indicates that this surface is not interested in keyboard + events and the compositor should never assign it the keyboard focus. + + This is the default value, set for newly created layer shell surfaces. + + This is useful for e.g. desktop widgets that display information or + only have interaction with non-keyboard input devices. + + + + + Request exclusive keyboard focus if this surface is above the shell surface layer. + + For the top and overlay layers, the seat will always give + exclusive keyboard focus to the top-most layer which has keyboard + interactivity set to exclusive. If this layer contains multiple + surfaces with keyboard interactivity set to exclusive, the compositor + determines the one receiving keyboard events in an implementation- + defined manner. In this case, no guarantee is made when this surface + will receive keyboard focus (if ever). + + For the bottom and background layers, the compositor is allowed to use + normal focus semantics. + + This setting is mainly intended for applications that need to ensure + they receive all keyboard events, such as a lock screen or a password + prompt. + + + + + This requests the compositor to allow this surface to be focused and + unfocused by the user in an implementation-defined manner. The user + should be able to unfocus this surface even regardless of the layer + it is on. + + Typically, the compositor will want to use its normal mechanism to + manage keyboard focus between layer shell surfaces with this setting + and regular toplevels on the desktop layer (e.g. click to focus). + Nevertheless, it is possible for a compositor to require a special + interaction to focus or unfocus layer shell surfaces (e.g. requiring + a click even if focus follows the mouse normally, or providing a + keybinding to switch focus between layers). + + This setting is mainly intended for desktop shell components (e.g. + panels) that allow keyboard interaction. Using this option can allow + implementing a desktop shell that can be fully usable without the + mouse. + + + + - Set to 1 to request that the seat send keyboard events to this layer - surface. For layers below the shell surface layer, the seat will use - normal focus semantics. For layers above the shell surface layers, the - seat will always give exclusive keyboard focus to the top-most layer - which has keyboard interactivity set to true. + Set how keyboard events are delivered to this surface. By default, + layer shell surfaces do not receive keyboard events; this request can + be used to change this. + + This setting is inherited by child surfaces set by the get_popup + request. Layer surfaces receive pointer, touch, and tablet events normally. If you do not want to receive them, set the input region on your surface to an empty region. - Events is double-buffered, see wl_surface.commit. + Keyboard interactivity is double-buffered, see wl_surface.commit. - + @@ -288,6 +366,7 @@ + diff --git a/external/wlr-protocols b/external/wlr-protocols index 16a2888..d1598e8 160000 --- a/external/wlr-protocols +++ b/external/wlr-protocols @@ -1 +1 @@ -Subproject commit 16a28885bc92869d8e589e725e7bf018432c47e4 +Subproject commit d1598e82240d6e8ca57729495a94d4e11d222033 From 7c6874d8267d031857ceeee473daa37ac00806b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 1 Jul 2021 17:14:26 +0200 Subject: [PATCH 166/611] module/river: disable debug log output --- modules/river.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/river.c b/modules/river.c index 95ed8d3..6fdeb85 100644 --- a/modules/river.c +++ b/modules/river.c @@ -8,7 +8,7 @@ #include #define LOG_MODULE "river" -#define LOG_ENABLE_DBG 1 +#define LOG_ENABLE_DBG 0 #include "../log.h" #include "../plugin.h" #include "../particles/dynlist.h" From 8f7ef7c20bffeeff12856c1d08fdcc28dda03ad5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 1 Jul 2021 17:14:47 +0200 Subject: [PATCH 167/611] =?UTF-8?q?module/river:=20don=E2=80=99t=20refresh?= =?UTF-8?q?=20the=20bar=20unless=20there=20are=20any=20actual=20changes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/river.c | 49 ++++++++++++++++++++++++++++--------------------- 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/modules/river.c b/modules/river.c index 6fdeb85..0cdab38 100644 --- a/modules/river.c +++ b/modules/river.c @@ -190,14 +190,15 @@ focused_tags(void *data, struct zriver_output_status_v1 *zriver_output_status_v1 uint32_t tags) { struct output *output = data; - struct module *mod = output->m->mod; + + if (output->focused == tags) + return; LOG_DBG("output: %s: focused tags: 0x%08x", output->name, tags); + struct module *mod = output->m->mod; mtx_lock(&mod->lock); - { - output->focused = tags; - } + output->focused = tags; mtx_unlock(&mod->lock); mod->bar->refresh(mod->bar); } @@ -316,25 +317,25 @@ focused_output(void *data, struct zriver_seat_status_v1 *zriver_seat_status_v1, struct private *m = seat->m; struct module *mod = m->mod; - mtx_lock(&mod->lock); - { - struct output *output = NULL; - tll_foreach(m->outputs, it) { - if (it->item.wl_output == wl_output) { - output = &it->item; - break; - } + struct output *output = NULL; + tll_foreach(m->outputs, it) { + if (it->item.wl_output == wl_output) { + output = &it->item; + break; } - - LOG_DBG("seat: %s: focused output: %s", seat->name, output != NULL ? output->name : ""); - - if (output == NULL) - LOG_WARN("seat: %s: couldn't find output we are mapped on", seat->name); - - seat->output = output; } - mtx_unlock(&mod->lock); - mod->bar->refresh(mod->bar); + + LOG_DBG("seat: %s: focused output: %s", seat->name, output != NULL ? output->name : ""); + + if (output == NULL) + LOG_WARN("seat: %s: couldn't find output we are mapped on", seat->name); + + if (seat->output != output) { + mtx_lock(&mod->lock); + seat->output = output; + mtx_unlock(&mod->lock); + mod->bar->refresh(mod->bar); + } } static void @@ -373,6 +374,12 @@ focused_view(void *data, struct zriver_seat_status_v1 *zriver_seat_status_v1, struct seat *seat = data; struct module *mod = seat->m->mod; + if (seat->title == NULL && title == NULL) + return; + + if (seat->title != NULL && title != NULL && strcmp(seat->title, title) == 0) + return; + LOG_DBG("seat: %s: focused view: %s", seat->name, title); mtx_lock(&mod->lock); From dd724d1bc284dc42ebae1e3625c5be5c8985c4db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 21 Jun 2021 21:00:07 +0200 Subject: [PATCH 168/611] =?UTF-8?q?exposable:=20add=20=E2=80=98btn?= =?UTF-8?q?=E2=80=99=20argument=20to=20on=5Fmouse()?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bar/backend.h | 2 +- bar/bar.c | 9 +++++---- bar/wayland.c | 23 +++++++++++++++++++---- bar/xcb.c | 13 ++++++++++--- particle.c | 7 ++++--- particle.h | 11 +++++++++-- particles/dynlist.c | 8 ++++---- particles/list.c | 8 ++++---- particles/map.c | 8 ++++---- particles/progress-bar.c | 10 +++++----- particles/ramp.c | 8 ++++---- 11 files changed, 69 insertions(+), 38 deletions(-) diff --git a/bar/backend.h b/bar/backend.h index f2681a8..d365da6 100644 --- a/bar/backend.h +++ b/bar/backend.h @@ -10,7 +10,7 @@ struct backend { void (*loop)(struct bar *bar, void (*expose)(const struct bar *bar), void (*on_mouse)(struct bar *bar, enum mouse_event event, - int x, int y)); + enum mouse_button btn, int x, int y)); void (*commit)(const struct bar *bar); void (*refresh)(const struct bar *bar); void (*set_cursor)(struct bar *bar, const char *cursor); diff --git a/bar/bar.c b/bar/bar.c index 0e51608..b7557fd 100644 --- a/bar/bar.c +++ b/bar/bar.c @@ -151,7 +151,8 @@ set_cursor(struct bar *bar, const char *cursor) } static void -on_mouse(struct bar *_bar, enum mouse_event event, int x, int y) +on_mouse(struct bar *_bar, enum mouse_event event, enum mouse_button btn, + int x, int y) { struct private *bar = _bar->private; @@ -173,7 +174,7 @@ on_mouse(struct bar *_bar, enum mouse_event event, int x, int y) mx += bar->left_spacing; if (x >= mx && x < mx + e->width) { if (e->on_mouse != NULL) - e->on_mouse(e, _bar, event, x - mx, y); + e->on_mouse(e, _bar, event, btn, x - mx, y); return; } @@ -187,7 +188,7 @@ on_mouse(struct bar *_bar, enum mouse_event event, int x, int y) mx += bar->left_spacing; if (x >= mx && x < mx + e->width) { if (e->on_mouse != NULL) - e->on_mouse(e, _bar, event, x - mx, y); + e->on_mouse(e, _bar, event, btn, x - mx, y); return; } @@ -205,7 +206,7 @@ on_mouse(struct bar *_bar, enum mouse_event event, int x, int y) mx += bar->left_spacing; if (x >= mx && x < mx + e->width) { if (e->on_mouse != NULL) - e->on_mouse(e, _bar, event, x - mx, y); + e->on_mouse(e, _bar, event, btn, x - mx, y); return; } diff --git a/bar/wayland.c b/bar/wayland.c index 1a72577..05a12a9 100644 --- a/bar/wayland.c +++ b/bar/wayland.c @@ -10,6 +10,7 @@ #include #include +#include #include #include @@ -113,7 +114,8 @@ struct wayland_backend { struct buffer *pending_buffer; /* Finished, but not yet rendered */ void (*bar_expose)(const struct bar *bar); - void (*bar_on_mouse)(struct bar *bar, enum mouse_event event, int x, int y); + void (*bar_on_mouse)(struct bar *bar, enum mouse_event event, + enum mouse_button btn, int x, int y); }; static void @@ -262,7 +264,8 @@ wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, backend->active_seat = seat; backend->bar_on_mouse( - backend->bar, ON_MOUSE_MOTION, seat->pointer.x, seat->pointer.y); + backend->bar, ON_MOUSE_MOTION, MOUSE_BTN_NONE, + seat->pointer.x, seat->pointer.y); } static void @@ -276,8 +279,19 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer, struct wayland_backend *backend = seat->backend; backend->active_seat = seat; + + enum mouse_button btn; + + switch (button) { + case BTN_LEFT: btn = MOUSE_BTN_LEFT; break; + case BTN_MIDDLE: btn = MOUSE_BTN_MIDDLE; break; + case BTN_RIGHT: btn = MOUSE_BTN_RIGHT; break; + default: + return; + } + backend->bar_on_mouse( - backend->bar, ON_MOUSE_CLICK, seat->pointer.x, seat->pointer.y); + backend->bar, ON_MOUSE_CLICK, btn, seat->pointer.x, seat->pointer.y); } static void @@ -1011,7 +1025,8 @@ cleanup(struct bar *_bar) static void loop(struct bar *_bar, void (*expose)(const struct bar *bar), - void (*on_mouse)(struct bar *bar, enum mouse_event event, int x, int y)) + void (*on_mouse)(struct bar *bar, enum mouse_event event, + enum mouse_button btn, int x, int y)) { struct private *bar = _bar->private; struct wayland_backend *backend = bar->backend.data; diff --git a/bar/xcb.c b/bar/xcb.c index 99b787d..6229bc5 100644 --- a/bar/xcb.c +++ b/bar/xcb.c @@ -312,7 +312,8 @@ cleanup(struct bar *_bar) static void loop(struct bar *_bar, void (*expose)(const struct bar *bar), - void (*on_mouse)(struct bar *bar, enum mouse_event event, int x, int y)) + void (*on_mouse)(struct bar *bar, enum mouse_event event, + enum mouse_button btn, int x, int y)) { struct private *bar = _bar->private; struct xcb_backend *backend = bar->backend.data; @@ -357,7 +358,7 @@ loop(struct bar *_bar, case XCB_MOTION_NOTIFY: { const xcb_motion_notify_event_t *evt = (void *)e; - on_mouse(_bar, ON_MOUSE_MOTION, evt->event_x, evt->event_y); + on_mouse(_bar, ON_MOUSE_MOTION, MOUSE_BTN_NONE, evt->event_x, evt->event_y); break; } @@ -366,7 +367,13 @@ loop(struct bar *_bar, case XCB_BUTTON_RELEASE: { const xcb_button_release_event_t *evt = (void *)e; - on_mouse(_bar, ON_MOUSE_CLICK, evt->event_x, evt->event_y); + + switch (evt->detail) { + case 1: case 2: case 3: + on_mouse(_bar, ON_MOUSE_CLICK, + evt->detail, evt->event_x, evt->event_y); + break; + } break; } diff --git a/particle.c b/particle.c index ffe330f..2ce4800 100644 --- a/particle.c +++ b/particle.c @@ -141,10 +141,11 @@ err: void exposable_default_on_mouse(struct exposable *exposable, struct bar *bar, - enum mouse_event event, int x, int y) + enum mouse_event event, enum mouse_button btn, + int x, int y) { - LOG_DBG("on_mouse: exposable=%p, event=%s, x=%d, y=%d (on-click=%s)", - exposable, event == ON_MOUSE_MOTION ? "motion" : "click", x, y, + LOG_DBG("on_mouse: exposable=%p, event=%s, btn=%d, x=%d, y=%d (on-click=%s)", + exposable, event == ON_MOUSE_MOTION ? "motion" : "click", btn, x, y, exposable->on_click); /* If we have a handler, change cursor to a hand */ diff --git a/particle.h b/particle.h index e89b9c4..1fc582a 100644 --- a/particle.h +++ b/particle.h @@ -29,6 +29,13 @@ enum mouse_event { ON_MOUSE_CLICK, }; +enum mouse_button { + MOUSE_BTN_NONE, + MOUSE_BTN_LEFT, + MOUSE_BTN_MIDDLE, + MOUSE_BTN_RIGHT, +}; + struct exposable { const struct particle *particle; void *private; @@ -42,7 +49,7 @@ struct exposable { int x, int y, int height); void (*on_mouse)(struct exposable *exposable, struct bar *bar, - enum mouse_event event, int x, int y); + enum mouse_event event, enum mouse_button btn, int x, int y); }; struct particle *particle_common_new( @@ -59,7 +66,7 @@ void exposable_render_deco( void exposable_default_on_mouse( struct exposable *exposable, struct bar *bar, - enum mouse_event event, int x, int y); + enum mouse_event event, enum mouse_button btn, int x, int y); /* List of attributes *all* particles implement */ #define PARTICLE_COMMON_ATTRS \ diff --git a/particles/dynlist.c b/particles/dynlist.c index c04d610..00bb41f 100644 --- a/particles/dynlist.c +++ b/particles/dynlist.c @@ -67,13 +67,13 @@ dynlist_expose(const struct exposable *exposable, pixman_image_t *pix, int x, in static void on_mouse(struct exposable *exposable, struct bar *bar, - enum mouse_event event, int x, int y) + enum mouse_event event, enum mouse_button btn, int x, int y) { //const struct particle *p = exposable->particle; const struct private *e = exposable->private; if (exposable->on_click != NULL) { - exposable_default_on_mouse(exposable, bar, event, x, y); + exposable_default_on_mouse(exposable, bar, event, btn, x, y); return; } @@ -82,7 +82,7 @@ on_mouse(struct exposable *exposable, struct bar *bar, if (x >= px && x < px + e->exposables[i]->width) { if (e->exposables[i]->on_mouse != NULL) { e->exposables[i]->on_mouse( - e->exposables[i], bar, event, x - px, y); + e->exposables[i], bar, event, btn, x - px, y); } return; } @@ -91,7 +91,7 @@ on_mouse(struct exposable *exposable, struct bar *bar, } LOG_DBG("on_mouse missed all sub-particles"); - exposable_default_on_mouse(exposable, bar, event, x, y); + exposable_default_on_mouse(exposable, bar, event, btn, x, y); } struct exposable * diff --git a/particles/list.c b/particles/list.c index 6f51152..f9e61f6 100644 --- a/particles/list.c +++ b/particles/list.c @@ -75,14 +75,14 @@ expose(const struct exposable *exposable, pixman_image_t *pix, int x, int y, int static void on_mouse(struct exposable *exposable, struct bar *bar, - enum mouse_event event, int x, int y) + enum mouse_event event, enum mouse_button btn, int x, int y) { const struct particle *p = exposable->particle; const struct eprivate *e = exposable->private; if (exposable->on_click != NULL) { /* We have our own handler */ - exposable_default_on_mouse(exposable, bar, event, x, y); + exposable_default_on_mouse(exposable, bar, event, btn, x, y); return; } @@ -91,7 +91,7 @@ on_mouse(struct exposable *exposable, struct bar *bar, if (x >= px && x < px + e->exposables[i]->width) { if (e->exposables[i]->on_mouse != NULL) { e->exposables[i]->on_mouse( - e->exposables[i], bar, event, x - px, y); + e->exposables[i], bar, event, btn, x - px, y); } return; } @@ -100,7 +100,7 @@ on_mouse(struct exposable *exposable, struct bar *bar, } /* We're between sub-particles (or in the left/right margin) */ - exposable_default_on_mouse(exposable, bar, event, x, y); + exposable_default_on_mouse(exposable, bar, event, btn, x, y); } static struct exposable * diff --git a/particles/map.c b/particles/map.c index 57bb0fb..cc26f77 100644 --- a/particles/map.c +++ b/particles/map.c @@ -61,26 +61,26 @@ expose(const struct exposable *exposable, pixman_image_t *pix, int x, int y, int static void on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event, - int x, int y) + enum mouse_button btn, int x, int y) { const struct particle *p = exposable->particle; const struct eprivate *e = exposable->private; if (exposable->on_click != NULL) { /* We have our own handler */ - exposable_default_on_mouse(exposable, bar, event, x, y); + exposable_default_on_mouse(exposable, bar, event, btn, x, y); return; } int px = p->left_margin; if (x >= px && x < px + e->exposable->width) { if (e->exposable->on_mouse != NULL) - e->exposable->on_mouse(e->exposable, bar, event, x - px, y); + e->exposable->on_mouse(e->exposable, bar, event, btn, x - px, y); return; } /* In the left- or right margin */ - exposable_default_on_mouse(exposable, bar, event, x, y); + exposable_default_on_mouse(exposable, bar, event, btn, x, y); } static struct exposable * diff --git a/particles/progress-bar.c b/particles/progress-bar.c index ad5c4cd..62a58b9 100644 --- a/particles/progress-bar.c +++ b/particles/progress-bar.c @@ -85,10 +85,10 @@ expose(const struct exposable *exposable, pixman_image_t *pix, int x, int y, int static void on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event, - int x, int y) + enum mouse_button btn, int x, int y) { if (exposable->on_click == NULL) { - exposable_default_on_mouse(exposable, bar, event, x, y); + exposable_default_on_mouse(exposable, bar, event, btn, x, y); return; } @@ -120,7 +120,7 @@ on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event, /* Mouse is over the start-marker */ struct exposable *start = e->exposables[0]; if (start->on_mouse != NULL) - start->on_mouse(start, bar, event, x - p->left_margin, y); + start->on_mouse(start, bar, event, btn, x - p->left_margin, y); } else { /* Mouse if over left margin */ bar->set_cursor(bar, "left_ptr"); @@ -139,7 +139,7 @@ on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event, /* Mouse is over the end-marker */ struct exposable *end = e->exposables[e->count - 1]; if (end->on_mouse != NULL) - end->on_mouse(end, bar, event, x - x_offset - clickable_width, y); + end->on_mouse(end, bar, event, btn, x - x_offset - clickable_width, y); } else { /* Mouse is over the right margin */ bar->set_cursor(bar, "left_ptr"); @@ -165,7 +165,7 @@ on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event, } /* Call default implementation, which will execute our handler */ - exposable_default_on_mouse(exposable, bar, event, x, y); + exposable_default_on_mouse(exposable, bar, event, btn, x, y); if (event == ON_MOUSE_CLICK) { /* Reset handler string */ diff --git a/particles/ramp.c b/particles/ramp.c index 9a7f06e..8087217 100644 --- a/particles/ramp.c +++ b/particles/ramp.c @@ -57,26 +57,26 @@ expose(const struct exposable *exposable, pixman_image_t *pix, int x, int y, int static void on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event, - int x, int y) + enum mouse_button btn, int x, int y) { const struct particle *p = exposable->particle; const struct eprivate *e = exposable->private; if (exposable->on_click != NULL) { /* We have our own handler */ - exposable_default_on_mouse(exposable, bar, event, x, y); + exposable_default_on_mouse(exposable, bar, event, btn, x, y); return; } int px = p->left_margin; if (x >= px && x < px + e->exposable->width) { if (e->exposable->on_mouse != NULL) - e->exposable->on_mouse(e->exposable, bar, event, x - px, y); + e->exposable->on_mouse(e->exposable, bar, event, btn, x - px, y); return; } /* In the left- or right margin */ - exposable_default_on_mouse(exposable, bar, event, x, y); + exposable_default_on_mouse(exposable, bar, event, btn, x, y); } static void From af163d3f77667b5eb1589c2ec84bea5172777d45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 21 Jun 2021 21:02:31 +0200 Subject: [PATCH 169/611] exposable: log button name in debug log --- particle.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/particle.c b/particle.c index 2ce4800..ad76965 100644 --- a/particle.c +++ b/particle.c @@ -144,9 +144,10 @@ exposable_default_on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event, enum mouse_button btn, int x, int y) { - LOG_DBG("on_mouse: exposable=%p, event=%s, btn=%d, x=%d, y=%d (on-click=%s)", - exposable, event == ON_MOUSE_MOTION ? "motion" : "click", btn, x, y, - exposable->on_click); + LOG_DBG("on_mouse: exposable=%p, event=%s, btn=%s, x=%d, y=%d (on-click=%s)", + exposable, event == ON_MOUSE_MOTION ? "motion" : "click", + btn == MOUSE_BTN_NONE ? "none" : btn == MOUSE_BTN_LEFT ? "left" : btn == MOUSE_BTN_MIDDLE ? "middle" : "right", + x, y, exposable->on_click); /* If we have a handler, change cursor to a hand */ bar->set_cursor(bar, exposable->on_click == NULL ? "left_ptr" : "hand2"); From c79ffbe057f8d9d664f960e262185cc64e9ec195 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 22 Jun 2021 19:04:13 +0200 Subject: [PATCH 170/611] Add support binding on-click handlers to other buttons than LEFT One can now bind the left/middle/right mouse buttons to on-click. In fact, you can have all three buttons bound to different handlers for the same particle. The new syntax is on-click: left: middle: right: Leaving one out is the same thing as not mapping it at all. Furthermore, on-click: is still valid, and is a shorthand for on-click: left: --- bar/wayland.c | 27 +++++++++-------- config-verify.c | 18 ++++++++++++ config-verify.h | 1 + config.c | 31 ++++++++++++++++++-- particle.c | 63 +++++++++++++++++++++++++++++----------- particle.h | 47 ++++++++++++++++-------------- particles/dynlist.c | 2 +- particles/empty.c | 6 +--- particles/list.c | 8 ++--- particles/map.c | 7 ++--- particles/progress-bar.c | 19 +++++++----- particles/ramp.c | 7 ++--- particles/string.c | 6 +--- tag.c | 8 +++++ tag.h | 3 ++ 15 files changed, 163 insertions(+), 90 deletions(-) diff --git a/bar/wayland.c b/bar/wayland.c index 05a12a9..1124ab2 100644 --- a/bar/wayland.c +++ b/bar/wayland.c @@ -272,26 +272,25 @@ static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state) { - if (state != WL_POINTER_BUTTON_STATE_PRESSED) - return; - struct seat *seat = data; struct wayland_backend *backend = seat->backend; - backend->active_seat = seat; + if (state == WL_POINTER_BUTTON_STATE_PRESSED) + backend->active_seat = seat; + else { + enum mouse_button btn; - enum mouse_button btn; + switch (button) { + case BTN_LEFT: btn = MOUSE_BTN_LEFT; break; + case BTN_MIDDLE: btn = MOUSE_BTN_MIDDLE; break; + case BTN_RIGHT: btn = MOUSE_BTN_RIGHT; break; + default: + return; + } - switch (button) { - case BTN_LEFT: btn = MOUSE_BTN_LEFT; break; - case BTN_MIDDLE: btn = MOUSE_BTN_MIDDLE; break; - case BTN_RIGHT: btn = MOUSE_BTN_RIGHT; break; - default: - return; + backend->bar_on_mouse( + backend->bar, ON_MOUSE_CLICK, btn, seat->pointer.x, seat->pointer.y); } - - backend->bar_on_mouse( - backend->bar, ON_MOUSE_CLICK, btn, seat->pointer.x, seat->pointer.y); } static void diff --git a/config-verify.c b/config-verify.c index 7e87d42..6a5278b 100644 --- a/config-verify.c +++ b/config-verify.c @@ -152,6 +152,24 @@ conf_verify_dict(keychain_t *chain, const struct yml_node *node, return true; } +bool +conf_verify_on_click(keychain_t *chain, const struct yml_node *node) +{ + /* on-click: */ + const char *s = yml_value_as_string(node); + if (s != NULL) + return true; + + static const struct attr_info info[] = { + {"left", false, &conf_verify_string}, + {"middle", false, &conf_verify_string}, + {"right", false, &conf_verify_string}, + {NULL, false, NULL}, + }; + + return conf_verify_dict(chain, node, info); +} + bool conf_verify_color(keychain_t *chain, const struct yml_node *node) { diff --git a/config-verify.h b/config-verify.h index 44a6a66..dccaf5f 100644 --- a/config-verify.h +++ b/config-verify.h @@ -40,6 +40,7 @@ bool conf_verify_list(keychain_t *chain, const struct yml_node *node, bool conf_verify_dict(keychain_t *chain, const struct yml_node *node, const struct attr_info info[]); /* NULL-terminated list */ +bool conf_verify_on_click(keychain_t *chain, const struct yml_node *node); bool conf_verify_color(keychain_t *chain, const struct yml_node *node); bool conf_verify_font(keychain_t *chain, const struct yml_node *node); diff --git a/config.c b/config.c index aaa62f9..b2e131c 100644 --- a/config.c +++ b/config.c @@ -139,8 +139,33 @@ conf_to_particle(const struct yml_node *node, struct conf_inherit inherited) int right = margin != NULL ? yml_value_as_int(margin) : right_margin != NULL ? yml_value_as_int(right_margin) : 0; - const char *on_click_template - = on_click != NULL ? yml_value_as_string(on_click) : NULL; + const char *on_click_templates[MOUSE_BTN_COUNT] = {NULL}; + if (on_click != NULL) { + const char *legacy = yml_value_as_string(on_click); + + if (legacy != NULL) + on_click_templates[MOUSE_BTN_LEFT] = legacy; + + if (yml_is_dict(on_click)) { + for (struct yml_dict_iter it = yml_dict_iter(on_click); + it.key != NULL; + yml_dict_next(&it)) + { + const char *key = yml_value_as_string(it.key); + const char *template = yml_value_as_string(it.value); + + if (strcmp(key, "left") == 0) + on_click_templates[MOUSE_BTN_LEFT] = template; + else if (strcmp(key, "middle") == 0) + on_click_templates[MOUSE_BTN_MIDDLE] = template; + else if (strcmp(key, "right") == 0) + on_click_templates[MOUSE_BTN_RIGHT] = template; + else + assert(false); + } + } + } + struct deco *deco = deco_node != NULL ? conf_to_deco(deco_node) : NULL; /* @@ -159,7 +184,7 @@ conf_to_particle(const struct yml_node *node, struct conf_inherit inherited) /* Instantiate base/common particle */ struct particle *common = particle_common_new( - left, right, on_click_template, font, foreground, deco); + left, right, on_click_templates, font, foreground, deco); const struct particle_iface *iface = plugin_load_particle(type); diff --git a/particle.c b/particle.c index ad76965..ea4bb62 100644 --- a/particle.c +++ b/particle.c @@ -22,31 +22,41 @@ particle_default_destroy(struct particle *particle) if (particle->deco != NULL) particle->deco->destroy(particle->deco); fcft_destroy(particle->font); - free(particle->on_click_template); + for (size_t i = 0; i < MOUSE_BTN_COUNT; i++) + free(particle->on_click_templates[i]); free(particle); } struct particle * particle_common_new(int left_margin, int right_margin, - const char *on_click_template, + const char **on_click_templates, struct fcft_font *font, pixman_color_t foreground, struct deco *deco) { struct particle *p = calloc(1, sizeof(*p)); p->left_margin = left_margin; p->right_margin = right_margin; - p->on_click_template = - on_click_template != NULL ? strdup(on_click_template) : NULL; p->foreground = foreground; p->font = font; p->deco = deco; + + if (on_click_templates != NULL) { + for (size_t i = 0; i < MOUSE_BTN_COUNT; i++) { + if (on_click_templates[i] != NULL) { + p->have_on_click_template = true; + p->on_click_templates[i] = strdup(on_click_templates[i]); + } + } + } + return p; } void exposable_default_destroy(struct exposable *exposable) { - free(exposable->on_click); + for (size_t i = 0; i < MOUSE_BTN_COUNT; i++) + free(exposable->on_click[i]); free(exposable); } @@ -144,19 +154,32 @@ exposable_default_on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event, enum mouse_button btn, int x, int y) { +#if defined(LOG_ENABLE_DBG) && LOG_ENABLE_DBG + static const char *button_name[] = { + [MOUSE_BTN_NONE] = "none", + [MOUSE_BTN_LEFT] = "left", + [MOUSE_BTN_MIDDLE] = "middle", + [MOUSE_BTN_RIGHT] = "right", + [MOUSE_BTN_COUNT] = "count", + }; LOG_DBG("on_mouse: exposable=%p, event=%s, btn=%s, x=%d, y=%d (on-click=%s)", exposable, event == ON_MOUSE_MOTION ? "motion" : "click", - btn == MOUSE_BTN_NONE ? "none" : btn == MOUSE_BTN_LEFT ? "left" : btn == MOUSE_BTN_MIDDLE ? "middle" : "right", - x, y, exposable->on_click); + button_name[btn], x, y, exposable->on_click[btn]); +#endif /* If we have a handler, change cursor to a hand */ - bar->set_cursor(bar, exposable->on_click == NULL ? "left_ptr" : "hand2"); + const char *cursor = + (exposable->particle != NULL && + exposable->particle->have_on_click_template) + ? "hand2" + : "left_ptr"; + bar->set_cursor(bar, cursor); /* If this is a mouse click, and we have a handler, execute it */ - if (exposable->on_click != NULL && event == ON_MOUSE_CLICK) { + if (exposable->on_click[btn] != NULL && event == ON_MOUSE_CLICK) { /* Need a writeable copy, whose scope *we* control */ - char *cmd = strdup(exposable->on_click); - LOG_DBG("cmd = \"%s\"", exposable->on_click); + char *cmd = strdup(exposable->on_click[btn]); + LOG_DBG("cmd = \"%s\"", exposable->on_click[btn]); char **argv; if (!tokenize_cmdline(cmd, &argv)) { @@ -174,15 +197,15 @@ exposable_default_on_mouse(struct exposable *exposable, struct bar *bar, int wstatus; if (waitpid(pid, &wstatus, 0) == -1) - LOG_ERRNO("%s: failed to wait for on_click handler", exposable->on_click); + LOG_ERRNO("%s: failed to wait for on_click handler", exposable->on_click[btn]); if (WIFEXITED(wstatus)) { if (WEXITSTATUS(wstatus) != 0) - LOG_ERRNO_P("%s: failed to execute", WEXITSTATUS(wstatus), exposable->on_click); + LOG_ERRNO_P("%s: failed to execute", WEXITSTATUS(wstatus), exposable->on_click[btn]); } else - LOG_ERR("%s: did not exit normally", exposable->on_click); + LOG_ERR("%s: did not exit normally", exposable->on_click[btn]); - LOG_DBG("%s: launched", exposable->on_click); + LOG_DBG("%s: launched", exposable->on_click[btn]); } else { /* * Use a pipe with O_CLOEXEC to communicate exec() failure @@ -283,11 +306,17 @@ exposable_default_on_mouse(struct exposable *exposable, struct bar *bar, } struct exposable * -exposable_common_new(const struct particle *particle, const char *on_click) +exposable_common_new(const struct particle *particle, const struct tag_set *tags) { struct exposable *exposable = calloc(1, sizeof(*exposable)); exposable->particle = particle; - exposable->on_click = on_click != NULL ? strdup(on_click) : NULL; + + if (particle != NULL && particle->have_on_click_template) { + tags_expand_templates( + exposable->on_click, + (const char **)particle->on_click_templates, + MOUSE_BTN_COUNT, tags); + } exposable->destroy = &exposable_default_destroy; exposable->on_mouse = &exposable_default_on_mouse; return exposable; diff --git a/particle.h b/particle.h index 1fc582a..4e449f3 100644 --- a/particle.h +++ b/particle.h @@ -7,23 +7,6 @@ #include "decoration.h" #include "tag.h" -struct bar; - -struct particle { - void *private; - - int left_margin, right_margin; - char *on_click_template; - - pixman_color_t foreground; - struct fcft_font *font; - struct deco *deco; - - void (*destroy)(struct particle *particle); - struct exposable *(*instantiate)(const struct particle *particle, - const struct tag_set *tags); -}; - enum mouse_event { ON_MOUSE_MOTION, ON_MOUSE_CLICK, @@ -34,14 +17,36 @@ enum mouse_button { MOUSE_BTN_LEFT, MOUSE_BTN_MIDDLE, MOUSE_BTN_RIGHT, + + MOUSE_BTN_COUNT, }; +struct bar; + +struct particle { + void *private; + + int left_margin, right_margin; + + bool have_on_click_template; + char *on_click_templates[MOUSE_BTN_COUNT]; + + pixman_color_t foreground; + struct fcft_font *font; + struct deco *deco; + + void (*destroy)(struct particle *particle); + struct exposable *(*instantiate)(const struct particle *particle, + const struct tag_set *tags); +}; + + struct exposable { const struct particle *particle; void *private; int width; /* Should be set by begin_expose(), at latest */ - char *on_click; + char *on_click[MOUSE_BTN_COUNT]; void (*destroy)(struct exposable *exposable); int (*begin_expose)(struct exposable *exposable); @@ -53,13 +58,13 @@ struct exposable { }; struct particle *particle_common_new( - int left_margin, int right_margin, const char *on_click_template, + int left_margin, int right_margin, const char *on_click_templates[], struct fcft_font *font, pixman_color_t foreground, struct deco *deco); void particle_default_destroy(struct particle *particle); struct exposable *exposable_common_new( - const struct particle *particle, const char *on_click); + const struct particle *particle, const struct tag_set *tags); void exposable_default_destroy(struct exposable *exposable); void exposable_render_deco( const struct exposable *exposable, pixman_image_t *pix, int x, int y, int height); @@ -73,7 +78,7 @@ void exposable_default_on_mouse( {"margin", false, &conf_verify_int}, \ {"left-margin", false, &conf_verify_int}, \ {"right-margin", false, &conf_verify_int}, \ - {"on-click", false, &conf_verify_string}, \ + {"on-click", false, &conf_verify_on_click}, \ {"font", false, &conf_verify_font}, \ {"foreground", false, &conf_verify_color}, \ {"deco", false, &conf_verify_decoration}, \ diff --git a/particles/dynlist.c b/particles/dynlist.c index 00bb41f..bd002d1 100644 --- a/particles/dynlist.c +++ b/particles/dynlist.c @@ -72,7 +72,7 @@ on_mouse(struct exposable *exposable, struct bar *bar, //const struct particle *p = exposable->particle; const struct private *e = exposable->private; - if (exposable->on_click != NULL) { + if (exposable->on_click[btn] != NULL) { exposable_default_on_mouse(exposable, bar, event, btn, x, y); return; } diff --git a/particles/empty.c b/particles/empty.c index e97f929..5c0be16 100644 --- a/particles/empty.c +++ b/particles/empty.c @@ -22,13 +22,9 @@ expose(const struct exposable *exposable, pixman_image_t *pix, int x, int y, int static struct exposable * instantiate(const struct particle *particle, const struct tag_set *tags) { - char *on_click = tags_expand_template(particle->on_click_template, tags); - - struct exposable *exposable = exposable_common_new(particle, on_click); + struct exposable *exposable = exposable_common_new(particle, tags); exposable->begin_expose = &begin_expose; exposable->expose = &expose; - - free(on_click); return exposable; } diff --git a/particles/list.c b/particles/list.c index f9e61f6..43bd3be 100644 --- a/particles/list.c +++ b/particles/list.c @@ -80,7 +80,7 @@ on_mouse(struct exposable *exposable, struct bar *bar, const struct particle *p = exposable->particle; const struct eprivate *e = exposable->private; - if (exposable->on_click != NULL) { + if (exposable->on_click[btn] != NULL) { /* We have our own handler */ exposable_default_on_mouse(exposable, bar, event, btn, x, y); return; @@ -121,16 +121,12 @@ instantiate(const struct particle *particle, const struct tag_set *tags) assert(e->exposables[i] != NULL); } - char *on_click = tags_expand_template(particle->on_click_template, tags); - - struct exposable *exposable = exposable_common_new(particle, on_click); + struct exposable *exposable = exposable_common_new(particle, tags); exposable->private = e; exposable->destroy = &exposable_destroy; exposable->begin_expose = &begin_expose; exposable->expose = &expose; exposable->on_mouse = &on_mouse; - - free(on_click); return exposable; } diff --git a/particles/map.c b/particles/map.c index cc26f77..3710f72 100644 --- a/particles/map.c +++ b/particles/map.c @@ -66,7 +66,7 @@ on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event, const struct particle *p = exposable->particle; const struct eprivate *e = exposable->private; - if (exposable->on_click != NULL) { + if (exposable->on_click[btn] != NULL) { /* We have our own handler */ exposable_default_on_mouse(exposable, bar, event, btn, x, y); return; @@ -119,15 +119,12 @@ instantiate(const struct particle *particle, const struct tag_set *tags) assert(e->exposable != NULL); - char *on_click = tags_expand_template(particle->on_click_template, tags); - struct exposable *exposable = exposable_common_new(particle, on_click); + struct exposable *exposable = exposable_common_new(particle, tags); exposable->private = e; exposable->destroy = &exposable_destroy; exposable->begin_expose = &begin_expose; exposable->expose = &expose; exposable->on_mouse = &on_mouse; - - free(on_click); return exposable; } diff --git a/particles/progress-bar.c b/particles/progress-bar.c index 62a58b9..74c8ebe 100644 --- a/particles/progress-bar.c +++ b/particles/progress-bar.c @@ -148,7 +148,9 @@ on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event, } /* Remember the original handler, so that we can restore it */ - char *original = exposable->on_click; + char *original[MOUSE_BTN_COUNT]; + for (size_t i = 0; i < MOUSE_BTN_COUNT; i++) + original[i] = exposable->on_click[i]; if (event == ON_MOUSE_CLICK) { long where = clickable_width > 0 @@ -160,7 +162,9 @@ on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event, .count = 1, }; - exposable->on_click = tags_expand_template(exposable->on_click, &tags); + tags_expand_templates( + exposable->on_click, (const char **)exposable->on_click, + MOUSE_BTN_COUNT, &tags); tag_set_destroy(&tags); } @@ -169,8 +173,10 @@ on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event, if (event == ON_MOUSE_CLICK) { /* Reset handler string */ - free(exposable->on_click); - exposable->on_click = original; + for (size_t i = 0; i < MOUSE_BTN_COUNT; i++) { + free(exposable->on_click[i]); + exposable->on_click[i] = original[i]; + } } } @@ -213,10 +219,7 @@ instantiate(const struct particle *particle, const struct tag_set *tags) for (size_t i = 0; i < epriv->count; i++) assert(epriv->exposables[i] != NULL); - char *on_click = tags_expand_template(particle->on_click_template, tags); - - struct exposable *exposable = exposable_common_new(particle, on_click); - free(on_click); + struct exposable *exposable = exposable_common_new(particle, tags); exposable->private = epriv; exposable->destroy = &exposable_destroy; diff --git a/particles/ramp.c b/particles/ramp.c index 8087217..14594a2 100644 --- a/particles/ramp.c +++ b/particles/ramp.c @@ -62,7 +62,7 @@ on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event, const struct particle *p = exposable->particle; const struct eprivate *e = exposable->private; - if (exposable->on_click != NULL) { + if (exposable->on_click[btn] != NULL) { /* We have our own handler */ exposable_default_on_mouse(exposable, bar, event, btn, x, y); return; @@ -146,15 +146,12 @@ instantiate(const struct particle *particle, const struct tag_set *tags) e->exposable = pp->instantiate(pp, tags); assert(e->exposable != NULL); - char *on_click = tags_expand_template(particle->on_click_template, tags); - struct exposable *exposable = exposable_common_new(particle, on_click); + struct exposable *exposable = exposable_common_new(particle, tags); exposable->private = e; exposable->destroy = &exposable_destroy; exposable->begin_expose = &begin_expose; exposable->expose = &expose; exposable->on_mouse = &on_mouse; - - free(on_click); return exposable; } diff --git a/particles/string.c b/particles/string.c index 653c8ef..fbed826 100644 --- a/particles/string.c +++ b/particles/string.c @@ -269,15 +269,11 @@ instantiate(const struct particle *particle, const struct tag_set *tags) } } - char *on_click = tags_expand_template(particle->on_click_template, tags); - - struct exposable *exposable = exposable_common_new(particle, on_click); + struct exposable *exposable = exposable_common_new(particle, tags); exposable->private = e; exposable->destroy = &exposable_destroy; exposable->begin_expose = &begin_expose; exposable->expose = &expose; - - free(on_click); return exposable; } diff --git a/tag.c b/tag.c index 5b00218..8ca7e52 100644 --- a/tag.c +++ b/tag.c @@ -533,3 +533,11 @@ tags_expand_template(const char *template, const struct tag_set *tags) return formatted.s; } + +void +tags_expand_templates(char *expanded[], const char *template[], size_t nmemb, + const struct tag_set *tags) +{ + for (size_t i = 0; i < nmemb; i++) + expanded[i] = tags_expand_template(template[i], tags); +} diff --git a/tag.h b/tag.h index 2c629c5..d6bfe6a 100644 --- a/tag.h +++ b/tag.h @@ -50,3 +50,6 @@ void tag_set_destroy(struct tag_set *set); /* Utility functions */ char *tags_expand_template(const char *template, const struct tag_set *tags); +void tags_expand_templates( + char *expanded[], const char *template[], size_t nmemb, + const struct tag_set *tags); From 34d832cd220f477ca0cb9f15bd6ff7f571cd7d46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 22 Jun 2021 19:30:41 +0200 Subject: [PATCH 171/611] config+particle: add support for mouse wheel up/down --- config-verify.c | 2 ++ config.c | 4 ++++ particle.c | 2 ++ particle.h | 2 ++ 4 files changed, 10 insertions(+) diff --git a/config-verify.c b/config-verify.c index 6a5278b..ae6e41d 100644 --- a/config-verify.c +++ b/config-verify.c @@ -164,6 +164,8 @@ conf_verify_on_click(keychain_t *chain, const struct yml_node *node) {"left", false, &conf_verify_string}, {"middle", false, &conf_verify_string}, {"right", false, &conf_verify_string}, + {"wheel-up", false, &conf_verify_string}, + {"wheel-down", false, &conf_verify_string}, {NULL, false, NULL}, }; diff --git a/config.c b/config.c index b2e131c..956eba9 100644 --- a/config.c +++ b/config.c @@ -160,6 +160,10 @@ conf_to_particle(const struct yml_node *node, struct conf_inherit inherited) on_click_templates[MOUSE_BTN_MIDDLE] = template; else if (strcmp(key, "right") == 0) on_click_templates[MOUSE_BTN_RIGHT] = template; + else if (strcmp(key, "wheel-up") == 0) + on_click_templates[MOUSE_BTN_WHEEL_UP] = template; + else if (strcmp(key, "wheel-down") == 0) + on_click_templates[MOUSE_BTN_WHEEL_DOWN] = template; else assert(false); } diff --git a/particle.c b/particle.c index ea4bb62..16eb738 100644 --- a/particle.c +++ b/particle.c @@ -161,6 +161,8 @@ exposable_default_on_mouse(struct exposable *exposable, struct bar *bar, [MOUSE_BTN_MIDDLE] = "middle", [MOUSE_BTN_RIGHT] = "right", [MOUSE_BTN_COUNT] = "count", + [MOUSE_BTN_WHEEL_UP] = "wheel-up", + [MOUSE_BTN_WHEEL_DOWN] = "wheel-down", }; LOG_DBG("on_mouse: exposable=%p, event=%s, btn=%s, x=%d, y=%d (on-click=%s)", exposable, event == ON_MOUSE_MOTION ? "motion" : "click", diff --git a/particle.h b/particle.h index 4e449f3..4ca520f 100644 --- a/particle.h +++ b/particle.h @@ -17,6 +17,8 @@ enum mouse_button { MOUSE_BTN_LEFT, MOUSE_BTN_MIDDLE, MOUSE_BTN_RIGHT, + MOUSE_BTN_WHEEL_UP, + MOUSE_BTN_WHEEL_DOWN, MOUSE_BTN_COUNT, }; From 13ef977eebd1350226c5d0374b873f941bef57bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 22 Jun 2021 20:17:58 +0200 Subject: [PATCH 172/611] =?UTF-8?q?particle:=20on-mouse:=20don=E2=80=99t?= =?UTF-8?q?=20close=20our=20own=20pipe=20FD=20before=20the=20execvp()?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- particle.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/particle.c b/particle.c index 16eb738..5748cce 100644 --- a/particle.c +++ b/particle.c @@ -277,7 +277,8 @@ exposable_default_on_mouse(struct exposable *exposable, struct bar *bar, /* Close *all* other FDs (e.g. script modules' FDs) */ for (int i = STDERR_FILENO + 1; i < 65536; i++) - close(i); + if (i != pipe_fds[1]) + close(i); execvp(argv[0], argv); From 4e2c4e1e3a304cc48bdcbcd673fe2daa73b6381a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 22 Jun 2021 20:18:23 +0200 Subject: [PATCH 173/611] particle: on-mouse: close the read pipe after reading from it, in parent --- particle.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/particle.c b/particle.c index 5748cce..a1e2b2c 100644 --- a/particle.c +++ b/particle.c @@ -295,6 +295,8 @@ exposable_default_on_mouse(struct exposable *exposable, struct bar *bar, int _errno = 0; ssize_t ret = read(pipe_fds[0], &_errno, sizeof(_errno)); + close(pipe_fds[0]); + if (ret == 0) { /* Pipe was closed - child succeeded with exec() */ _exit(0); From 5e6e9e189b5a7f731292fef6e066a1af02bd3ae9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 22 Jun 2021 20:18:50 +0200 Subject: [PATCH 174/611] bar: xcb: add support for mouse wheel up/down --- bar/xcb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bar/xcb.c b/bar/xcb.c index 6229bc5..9c452da 100644 --- a/bar/xcb.c +++ b/bar/xcb.c @@ -369,7 +369,7 @@ loop(struct bar *_bar, const xcb_button_release_event_t *evt = (void *)e; switch (evt->detail) { - case 1: case 2: case 3: + case 1: case 2: case 3: case 4: case 5: on_mouse(_bar, ON_MOUSE_CLICK, evt->detail, evt->event_x, evt->event_y); break; From 93a5bbb4a4ff94137eb10447de4242c9013f3b69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 22 Jun 2021 20:19:07 +0200 Subject: [PATCH 175/611] bar: wayland: require seat version 5, to get discrete axis events --- bar/wayland.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bar/wayland.c b/bar/wayland.c index 1124ab2..7fb9d29 100644 --- a/bar/wayland.c +++ b/bar/wayland.c @@ -574,7 +574,7 @@ handle_global(void *data, struct wl_registry *registry, } else if (strcmp(interface, wl_seat_interface.name) == 0) { - const uint32_t required = 3; + const uint32_t required = 5; if (!verify_iface_version(interface, version, required)) return; From 530afe6cf56a51126f6ec62282110eebdd3bf558 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 22 Jun 2021 20:20:06 +0200 Subject: [PATCH 176/611] bar: wayland: initial support for mouse wheel up/down --- bar/wayland.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/bar/wayland.c b/bar/wayland.c index 7fb9d29..77474eb 100644 --- a/bar/wayland.c +++ b/bar/wayland.c @@ -113,6 +113,9 @@ struct wayland_backend { struct buffer *next_buffer; /* Bar is rendering to this one */ struct buffer *pending_buffer; /* Finished, but not yet rendered */ + double aggregated_scroll; + bool have_discrete; + void (*bar_expose)(const struct bar *bar); void (*bar_on_mouse)(struct bar *bar, enum mouse_event event, enum mouse_button btn, int x, int y); @@ -248,6 +251,8 @@ wl_pointer_leave(void *data, struct wl_pointer *wl_pointer, struct seat *seat = data; struct wayland_backend *backend = seat->backend; + backend->have_discrete = false; + if (backend->active_seat == seat) backend->active_seat = NULL; } @@ -297,11 +302,46 @@ static void wl_pointer_axis(void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis, wl_fixed_t value) { + if (axis != WL_POINTER_AXIS_VERTICAL_SCROLL) + return; + + struct seat *seat = data; + struct wayland_backend *backend = seat->backend; + backend->active_seat = seat; + + if (backend->have_discrete) + return; + + const double amount = wl_fixed_to_double(value); + + if ((backend->aggregated_scroll > 0 && amount < 0) || + (backend->aggregated_scroll < 0 && amount > 0)) + { + backend->aggregated_scroll = amount; + } else + backend->aggregated_scroll += amount; + + enum mouse_button btn = backend->aggregated_scroll > 0 + ? MOUSE_BTN_WHEEL_DOWN + : MOUSE_BTN_WHEEL_UP; + + const double step = 20.; + const double adjust = backend->aggregated_scroll > 0 ? -step : step; + + while (fabs(backend->aggregated_scroll) >= step) { + backend->bar_on_mouse( + backend->bar, ON_MOUSE_CLICK, btn, + seat->pointer.x, seat->pointer.y); + backend->aggregated_scroll += adjust; + } } static void wl_pointer_frame(void *data, struct wl_pointer *wl_pointer) { + struct seat *seat = data; + struct wayland_backend *backend = seat->backend; + backend->have_discrete = false; } static void @@ -314,12 +354,34 @@ static void wl_pointer_axis_stop(void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis) { + if (axis != WL_POINTER_AXIS_VERTICAL_SCROLL) + return; + + struct seat *seat = data; + struct wayland_backend *backend = seat->backend; + backend->aggregated_scroll = 0.; } static void wl_pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer, uint32_t axis, int32_t discrete) { + if (axis != WL_POINTER_AXIS_VERTICAL_SCROLL) + return; + + struct seat *seat = data; + struct wayland_backend *backend = seat->backend; + backend->have_discrete = true; + + enum mouse_button btn = discrete > 0 + ? MOUSE_BTN_WHEEL_DOWN + : MOUSE_BTN_WHEEL_UP; + + for (int32_t i = 0; i < discrete; i++) { + backend->bar_on_mouse( + backend->bar, ON_MOUSE_CLICK, btn, + seat->pointer.x, seat->pointer.y); + } } static const struct wl_pointer_listener pointer_listener = { From 46e6539b1ad81743b30b0fc9984d29e99b46b791 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 23 Jun 2021 10:58:28 +0200 Subject: [PATCH 177/611] particle/list: call default handler on motion events if we a have click handler This ensures the cursor changes shape correctly --- particles/list.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/particles/list.c b/particles/list.c index 43bd3be..9f03231 100644 --- a/particles/list.c +++ b/particles/list.c @@ -80,7 +80,10 @@ on_mouse(struct exposable *exposable, struct bar *bar, const struct particle *p = exposable->particle; const struct eprivate *e = exposable->private; - if (exposable->on_click[btn] != NULL) { + if ((event == ON_MOUSE_MOTION && + exposable->particle->have_on_click_template) || + exposable->on_click[btn] != NULL) + { /* We have our own handler */ exposable_default_on_mouse(exposable, bar, event, btn, x, y); return; From 4ce3fe22858ed86480cead7302afa40c7a2a40fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 23 Jun 2021 11:21:26 +0200 Subject: [PATCH 178/611] bar/wayland: fix mouse wheel up not being emitted MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We emit mouse wheel up events when the ‘discrete’ counter is negative. Thus, when looping, we need to loop to its *absolute* value. --- bar/wayland.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bar/wayland.c b/bar/wayland.c index 77474eb..19470fc 100644 --- a/bar/wayland.c +++ b/bar/wayland.c @@ -377,7 +377,9 @@ wl_pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer, ? MOUSE_BTN_WHEEL_DOWN : MOUSE_BTN_WHEEL_UP; - for (int32_t i = 0; i < discrete; i++) { + int count = abs(discrete); + + for (int32_t i = 0; i < count; i++) { backend->bar_on_mouse( backend->bar, ON_MOUSE_CLICK, btn, seat->pointer.x, seat->pointer.y); From 0aef2f85ee93fdd8fbf43f959bc15ffb6159701f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 23 Jun 2021 11:28:59 +0200 Subject: [PATCH 179/611] config: add bar.trackpad-sensitivity This is an integer that specifies the amount of scrolling that needs to be accumulated before a wheel-up/down event is emitted. A higher value means you need to drag your fingers a longer distance before the event is emitted. The default is 30. --- bar/bar.c | 1 + bar/bar.h | 1 + bar/private.h | 1 + bar/wayland.c | 4 +++- config-verify.c | 2 ++ config.c | 6 ++++++ doc/yambar.5.scd | 6 ++++++ 7 files changed, 20 insertions(+), 1 deletion(-) diff --git a/bar/bar.c b/bar/bar.c index b7557fd..9806f44 100644 --- a/bar/bar.c +++ b/bar/bar.c @@ -408,6 +408,7 @@ bar_new(const struct bar_config *config) priv->right_spacing = config->right_spacing; priv->left_margin = config->left_margin; priv->right_margin = config->right_margin; + priv->trackpad_sensitivity = config->trackpad_sensitivity; priv->border.width = config->border.width; priv->border.color = config->border.color; priv->border.left_margin = config->border.left_margin; diff --git a/bar/bar.h b/bar/bar.h index 78e2414..4e82534 100644 --- a/bar/bar.h +++ b/bar/bar.h @@ -25,6 +25,7 @@ struct bar_config { int height; int left_spacing, right_spacing; int left_margin, right_margin; + int trackpad_sensitivity; pixman_color_t background; diff --git a/bar/private.h b/bar/private.h index b5d888f..eed532b 100644 --- a/bar/private.h +++ b/bar/private.h @@ -10,6 +10,7 @@ struct private { int height; int left_spacing, right_spacing; int left_margin, right_margin; + int trackpad_sensitivity; pixman_color_t background; diff --git a/bar/wayland.c b/bar/wayland.c index 19470fc..7e63cf0 100644 --- a/bar/wayland.c +++ b/bar/wayland.c @@ -307,6 +307,8 @@ wl_pointer_axis(void *data, struct wl_pointer *wl_pointer, struct seat *seat = data; struct wayland_backend *backend = seat->backend; + struct private *bar = backend->bar->private; + backend->active_seat = seat; if (backend->have_discrete) @@ -325,7 +327,7 @@ wl_pointer_axis(void *data, struct wl_pointer *wl_pointer, ? MOUSE_BTN_WHEEL_DOWN : MOUSE_BTN_WHEEL_UP; - const double step = 20.; + const double step = bar->trackpad_sensitivity; const double adjust = backend->aggregated_scroll > 0 ? -step : step; while (fabs(backend->aggregated_scroll) >= step) { diff --git a/config-verify.c b/config-verify.c index ae6e41d..6c09a4a 100644 --- a/config-verify.c +++ b/config-verify.c @@ -423,6 +423,8 @@ conf_verify_bar(const struct yml_node *bar) {"center", false, &verify_module_list}, {"right", false, &verify_module_list}, + {"trackpad-sensitivity", false, &conf_verify_int}, + {NULL, false, NULL}, }; diff --git a/config.c b/config.c index 956eba9..bc6d7a7 100644 --- a/config.c +++ b/config.c @@ -252,6 +252,12 @@ conf_to_bar(const struct yml_node *bar, enum bar_backend backend) if (right_margin != NULL) conf.right_margin = yml_value_as_int(right_margin); + const struct yml_node *trackpad_sensitivity = + yml_get_value(bar, "trackpad-sensitivity"); + conf.trackpad_sensitivity = trackpad_sensitivity != NULL + ? yml_value_as_int(trackpad_sensitivity) + : 30; + const struct yml_node *border = yml_get_value(bar, "border"); if (border != NULL) { const struct yml_node *width = yml_get_value(border, "width"); diff --git a/doc/yambar.5.scd b/doc/yambar.5.scd index 6b4a5ff..d352e0e 100644 --- a/doc/yambar.5.scd +++ b/doc/yambar.5.scd @@ -109,6 +109,12 @@ types that are frequently used: : color : no : Default foreground (text) color to use +| trackpad-sensitivity +: int +: no +: How easy it is to trigger wheel-up and wheel-down on-click + handlers. Higher values means you need to drag your finger a longer + distance. The default is 30. | left : list : no From e11fe12c98aed71ce7b720cca6f78c77fbdb7c66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 2 Jul 2021 16:36:09 +0200 Subject: [PATCH 180/611] particles: fix mouse hover on non-primitive particles If a ramp, map or progress-bar has an on-click handler, then the mouse should _always_ reflect this. --- particles/dynlist.c | 1 - particles/map.c | 5 ++++- particles/progress-bar.c | 5 ++++- particles/ramp.c | 5 ++++- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/particles/dynlist.c b/particles/dynlist.c index bd002d1..8c6319c 100644 --- a/particles/dynlist.c +++ b/particles/dynlist.c @@ -69,7 +69,6 @@ static void on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event, enum mouse_button btn, int x, int y) { - //const struct particle *p = exposable->particle; const struct private *e = exposable->private; if (exposable->on_click[btn] != NULL) { diff --git a/particles/map.c b/particles/map.c index 3710f72..e2d8c03 100644 --- a/particles/map.c +++ b/particles/map.c @@ -66,7 +66,10 @@ on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event, const struct particle *p = exposable->particle; const struct eprivate *e = exposable->private; - if (exposable->on_click[btn] != NULL) { + if ((event == ON_MOUSE_MOTION && + exposable->particle->have_on_click_template) || + exposable->on_click[btn] != NULL) + { /* We have our own handler */ exposable_default_on_mouse(exposable, bar, event, btn, x, y); return; diff --git a/particles/progress-bar.c b/particles/progress-bar.c index 74c8ebe..a825117 100644 --- a/particles/progress-bar.c +++ b/particles/progress-bar.c @@ -87,7 +87,10 @@ static void on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event, enum mouse_button btn, int x, int y) { - if (exposable->on_click == NULL) { + if ((event == ON_MOUSE_MOTION && + exposable->particle->have_on_click_template) || + exposable->on_click[btn] != NULL) + { exposable_default_on_mouse(exposable, bar, event, btn, x, y); return; } diff --git a/particles/ramp.c b/particles/ramp.c index 14594a2..3fa2fc8 100644 --- a/particles/ramp.c +++ b/particles/ramp.c @@ -62,7 +62,10 @@ on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event, const struct particle *p = exposable->particle; const struct eprivate *e = exposable->private; - if (exposable->on_click[btn] != NULL) { + if ((event == ON_MOUSE_MOTION && + exposable->particle->have_on_click_template) || + exposable->on_click[btn] != NULL) + { /* We have our own handler */ exposable_default_on_mouse(exposable, bar, event, btn, x, y); return; From fc9c3ebbb82abfe07b556ff84ee7b47030d8833f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 4 Jul 2021 19:50:15 +0200 Subject: [PATCH 181/611] changelog: mouse buttons + scrolling --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c78a2a2..850a307 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ ### Added * Text shaping support. +* Support for middle and right mouse buttons, mouse wheel and trackpad + scrolling (https://codeberg.org/dnkl/yambar/issues/39). ### Changed From cf41d008f8b6bce77c98f8e5782f8b5c4606c25d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 4 Jul 2021 20:23:01 +0200 Subject: [PATCH 182/611] module/script: add poll-interval option When set to a non-negative value, the script module will call the configured script every second. In this mode, the script is expected to write one tag set and then exit. This is intended to simplify the implementation of scripts that would otherwise just do a loop + sleep. Closes #67 --- CHANGELOG.md | 2 + doc/yambar-modules-script.5.scd | 24 ++++++--- modules/script.c | 92 +++++++++++++++++++++++++++++---- 3 files changed, 101 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 850a307..af8d75a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,8 @@ * Text shaping support. * Support for middle and right mouse buttons, mouse wheel and trackpad scrolling (https://codeberg.org/dnkl/yambar/issues/39). +* script: polling mode. See the new `poll-interval` option + (https://codeberg.org/dnkl/yambar/issues/67). ### Changed diff --git a/doc/yambar-modules-script.5.scd b/doc/yambar-modules-script.5.scd index 6ba1384..e4347f8 100644 --- a/doc/yambar-modules-script.5.scd +++ b/doc/yambar-modules-script.5.scd @@ -8,11 +8,16 @@ script - This module executes a user-provided script (or binary!) This module executes a user-provided script (or binary!) that writes tags on its stdout. -The script can either exit immediately after writing a set of tags, in -which case yambar will display those tags until yambar is -terminated. Or, the script can continue executing and update yambar -with new tag sets, either periodically, or when there is new data to -feed to yambar. +Scripts can be run in two modes: yambar polled, or continously. In the +yambar polled mode, the script is expected to write one set of tags +and then exit. Yambar will execute the script again after a +configurable amount of time. + +In continous mode, the script is executed once. It will typically run +in a loop, sending an updated tag set whenever it needs, or wants +to. The last tag set is used (displayed) by yambar until a new tag set +is received. This mode is intended to be used by scripts that depends +on non-polling methods to update their state. Tag sets, or _transactions_, are separated by an empty line (e.g. *echo ""*). The empty line is required to commit (update) the @@ -70,6 +75,10 @@ User defined. : list of strings : no : Arguments to pass to the script/binary. +| poll-interval +: integer +: Number of seconds between each script run. If unset, continous mode + is used. # EXAMPLES @@ -89,8 +98,9 @@ while true; do done ``` -This script will emit a single string tag, _test_, and alternate its -value between *hello* and *world* every three seconds. +This script runs in continous mode, and will emit a single string tag, +_test_, and alternate its value between *hello* and *world* every +three seconds. A corresponding yambar configuration could look like this: diff --git a/modules/script.c b/modules/script.c index a034910..189f4b6 100644 --- a/modules/script.c +++ b/modules/script.c @@ -25,6 +25,8 @@ struct private { char *path; size_t argc; char **argv; + int poll_interval; + bool aborted; struct particle *content; @@ -331,7 +333,7 @@ data_received(struct module *mod, const char *data, size_t len) static int run_loop(struct module *mod, pid_t pid, int comm_fd) { - int ret = 0; + int ret = 1; while (true) { struct pollfd fds[] = { @@ -360,19 +362,18 @@ run_loop(struct module *mod, pid_t pid, int comm_fd) data_received(mod, data, amount); } - if (fds[0].revents & POLLHUP) { + if (fds[0].revents & (POLLHUP | POLLIN)) { /* Aborted */ + struct private *m = mod->private; + m->aborted = true; + ret = 0; break; } if (fds[1].revents & POLLHUP) { /* Child's stdout closed */ LOG_DBG("script pipe closed (script terminated?)"); - break; - } - - if (fds[0].revents & POLLIN) { - /* Aborted */ + ret = 0; break; } } @@ -381,7 +382,7 @@ run_loop(struct module *mod, pid_t pid, int comm_fd) } static int -run(struct module *mod) +execute_script(struct module *mod) { struct private *m = mod->private; @@ -565,9 +566,75 @@ run(struct module *mod) return ret; } +static int +run(struct module *mod) +{ + struct private *m = mod->private; + + int ret = 1; + bool keep_going = true; + + while (keep_going && !m->aborted) { + ret = execute_script(mod); + + if (ret != 0) + break; + if (m->aborted) + break; + if (m->poll_interval < 0) + break; + + struct timeval now; + if (gettimeofday(&now, NULL) < 0) { + LOG_ERRNO("failed to get current time"); + break; + } + + struct timeval poll_interval = {.tv_sec = m->poll_interval}; + + struct timeval timeout; + timeradd(&now, &poll_interval, &timeout); + + while (true) { + struct pollfd fds[] = {{.fd = mod->abort_fd, .events = POLLIN}}; + + struct timeval now; + if (gettimeofday(&now, NULL) < 0) { + LOG_ERRNO("failed to get current time"); + keep_going = false; + break; + } + + if (!timercmp(&now, &timeout, <)) { + /* We’ve reached the timeout, it’s time to execute the script again */ + break; + } + + struct timeval time_left; + timersub(&timeout, &now, &time_left); + + int r = poll(fds, 1, time_left.tv_sec * 1000 + time_left.tv_usec / 1000); + if (r < 0) { + if (errno == EINTR) + continue; + LOG_ERRNO("failed to poll"); + keep_going = false; + break; + } + + if (r > 0) { + m->aborted = true; + break; + } + } + } + + return ret; +} + static struct module * script_new(const char *path, size_t argc, const char *const argv[static argc], - struct particle *_content) + int poll_interval, struct particle *_content) { struct private *m = calloc(1, sizeof(*m)); m->path = strdup(path); @@ -576,6 +643,7 @@ script_new(const char *path, size_t argc, const char *const argv[static argc], m->argv = malloc(argc * sizeof(m->argv[0])); for (size_t i = 0; i < argc; i++) m->argv[i] = strdup(argv[i]); + m->poll_interval = poll_interval; struct module *mod = module_common_new(); mod->private = m; @@ -592,6 +660,7 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) const struct yml_node *path = yml_get_value(node, "path"); const struct yml_node *args = yml_get_value(node, "args"); const struct yml_node *c = yml_get_value(node, "content"); + const struct yml_node *poll_interval = yml_get_value(node, "poll-interval"); size_t argc = args != NULL ? yml_list_length(args) : 0; const char *argv[argc]; @@ -607,7 +676,9 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) } return script_new( - yml_value_as_string(path), argc, argv, conf_to_particle(c, inherited)); + yml_value_as_string(path), argc, argv, + poll_interval != NULL ? yml_value_as_int(poll_interval) : -1, + conf_to_particle(c, inherited)); } static bool @@ -637,6 +708,7 @@ verify_conf(keychain_t *chain, const struct yml_node *node) static const struct attr_info attrs[] = { {"path", true, &conf_verify_path}, {"args", false, &conf_verify_args}, + {"poll-interval", false, &conf_verify_int}, MODULE_COMMON_ATTRS, }; From 0ddabacc7785011bda86b91787a81201028f0971 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 11 Jul 2021 15:27:57 +0200 Subject: [PATCH 183/611] doc: yambar-modules-script: codespell fixes --- doc/yambar-modules-script.5.scd | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/yambar-modules-script.5.scd b/doc/yambar-modules-script.5.scd index e4347f8..26b210f 100644 --- a/doc/yambar-modules-script.5.scd +++ b/doc/yambar-modules-script.5.scd @@ -8,12 +8,12 @@ script - This module executes a user-provided script (or binary!) This module executes a user-provided script (or binary!) that writes tags on its stdout. -Scripts can be run in two modes: yambar polled, or continously. In the +Scripts can be run in two modes: yambar polled, or continuously. In the yambar polled mode, the script is expected to write one set of tags and then exit. Yambar will execute the script again after a configurable amount of time. -In continous mode, the script is executed once. It will typically run +In continuous mode, the script is executed once. It will typically run in a loop, sending an updated tag set whenever it needs, or wants to. The last tag set is used (displayed) by yambar until a new tag set is received. This mode is intended to be used by scripts that depends @@ -77,7 +77,7 @@ User defined. : Arguments to pass to the script/binary. | poll-interval : integer -: Number of seconds between each script run. If unset, continous mode +: Number of seconds between each script run. If unset, continuous mode is used. # EXAMPLES @@ -98,7 +98,7 @@ while true; do done ``` -This script runs in continous mode, and will emit a single string tag, +This script runs in continuous mode, and will emit a single string tag, _test_, and alternate its value between *hello* and *world* every three seconds. From 8187d60193b960509112759bb65a2166eb178dd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 19 Jul 2021 12:27:20 +0200 Subject: [PATCH 184/611] particle/string: use HORIZONTAL ELLIPSIS as truncation character MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit First, apply max-len to the converted wide character string, instead of the UTF-8 string. This is better, and more correct, since UTF-8 is multibyte, and applying max-len to that results in strings _shorter_ than max-len. Second, use HORIZONTAL ELLIPSIS (…) instead of three regular periods (...) as truncation character. This “saves” 2 characters. To be able to do this, the conversion to a wide character, and glyph rasterization is now done when the exposable is instantiated, instead of in begin_expose(). Closes #73 --- CHANGELOG.md | 3 + particles/string.c | 266 +++++++++++++++++++++++---------------------- 2 files changed, 137 insertions(+), 132 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index af8d75a..706b824 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,9 @@ * battery: differentiate "Not Charging" and "Discharging" in state tag of battery module. (https://codeberg.org/dnkl/yambar/issues/57). +* string: use HORIZONTAL ELLIPSIS instead of three regular periods + when truncating a string + (https://codeberg.org/dnkl/yambar/issues/73). ### Deprecated diff --git a/particles/string.c b/particles/string.c index fbed826..c475d5b 100644 --- a/particles/string.c +++ b/particles/string.c @@ -26,9 +26,6 @@ struct private { }; struct eprivate { - /* Set when instantiating */ - char *text; - ssize_t cache_idx; const struct fcft_glyph **glyphs; const struct fcft_glyph **allocated_glyphs; @@ -36,25 +33,11 @@ struct eprivate { int num_glyphs; }; -static uint64_t -sdbm_hash(const char *s) -{ - uint64_t hash = 0; - - for (; *s != '\0'; s++) { - int c = *s; - hash = c + (hash << 6) + (hash << 16) - hash; - } - - return hash; -} - static void exposable_destroy(struct exposable *exposable) { struct eprivate *e = exposable->private; - free(e->text); free(e->allocated_glyphs); free(e->kern_x); free(e); @@ -66,108 +49,18 @@ begin_expose(struct exposable *exposable) { struct eprivate *e = exposable->private; struct private *p = exposable->particle->private; - struct fcft_font *font = exposable->particle->font; - e->glyphs = e->allocated_glyphs = NULL; - e->num_glyphs = 0; - e->kern_x = NULL; - e->cache_idx = -1; - - uint64_t hash = sdbm_hash(e->text); - - for (size_t i = 0; i < p->cache_size; i++) { - if (p->cache[i].hash == hash) { - assert(p->cache[i].run != NULL); - - p->cache[i].in_use = true; - e->cache_idx = i; - e->glyphs = p->cache[i].run->glyphs; - e->num_glyphs = p->cache[i].run->count; - e->kern_x = calloc(p->cache[i].run->count, sizeof(e->kern_x[0])); - - exposable->width = - exposable->particle->left_margin + - p->cache[i].width + - exposable->particle->right_margin; - - return exposable->width; - } - } - - size_t chars = mbstowcs(NULL, e->text, 0); - if (chars != (size_t)-1) { - wchar_t wtext[chars + 1]; - mbstowcs(wtext, e->text, chars + 1); - - e->kern_x = calloc(chars, sizeof(e->kern_x[0])); - - if (fcft_capabilities() & FCFT_CAPABILITY_TEXT_RUN_SHAPING) { - struct fcft_text_run *run = fcft_text_run_rasterize(font, chars, wtext, FCFT_SUBPIXEL_NONE); - if (run != NULL) { - int w = 0; - for (size_t i = 0; i < run->count; i++) - w += run->glyphs[i]->advance.x; - - ssize_t cache_idx = -1; - for (size_t i = 0; i < p->cache_size; i++) { - if (p->cache[i].run == NULL || !p->cache[i].in_use) { - fcft_text_run_destroy(p->cache[i].run); - cache_idx = i; - break; - } - } - - if (cache_idx < 0) { - size_t new_size = p->cache_size + 1; - struct text_run_cache *new_cache = realloc( - p->cache, new_size * sizeof(new_cache[0])); - - p->cache_size = new_size; - p->cache = new_cache; - cache_idx = new_size - 1; - } - - assert(cache_idx >= 0 && cache_idx < p->cache_size); - p->cache[cache_idx].hash = hash; - p->cache[cache_idx].run = run; - p->cache[cache_idx].width = w; - p->cache[cache_idx].in_use = true; - - e->cache_idx = cache_idx; - e->num_glyphs = run->count; - e->glyphs = run->glyphs; - } - } - - if (e->glyphs == NULL) { - e->allocated_glyphs = malloc(chars * sizeof(e->glyphs[0])); - - /* Convert text to glyph masks/images. */ - for (size_t i = 0; i < chars; i++) { - const struct fcft_glyph *glyph = fcft_glyph_rasterize( - font, wtext[i], FCFT_SUBPIXEL_NONE); - - if (glyph == NULL) - continue; - - e->allocated_glyphs[e->num_glyphs++] = glyph; - - if (i == 0) - continue; - - fcft_kerning(font, wtext[i - 1], wtext[i], &e->kern_x[i], NULL); - } - - e->glyphs = e->allocated_glyphs; - } - } - - exposable->width = exposable->particle->left_margin + + exposable->width = + exposable->particle->left_margin + exposable->particle->right_margin; - /* Calculate the size we need to render the glyphs */ - for (int i = 0; i < e->num_glyphs; i++) - exposable->width += e->kern_x[i] + e->glyphs[i]->advance.x; + if (e->cache_idx >= 0) { + exposable->width += p->cache[e->cache_idx].width; + } else { + /* Calculate the size we need to render the glyphs */ + for (int i = 0; i < e->num_glyphs; i++) + exposable->width += e->kern_x[i] + e->glyphs[i]->advance.x; + } return exposable->width; } @@ -236,39 +129,148 @@ expose(const struct exposable *exposable, pixman_image_t *pix, int x, int y, int } } +static uint64_t +sdbm_hash(const char *s) +{ + uint64_t hash = 0; + + for (; *s != '\0'; s++) { + int c = *s; + hash = c + (hash << 6) + (hash << 16) - hash; + } + + return hash; +} + static struct exposable * instantiate(const struct particle *particle, const struct tag_set *tags) { - const struct private *p = particle->private; + struct private *p = (struct private *)particle->private; struct eprivate *e = calloc(1, sizeof(*e)); + struct fcft_font *font = particle->font; - e->text = tags_expand_template(p->text, tags); - e->glyphs = NULL; + wchar_t *wtext = NULL; + char *text = tags_expand_template(p->text, tags); + + e->glyphs = e->allocated_glyphs = NULL; e->num_glyphs = 0; + e->kern_x = NULL; + e->cache_idx = -1; + uint64_t hash = sdbm_hash(text); + + /* First, check if we have this string cached */ + for (size_t i = 0; i < p->cache_size; i++) { + if (p->cache[i].hash == hash) { + assert(p->cache[i].run != NULL); + + p->cache[i].in_use = true; + e->cache_idx = i; + e->glyphs = p->cache[i].run->glyphs; + e->num_glyphs = p->cache[i].run->count; + e->kern_x = calloc(p->cache[i].run->count, sizeof(e->kern_x[0])); + goto done; + } + } + + /* Not in cache - we need to rasterize it. First, convert to wchar */ + size_t chars = mbstowcs(NULL, text, 0); + if (chars == (size_t)-1) + goto done; + + wtext = malloc((chars + 1) * sizeof(wtext[0])); + mbstowcs(wtext, text, chars + 1); + + /* Truncate, if necessary */ if (p->max_len > 0) { - const size_t len = strlen(e->text); + const size_t len = wcslen(wtext); if (len > p->max_len) { size_t end = p->max_len; - if (end >= 3) { + if (end >= 1) { /* "allocate" room for three dots at the end */ - end -= 3; + end -= 1; } - /* Mucho importante - don't cut in the middle of a utf8 multibyte */ - while (end > 0 && e->text[end - 1] >> 7) - end--; - - if (p->max_len > 3) { - for (size_t i = 0; i < 3; i++) - e->text[end + i] = '.'; - e->text[end + 3] = '\0'; - } else - e->text[end] = '\0'; + if (p->max_len > 1) { + wtext[end] = L'…'; + wtext[end + 1] = L'\0'; + chars = end + 1; + } else { + wtext[end] = L'\0'; + chars = 0; + } } } + e->kern_x = calloc(chars, sizeof(e->kern_x[0])); + + if (fcft_capabilities() & FCFT_CAPABILITY_TEXT_RUN_SHAPING) { + struct fcft_text_run *run = fcft_text_run_rasterize( + font, chars, wtext, FCFT_SUBPIXEL_NONE); + + if (run != NULL) { + int w = 0; + for (size_t i = 0; i < run->count; i++) + w += run->glyphs[i]->advance.x; + + ssize_t cache_idx = -1; + for (size_t i = 0; i < p->cache_size; i++) { + if (p->cache[i].run == NULL || !p->cache[i].in_use) { + fcft_text_run_destroy(p->cache[i].run); + cache_idx = i; + break; + } + } + + if (cache_idx < 0) { + size_t new_size = p->cache_size + 1; + struct text_run_cache *new_cache = realloc( + p->cache, new_size * sizeof(new_cache[0])); + + p->cache_size = new_size; + p->cache = new_cache; + cache_idx = new_size - 1; + } + + assert(cache_idx >= 0 && cache_idx < p->cache_size); + p->cache[cache_idx].hash = hash; + p->cache[cache_idx].run = run; + p->cache[cache_idx].width = w; + p->cache[cache_idx].in_use = true; + + e->cache_idx = cache_idx; + e->num_glyphs = run->count; + e->glyphs = run->glyphs; + } + } + + if (e->glyphs == NULL) { + e->allocated_glyphs = malloc(chars * sizeof(e->glyphs[0])); + + /* Convert text to glyph masks/images. */ + for (size_t i = 0; i < chars; i++) { + const struct fcft_glyph *glyph = fcft_glyph_rasterize( + font, wtext[i], FCFT_SUBPIXEL_NONE); + + if (glyph == NULL) + continue; + + e->allocated_glyphs[e->num_glyphs++] = glyph; + + if (i == 0) + continue; + + fcft_kerning(font, wtext[i - 1], wtext[i], &e->kern_x[i], NULL); + } + + e->glyphs = e->allocated_glyphs; + } + +done: + free(wtext); + free(text); + struct exposable *exposable = exposable_common_new(particle, tags); exposable->private = e; exposable->destroy = &exposable_destroy; From 481040f2da7bed471315ab3f6055a5e571b475c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 24 Jul 2021 13:37:50 +0200 Subject: [PATCH 185/611] changelog: prepare for 1.6.2 --- CHANGELOG.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 706b824..c253670 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,12 @@ # Changelog -* [Unreleased](#Unreleased) +* [1.6.2](#1-6-2) * [1.6.1](#1-6-1) * [1.6.0](#1-6-0) * [1.5.0](#1-5-0) -## Unreleased +## 1.6.2 ### Added @@ -35,8 +35,6 @@ (https://codeberg.org/dnkl/yambar/issues/73). -### Deprecated -### Removed ### Fixed * Crash when merging non-dictionary anchors in the YAML configuration @@ -49,12 +47,12 @@ `9` (https://codeberg.org/dnkl/yambar/issues/60). -### Security ### Contributors * [novakane](https://codeberg.org/novakane) * [mz](https://codeberg.org/mz) + ## 1.6.1 ### Changed From 66ea64d8267bbac6b4dfe31d886e05edb8e5c5b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 24 Jul 2021 13:38:09 +0200 Subject: [PATCH 186/611] meson/pkgbuild: bump version to 1.6.2 --- PKGBUILD | 2 +- PKGBUILD.wayland-only | 2 +- meson.build | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/PKGBUILD b/PKGBUILD index 66e3efe..17a867c 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -1,5 +1,5 @@ pkgname=yambar -pkgver=1.6.1 +pkgver=1.6.2 pkgrel=1 pkgdesc="Simplistic and highly configurable status panel for X and Wayland" arch=('x86_64' 'aarch64') diff --git a/PKGBUILD.wayland-only b/PKGBUILD.wayland-only index 34dd8ae..a3f8207 100644 --- a/PKGBUILD.wayland-only +++ b/PKGBUILD.wayland-only @@ -1,5 +1,5 @@ pkgname=yambar-wayland -pkgver=1.6.1 +pkgver=1.6.2 pkgrel=1 pkgdesc="Simplistic and highly configurable status panel for Wayland" arch=('x86_64' 'aarch64') diff --git a/meson.build b/meson.build index a1e4fa1..23f6731 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('yambar', 'c', - version: '1.6.1', + version: '1.6.2', license: 'MIT', meson_version: '>=0.53.0', default_options: ['c_std=c18', From 21adc40a52826bf42bcb5a6b90e3c44360d54ac7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 24 Jul 2021 13:43:13 +0200 Subject: [PATCH 187/611] =?UTF-8?q?changelog:=20add=20new=20=E2=80=98unrel?= =?UTF-8?q?eased=E2=80=99=20section?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c253670..e534c9a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,22 @@ # Changelog +* [Unreleased](#unreleased) * [1.6.2](#1-6-2) * [1.6.1](#1-6-1) * [1.6.0](#1-6-0) * [1.5.0](#1-5-0) +## Unreleased +### Added +### Changed +### Deprecated +### Removed +### Fixed +### Security +### Contributors + + ## 1.6.2 ### Added From 7da13a26d0c008903341cf65e0bab2eccf0b6ec2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 26 Jul 2021 16:32:27 +0200 Subject: [PATCH 188/611] =?UTF-8?q?module/i3:=20add=20=E2=80=98persistent?= =?UTF-8?q?=E2=80=99=20attribute?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add ‘persistent’, a list-of-strings specifying workspace names that should be persistent. That is, workspaces that should never be removed, even if empty. Note that the workspaces _are_ still destroyed (in i3/Sway), but yambar keeps abstractions for them around. This is useful to e.g. keep a strict order between your “core” workspaces. Closes #72 --- CHANGELOG.md | 5 + doc/yambar-modules-i3.5.scd | 4 + modules/i3.c | 184 ++++++++++++++++++++++++++---------- 3 files changed, 143 insertions(+), 50 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e534c9a..5af6ce7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,11 @@ ## Unreleased ### Added + +* i3: `persistent` attribute, allowing persistent workspaces + (https://codeberg.org/dnkl/yambar/issues/72). + + ### Changed ### Deprecated ### Removed diff --git a/doc/yambar-modules-i3.5.scd b/doc/yambar-modules-i3.5.scd index d61ebda..8bf4a75 100644 --- a/doc/yambar-modules-i3.5.scd +++ b/doc/yambar-modules-i3.5.scd @@ -65,6 +65,10 @@ with the _application_ and _title_ tags to replace the X11-only : enum : no : How to sort the list of workspaces; one of _none_, _ascending_ or _descending_, defaults to _none_. +| persistent +: list of strings +: no +: Persistent workspaces. I.e. workspaces that are never removed, even if empty. | left-spacing : int : no diff --git a/modules/i3.c b/modules/i3.c index 5aa3d3b..5e5c80a 100644 --- a/modules/i3.c +++ b/modules/i3.c @@ -30,7 +30,8 @@ struct ws_content { struct workspace { char *name; - int name_as_int; /* -1 is name is not a decimal number */ + int name_as_int; /* -1 if name is not a decimal number */ + bool persistent; char *output; bool visible; @@ -60,8 +61,26 @@ struct private { enum sort_mode sort_mode; tll(struct workspace) workspaces; + + size_t persistent_count; + char **persistent_workspaces; }; +static int +workspace_name_as_int(const char *name) +{ + int name_as_int = 0; + for (const char *p = name; *p != '\0'; p++) { + if (!(*p >= '0' && *p <= '9')) + return -1; + + name_as_int *= 10; + name_as_int += *p - '0'; + } + + return name_as_int; +} + static bool workspace_from_json(const struct json_object *json, struct workspace *ws) { @@ -82,20 +101,10 @@ workspace_from_json(const struct json_object *json, struct workspace *ws) const char *name_as_string = json_object_get_string(name); - int name_as_int = 0; - for (const char *p = name_as_string; *p != '\0'; p++) { - if (!(*p >= '0' && *p <= '9')) { - name_as_int = -1; - break; - } - - name_as_int *= 10; - name_as_int += *p - '0'; - } - *ws = (struct workspace) { .name = strdup(name_as_string), - .name_as_int = name_as_int, + .name_as_int = workspace_name_as_int(name_as_string), + .persistent = false, .output = strdup(json_object_get_string(output)), .visible = json_object_get_boolean(visible), .focused = json_object_get_boolean(focused), @@ -109,18 +118,21 @@ workspace_from_json(const struct json_object *json, struct workspace *ws) static void workspace_free(struct workspace *ws) { - free(ws->name); - free(ws->output); - free(ws->window.title); - free(ws->window.application); + free(ws->name); ws->name = NULL; + free(ws->output); ws->output = NULL; + free(ws->window.title); ws->window.title = NULL; + free(ws->window.application); ws->window.application = NULL; } static void -workspaces_free(struct private *m) +workspaces_free(struct private *m, bool free_persistent) { - tll_foreach(m->workspaces, it) - workspace_free(&it->item); - tll_free(m->workspaces); + tll_foreach(m->workspaces, it) { + if (free_persistent || !it->item.persistent) { + workspace_free(&it->item); + tll_remove(m->workspaces, it); + } + } } @@ -234,6 +246,35 @@ handle_subscribe_reply(int type, const struct json_object *json, void *_m) return true; } +static bool +workspace_update_or_add(struct private *m, const struct json_object *ws_json) +{ + struct json_object *name; + if (!json_object_object_get_ex(ws_json, "name", &name)) + return false; + + const char *name_as_string = json_object_get_string(name); + struct workspace *already_exists = workspace_lookup(m, name_as_string); + + if (already_exists != NULL) { + bool persistent = already_exists->persistent; + assert(persistent); + + workspace_free(already_exists); + if (!workspace_from_json(ws_json, already_exists)) + return false; + already_exists->persistent = persistent; + } else { + struct workspace ws; + if (!workspace_from_json(ws_json, &ws)) + return false; + + workspace_add(m, ws); + } + + return true; +} + static bool handle_get_workspaces_reply(int type, const struct json_object *json, void *_mod) { @@ -242,25 +283,23 @@ handle_get_workspaces_reply(int type, const struct json_object *json, void *_mod mtx_lock(&mod->lock); - workspaces_free(m); + workspaces_free(m, false); m->dirty = true; size_t count = json_object_array_length(json); for (size_t i = 0; i < count; i++) { - struct workspace ws = {}; - if (!workspace_from_json(json_object_array_get_idx(json, i), &ws)) { - workspaces_free(m); - mtx_unlock(&mod->lock); - return false; - } - - LOG_DBG("#%zu: %s", i, m->workspaces.v[i].name); - workspace_add(m, ws); + if (!workspace_update_or_add(m, json_object_array_get_idx(json, i))) + goto err; } mtx_unlock(&mod->lock); return true; + +err: + workspaces_free(m, false); + mtx_unlock(&mod->lock); + return false; } static bool @@ -301,24 +340,21 @@ handle_workspace_event(int type, const struct json_object *json, void *_mod) mtx_lock(&mod->lock); if (is_init) { - struct workspace *already_exists = workspace_lookup(m, current_name); - if (already_exists != NULL) { - LOG_WARN("workspace 'init' event for already existing workspace: %s", current_name); - workspace_free(already_exists); - if (!workspace_from_json(current, already_exists)) - goto err; - } else { - struct workspace ws; - if (!workspace_from_json(current, &ws)) - goto err; - - workspace_add(m, ws); - } + if (!workspace_update_or_add(m, current)) + goto err; } else if (is_empty) { - assert(workspace_lookup(m, current_name) != NULL); - workspace_del(m, current_name); + struct workspace *ws = workspace_lookup(m, current_name); + assert(ws != NULL); + + if (!ws->persistent) + workspace_del(m, current_name); + else { + workspace_free(ws); + ws->name = strdup(current_name); + assert(ws->persistent); + } } else if (is_focused) { @@ -340,7 +376,7 @@ handle_workspace_event(int type, const struct json_object *json, void *_mod) /* Mark all workspaces on current's output invisible */ tll_foreach(m->workspaces, it) { struct workspace *ws = &it->item; - if (strcmp(ws->output, w->output) == 0) + if (ws->output != NULL && strcmp(ws->output, w->output) == 0) ws->visible = false; } @@ -558,6 +594,18 @@ run(struct module *mod) return 1; } + struct private *m = mod->private; + for (size_t i = 0; i < m->persistent_count; i++) { + const char *name_as_string = m->persistent_workspaces[i]; + + struct workspace ws = { + .name = strdup(name_as_string), + .name_as_int = workspace_name_as_int(name_as_string), + .persistent = true, + }; + workspace_add(m, ws); + } + i3_send_pkg(sock, I3_IPC_MESSAGE_TYPE_GET_VERSION, NULL); i3_send_pkg(sock, I3_IPC_MESSAGE_TYPE_SUBSCRIBE, "[\"workspace\", \"window\", \"mode\"]"); i3_send_pkg(sock, I3_IPC_MESSAGE_TYPE_GET_WORKSPACES, NULL); @@ -589,7 +637,11 @@ destroy(struct module *mod) } free(m->ws_content.v); - workspaces_free(m); + workspaces_free(m, true); + + for (size_t i = 0; i < m->persistent_count; i++) + free(m->persistent_workspaces[i]); + free(m->persistent_workspaces); free(m->mode); free(m); @@ -693,7 +745,9 @@ struct i3_workspaces { static struct module * i3_new(struct i3_workspaces workspaces[], size_t workspace_count, - int left_spacing, int right_spacing, enum sort_mode sort_mode) + int left_spacing, int right_spacing, enum sort_mode sort_mode, + size_t persistent_count, + const char *persistent_workspaces[static persistent_count]) { struct private *m = calloc(1, sizeof(*m)); @@ -711,6 +765,13 @@ i3_new(struct i3_workspaces workspaces[], size_t workspace_count, m->sort_mode = sort_mode; + m->persistent_count = persistent_count; + m->persistent_workspaces = calloc( + persistent_count, sizeof(m->persistent_workspaces[0])); + + for (size_t i = 0; i < persistent_count; i++) + m->persistent_workspaces[i] = strdup(persistent_workspaces[i]); + struct module *mod = module_common_new(); mod->private = m; mod->run = &run; @@ -728,6 +789,7 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) const struct yml_node *left_spacing = yml_get_value(node, "left-spacing"); const struct yml_node *right_spacing = yml_get_value(node, "right-spacing"); const struct yml_node *sort = yml_get_value(node, "sort"); + const struct yml_node *persistent = yml_get_value(node, "persistent"); int left = spacing != NULL ? yml_value_as_int(spacing) : left_spacing != NULL ? yml_value_as_int(left_spacing) : 0; @@ -740,6 +802,20 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) strcmp(sort_value, "none") == 0 ? SORT_NONE : strcmp(sort_value, "ascending") == 0 ? SORT_ASCENDING : SORT_DESCENDING; + const size_t persistent_count = + persistent != NULL ? yml_list_length(persistent) : 0; + const char *persistent_workspaces[persistent_count]; + + if (persistent != NULL) { + size_t idx = 0; + for (struct yml_list_iter it = yml_list_iter(persistent); + it.node != NULL; + yml_list_next(&it), idx++) + { + persistent_workspaces[idx] = yml_value_as_string(it.node); + } + } + struct i3_workspaces workspaces[yml_dict_length(c)]; size_t idx = 0; @@ -751,7 +827,8 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) workspaces[idx].content = conf_to_particle(it.value, inherited); } - return i3_new(workspaces, yml_dict_length(c), left, right, sort_mode); + return i3_new(workspaces, yml_dict_length(c), left, right, sort_mode, + persistent_count, persistent_workspaces); } static bool @@ -791,6 +868,12 @@ verify_sort(keychain_t *chain, const struct yml_node *node) chain, node, (const char *[]){"none", "ascending", "descending"}, 3); } +static bool +verify_persistent(keychain_t *chain, const struct yml_node *node) +{ + return conf_verify_list(chain, node, &conf_verify_string); +} + static bool verify_conf(keychain_t *chain, const struct yml_node *node) { @@ -799,6 +882,7 @@ verify_conf(keychain_t *chain, const struct yml_node *node) {"left-spacing", false, &conf_verify_int}, {"right-spacing", false, &conf_verify_int}, {"sort", false, &verify_sort}, + {"persistent", false, &verify_persistent}, {"content", true, &verify_content}, {"anchors", false, NULL}, {NULL, false, NULL}, From b4ce851b4d75c5baeab1749fd85f98706b6be7ed Mon Sep 17 00:00:00 2001 From: Rafael Escobar Date: Tue, 27 Jul 2021 21:18:00 -0300 Subject: [PATCH 189/611] doc: fix typo and missing values --- doc/yambar-modules-network.5.scd | 1 + doc/yambar-particles.5.scd | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/yambar-modules-network.5.scd b/doc/yambar-modules-network.5.scd index cb78809..c8a66d8 100644 --- a/doc/yambar-modules-network.5.scd +++ b/doc/yambar-modules-network.5.scd @@ -48,6 +48,7 @@ address. :[ *Description* | name : string +: yes : Name of network interface to monitor # EXAMPLES diff --git a/doc/yambar-particles.5.scd b/doc/yambar-particles.5.scd index 08977c5..4ded3c1 100644 --- a/doc/yambar-particles.5.scd +++ b/doc/yambar-particles.5.scd @@ -82,7 +82,7 @@ content: # EMPTY This particle is a place-holder. While it does not render any tags, -margins and decortions are rendered. +margins and decorations are rendered. ## CONFIGURATION From 8c095eb4237e38b56f4a3b3fb3d4e7d79f05cf26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 28 Jul 2021 10:44:39 +0200 Subject: [PATCH 190/611] changelog: update contributors list --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5af6ce7..4156e05 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,8 @@ ### Security ### Contributors +* [paemuri](https://codeberg.org/paemuri) + ## 1.6.2 From b97ba80aea3d15978eb161434a411aebb0bf28ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 27 Jul 2021 16:32:13 +0200 Subject: [PATCH 191/611] bar: add border.{left,right,top,bottom}-width This allows you to configure the width of each side of the border individually. border.width can still be used, and will set all four borders to the same width. Closes #77 --- CHANGELOG.md | 5 +++++ bar/bar.c | 56 +++++++++++++++++++++++++++++++----------------- bar/bar.h | 3 ++- bar/private.h | 3 ++- bar/wayland.c | 2 +- config-verify.c | 4 ++++ config.c | 18 +++++++++++++++- doc/yambar.5.scd | 18 +++++++++++++++- 8 files changed, 84 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4156e05..b9a6669 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,11 @@ * i3: `persistent` attribute, allowing persistent workspaces (https://codeberg.org/dnkl/yambar/issues/72). +* bar: `border.{left,right,top,bottom}-width`, allowing the width of + each side of the border to be configured + individually. `border.width` is now a short-hand for setting all + four borders to the same value + (https://codeberg.org/dnkl/yambar/issues/77). ### Changed diff --git a/bar/bar.c b/bar/bar.c index 9806f44..4829162 100644 --- a/bar/bar.c +++ b/bar/bar.c @@ -67,16 +67,28 @@ expose(const struct bar *_bar) PIXMAN_OP_SRC, pix, &bar->background, 1, &(pixman_rectangle16_t){0, 0, bar->width, bar->height_with_border}); - if (bar->border.width > 0) { - pixman_image_fill_rectangles( - PIXMAN_OP_OVER, pix, &bar->border.color, 4, - (pixman_rectangle16_t[]){ - {0, 0, bar->width, bar->border.width}, - {0, 0, bar->border.width, bar->height_with_border}, - {bar->width - bar->border.width, 0, bar->border.width, bar->height_with_border}, - {0, bar->height_with_border - bar->border.width, bar->width, bar->border.width}, - }); - } + pixman_image_fill_rectangles( + PIXMAN_OP_OVER, pix, &bar->border.color, 4, + (pixman_rectangle16_t[]){ + /* Left */ + {0, 0, bar->border.left_width, bar->height_with_border}, + + /* Right */ + {bar->width - bar->border.right_width, + 0, bar->border.right_width, bar->height_with_border}, + + /* Top */ + {bar->border.left_width, + 0, + bar->width - bar->border.left_width - bar->border.right_width, + bar->border.top_width}, + + /* Bottom */ + {bar->border.left_width, + bar->height_with_border - bar->border.bottom_width, + bar->width - bar->border.left_width - bar->border.right_width, + bar->border.bottom_width}, + }); for (size_t i = 0; i < bar->left.count; i++) { struct module *m = bar->left.mods[i]; @@ -105,8 +117,8 @@ expose(const struct bar *_bar) int left_width, center_width, right_width; calculate_widths(bar, &left_width, ¢er_width, &right_width); - int y = bar->border.width; - int x = bar->border.width + bar->left_margin - bar->left_spacing; + int y = bar->border.top_width; + int x = bar->border.left_width + bar->left_margin - bar->left_spacing; for (size_t i = 0; i < bar->left.count; i++) { const struct exposable *e = bar->left.exps[i]; e->expose(e, pix, x + bar->left_spacing, y, bar->height); @@ -124,7 +136,7 @@ expose(const struct bar *_bar) right_width + bar->left_spacing + bar->right_margin + - bar->border.width); + bar->border.right_width); for (size_t i = 0; i < bar->right.count; i++) { const struct exposable *e = bar->right.exps[i]; @@ -156,9 +168,9 @@ on_mouse(struct bar *_bar, enum mouse_event event, enum mouse_button btn, { struct private *bar = _bar->private; - if ((y < bar->border.width || - y >= (bar->height_with_border - bar->border.width)) || - (x < bar->border.width || x >= (bar->width - bar->border.width))) + if ((y < bar->border.top_width || + y >= (bar->height_with_border - bar->border.bottom_width)) || + (x < bar->border.left_width || x >= (bar->width - bar->border.right_width))) { set_cursor(_bar, "left_ptr"); return; @@ -167,7 +179,7 @@ on_mouse(struct bar *_bar, enum mouse_event event, enum mouse_button btn, int left_width, center_width, right_width; calculate_widths(bar, &left_width, ¢er_width, &right_width); - int mx = bar->border.width + bar->left_margin - bar->left_spacing; + int mx = bar->border.left_width + bar->left_margin - bar->left_spacing; for (size_t i = 0; i < bar->left.count; i++) { struct exposable *e = bar->left.exps[i]; @@ -198,7 +210,7 @@ on_mouse(struct bar *_bar, enum mouse_event event, enum mouse_button btn, mx = bar->width - (right_width + bar->left_spacing + bar->right_margin + - bar->border.width); + bar->border.right_width); for (size_t i = 0; i < bar->right.count; i++) { struct exposable *e = bar->right.exps[i]; @@ -236,7 +248,8 @@ run(struct bar *_bar) { struct private *bar = _bar->private; - bar->height_with_border = bar->height + 2 * bar->border.width; + bar->height_with_border = + bar->height + bar->border.top_width + bar->border.bottom_width; if (!bar->backend.iface->setup(_bar)) { bar->backend.iface->cleanup(_bar); @@ -409,7 +422,10 @@ bar_new(const struct bar_config *config) priv->left_margin = config->left_margin; priv->right_margin = config->right_margin; priv->trackpad_sensitivity = config->trackpad_sensitivity; - priv->border.width = config->border.width; + priv->border.left_width = config->border.left_width; + priv->border.right_width = config->border.right_width; + priv->border.top_width = config->border.top_width; + priv->border.bottom_width = config->border.bottom_width; priv->border.color = config->border.color; priv->border.left_margin = config->border.left_margin; priv->border.right_margin = config->border.right_margin; diff --git a/bar/bar.h b/bar/bar.h index 4e82534..5d154dd 100644 --- a/bar/bar.h +++ b/bar/bar.h @@ -30,7 +30,8 @@ struct bar_config { pixman_color_t background; struct { - int width; + int left_width, right_width; + int top_width, bottom_width; pixman_color_t color; int left_margin, right_margin; int top_margin, bottom_margin; diff --git a/bar/private.h b/bar/private.h index eed532b..56e59bb 100644 --- a/bar/private.h +++ b/bar/private.h @@ -15,7 +15,8 @@ struct private { pixman_color_t background; struct { - int width; + int left_width, right_width; + int top_width, bottom_width; pixman_color_t color; int left_margin, right_margin; int top_margin, bottom_margin; diff --git a/bar/wayland.c b/bar/wayland.c index 7e63cf0..ef62025 100644 --- a/bar/wayland.c +++ b/bar/wayland.c @@ -891,7 +891,7 @@ update_size(struct wayland_backend *backend) int height = bar->height_with_border; height /= scale; height *= scale; - bar->height = height - 2 * bar->border.width; + bar->height = height - bar->border.top_width - bar->border.bottom_width; bar->height_with_border = height; zwlr_layer_surface_v1_set_size( diff --git a/config-verify.c b/config-verify.c index 6c09a4a..7c4f365 100644 --- a/config-verify.c +++ b/config-verify.c @@ -371,6 +371,10 @@ verify_bar_border(keychain_t *chain, const struct yml_node *node) { static const struct attr_info attrs[] = { {"width", false, &conf_verify_int}, + {"left-width", false, &conf_verify_int}, + {"right-width", false, &conf_verify_int}, + {"top-width", false, &conf_verify_int}, + {"bottom-width", false, &conf_verify_int}, {"color", false, &conf_verify_color}, {"margin", false, &conf_verify_int}, {"left-margin", false, &conf_verify_int}, diff --git a/config.c b/config.c index bc6d7a7..241e25b 100644 --- a/config.c +++ b/config.c @@ -261,6 +261,10 @@ conf_to_bar(const struct yml_node *bar, enum bar_backend backend) const struct yml_node *border = yml_get_value(bar, "border"); if (border != NULL) { const struct yml_node *width = yml_get_value(border, "width"); + const struct yml_node *left_width = yml_get_value(border, "left-width"); + const struct yml_node *right_width = yml_get_value(border, "right-width"); + const struct yml_node *top_width = yml_get_value(border, "top-width"); + const struct yml_node *bottom_width = yml_get_value(border, "bottom-width"); const struct yml_node *color = yml_get_value(border, "color"); const struct yml_node *margin = yml_get_value(border, "margin"); const struct yml_node *left_margin = yml_get_value(border, "left-margin"); @@ -269,7 +273,19 @@ conf_to_bar(const struct yml_node *bar, enum bar_backend backend) const struct yml_node *bottom_margin = yml_get_value(border, "bottom-margin"); if (width != NULL) - conf.border.width = yml_value_as_int(width); + conf.border.left_width = + conf.border.right_width = + conf.border.top_width = + conf.border.bottom_width = yml_value_as_int(width); + + if (left_width != NULL) + conf.border.left_width = yml_value_as_int(left_width); + if (right_width != NULL) + conf.border.right_width = yml_value_as_int(right_width); + if (top_width != NULL) + conf.border.top_width = yml_value_as_int(top_width); + if (bottom_width != NULL) + conf.border.bottom_width = yml_value_as_int(bottom_width); if (color != NULL) conf.border.color = conf_to_color(color); diff --git a/doc/yambar.5.scd b/doc/yambar.5.scd index d352e0e..9e1eefa 100644 --- a/doc/yambar.5.scd +++ b/doc/yambar.5.scd @@ -73,10 +73,26 @@ types that are frequently used: : associative array : no : Configures the border around the status bar +| border.left-width +: int +: no +: Width of the border on the left side, in pixels +| border.right-width +: int +: no +: Width of the border on the right side, in pixels +| border.top-width +: int +: no +: Width of the border on the top side, in pixels +| border.bottom-width +: int +: no +: Width of the border on the bottom side, in pixels | border.width : int : no -: Width, in pixels, of the border +: Short-hand for setting _border.left/right/top/bottom-width_ | border.color : color : no From 7d94631991a5d530884664705341a4410ff45f74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89rico=20Nogueira?= Date: Thu, 29 Jul 2021 15:57:00 -0300 Subject: [PATCH 192/611] bar: fix typo. Running 'yambar --backend=wayland' would always error out saying it was built without wayland support. --- bar/bar.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bar/bar.c b/bar/bar.c index 4829162..c7b1ea5 100644 --- a/bar/bar.c +++ b/bar/bar.c @@ -399,7 +399,7 @@ bar_new(const struct bar_config *config) break; case BAR_BACKEND_WAYLAND: -#if defined(BAR_WAYLAND) +#if defined(ENABLE_WAYLAND) backend_data = bar_backend_wayland_new(); backend_iface = &wayland_backend_iface; #else From 64df5d9806fa9ef5e6760ba678688f7af7505a41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 30 Jul 2021 10:31:31 +0200 Subject: [PATCH 193/611] changelog: --backend=wayland --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b9a6669..6d76064 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,10 +23,16 @@ ### Deprecated ### Removed ### Fixed + +* `yambar --backend=wayland` always erroring out with _”yambar was + compiled without the Wayland backend”_. + + ### Security ### Contributors * [paemuri](https://codeberg.org/paemuri) +* [ericonr](https://codeberg.org/ericonr) ## 1.6.2 From a210d3332017838faaa7b67331f5d44e410b11ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 9 Aug 2021 18:21:45 +0200 Subject: [PATCH 194/611] =?UTF-8?q?particle/progress-bar:=20fix=20?= =?UTF-8?q?=E2=80=98where=E2=80=99=20tag=20regression?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After implementing support for more mouse buttons (and scrolling), the ‘where’ tag stopped working in the progress-bar. This patch repairs it. --- CHANGELOG.md | 2 ++ particles/progress-bar.c | 40 ++++++++++++++++------------------------ 2 files changed, 18 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d76064..9475062 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,8 @@ * `yambar --backend=wayland` always erroring out with _”yambar was compiled without the Wayland backend”_. +* Regression: `{where}` tag not being expanded in progress-bar + `on-click` handlers. ### Security diff --git a/particles/progress-bar.c b/particles/progress-bar.c index a825117..52d1e74 100644 --- a/particles/progress-bar.c +++ b/particles/progress-bar.c @@ -87,30 +87,6 @@ static void on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event, enum mouse_button btn, int x, int y) { - if ((event == ON_MOUSE_MOTION && - exposable->particle->have_on_click_template) || - exposable->on_click[btn] != NULL) - { - exposable_default_on_mouse(exposable, bar, event, btn, x, y); - return; - } - - /* - * Hack-warning! - * - * In order to pass the *clicked* position to the on_click - * handler, we expand the handler *again* (first time would be - * when the particle instantiated us). - * - * We pass a single tag, "where", which is a percentage value. - * - * Keep a reference to the un-expanded string, to be able to reset - * it after executing the handler. - * - * Note that we only consider the actual progress bar to be - * clickable. This means we ignore the start and end markers. - */ - const struct particle *p = exposable->particle; const struct eprivate *e = exposable->private; @@ -150,6 +126,22 @@ on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event, return; } + /* + * Hack-warning! + * + * In order to pass the *clicked* position to the on_click + * handler, we expand the handler *again* (first time would be + * when the particle instantiated us). + * + * We pass a single tag, "where", which is a percentage value. + * + * Keep a reference to the un-expanded string, to be able to + * reset it after executing the handler. + * + * Note that we only consider the actual progress bar to be + * clickable. This means we ignore the start and end markers. + */ + /* Remember the original handler, so that we can restore it */ char *original[MOUSE_BTN_COUNT]; for (size_t i = 0; i < MOUSE_BTN_COUNT; i++) From 74016d7d3349d8600933a1e88b62caab12fc1009 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 2 Aug 2021 19:29:52 +0200 Subject: [PATCH 195/611] =?UTF-8?q?bar:=20add=20bar->output=5Fname(),=20re?= =?UTF-8?q?turns=20the=20name=20of=20the=20output=20we=E2=80=99re=20on?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bar/backend.h | 1 + bar/bar.c | 8 ++++++++ bar/bar.h | 2 ++ bar/wayland.c | 10 ++++++++++ bar/xcb.c | 8 ++++++++ 5 files changed, 29 insertions(+) diff --git a/bar/backend.h b/bar/backend.h index d365da6..47fae95 100644 --- a/bar/backend.h +++ b/bar/backend.h @@ -14,4 +14,5 @@ struct backend { void (*commit)(const struct bar *bar); void (*refresh)(const struct bar *bar); void (*set_cursor)(struct bar *bar, const char *cursor); + const char *(*output_name)(const struct bar *bar); }; diff --git a/bar/bar.c b/bar/bar.c index c7b1ea5..9d1b0cc 100644 --- a/bar/bar.c +++ b/bar/bar.c @@ -162,6 +162,13 @@ set_cursor(struct bar *bar, const char *cursor) b->backend.iface->set_cursor(bar, cursor); } +static const char * +output_name(const struct bar *bar) +{ + const struct private *b = bar->private; + return b->backend.iface->output_name(bar); +} + static void on_mouse(struct bar *_bar, enum mouse_event event, enum mouse_button btn, int x, int y) @@ -456,6 +463,7 @@ bar_new(const struct bar_config *config) bar->destroy = &destroy; bar->refresh = &refresh; bar->set_cursor = &set_cursor; + bar->output_name = &output_name; for (size_t i = 0; i < priv->left.count; i++) priv->left.mods[i]->bar = bar; diff --git a/bar/bar.h b/bar/bar.h index 5d154dd..45fb328 100644 --- a/bar/bar.h +++ b/bar/bar.h @@ -12,6 +12,8 @@ struct bar { void (*refresh)(const struct bar *bar); void (*set_cursor)(struct bar *bar, const char *cursor); + + const char *(*output_name)(const struct bar *bar); }; enum bar_location { BAR_TOP, BAR_BOTTOM }; diff --git a/bar/wayland.c b/bar/wayland.c index ef62025..cb10eae 100644 --- a/bar/wayland.c +++ b/bar/wayland.c @@ -1308,6 +1308,15 @@ set_cursor(struct bar *_bar, const char *cursor) update_cursor_surface(backend, seat); } +static const char * +output_name(const struct bar *_bar) +{ + const struct private *bar = _bar->private; + const struct wayland_backend *backend = bar->backend.data; + + return backend->monitor != NULL ? backend->monitor->name : NULL; +} + const struct backend wayland_backend_iface = { .setup = &setup, .cleanup = &cleanup, @@ -1315,4 +1324,5 @@ const struct backend wayland_backend_iface = { .commit = &commit, .refresh = &refresh, .set_cursor = &set_cursor, + .output_name = &output_name, }; diff --git a/bar/xcb.c b/bar/xcb.c index 9c452da..4b0e7c1 100644 --- a/bar/xcb.c +++ b/bar/xcb.c @@ -462,6 +462,13 @@ set_cursor(struct bar *_bar, const char *cursor) backend->conn, backend->win, XCB_CW_CURSOR, &backend->cursor); } +static const char * +output_name(const struct bar *_bar) +{ + /* Not implemented */ + return NULL; +} + const struct backend xcb_backend_iface = { .setup = &setup, .cleanup = &cleanup, @@ -469,4 +476,5 @@ const struct backend xcb_backend_iface = { .commit = &commit, .refresh = &refresh, .set_cursor = &set_cursor, + .output_name = &output_name, }; From 1c6c73928b841efa9c043045a6feee4d3cfe0955 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 2 Aug 2021 19:30:22 +0200 Subject: [PATCH 196/611] config-verify: add conf_verify_bool() --- config-verify.c | 11 +++++++++++ config-verify.h | 1 + 2 files changed, 12 insertions(+) diff --git a/config-verify.c b/config-verify.c index 7c4f365..5d4089a 100644 --- a/config-verify.c +++ b/config-verify.c @@ -50,6 +50,17 @@ conf_verify_int(keychain_t *chain, const struct yml_node *node) return false; } +bool +conf_verify_bool(keychain_t *chain, const struct yml_node *node) +{ + if (yml_value_is_bool(node)) + return true; + + LOG_ERR("%s: value is not a boolean: '%s'", + conf_err_prefix(chain, node), yml_value_as_string(node)); + return false; +} + bool conf_verify_list(keychain_t *chain, const struct yml_node *node, bool (*verify)(keychain_t *chain, const struct yml_node *node)) diff --git a/config-verify.h b/config-verify.h index dccaf5f..5afe3f6 100644 --- a/config-verify.h +++ b/config-verify.h @@ -32,6 +32,7 @@ const char *conf_err_prefix( bool conf_verify_string(keychain_t *chain, const struct yml_node *node); bool conf_verify_int(keychain_t *chain, const struct yml_node *node); +bool conf_verify_bool(keychain_t *chain, const struct yml_node *node); bool conf_verify_enum(keychain_t *chain, const struct yml_node *node, const char *values[], size_t count); From ba7b9e6244d5f28ffc6220190fec2c7b5548234b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 2 Aug 2021 19:31:01 +0200 Subject: [PATCH 197/611] =?UTF-8?q?module/river:=20add=20=E2=80=98per-outp?= =?UTF-8?q?ut=E2=80=99=20attribute?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When set, river tags and seats’ view titles apply to the output yambar is on, only. The default is disabled, which implements the old behavior, where river tags and seats’ view titles represent the union of all outputs. --- CHANGELOG.md | 1 + doc/yambar-modules-river.5.scd | 14 +++++++++---- modules/river.c | 38 +++++++++++++++++++++++++++------- 3 files changed, 41 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9475062..6d27aea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ individually. `border.width` is now a short-hand for setting all four borders to the same value (https://codeberg.org/dnkl/yambar/issues/77). +* river: `per-output: false|true`. ### Changed diff --git a/doc/yambar-modules-river.5.scd b/doc/yambar-modules-river.5.scd index 46607a2..570e8a5 100644 --- a/doc/yambar-modules-river.5.scd +++ b/doc/yambar-modules-river.5.scd @@ -12,8 +12,8 @@ about the river tags. It has an interface similar to the i3/sway module. The configuration for the river module specifies one _title_ particle, -which will be instantiated with tags representing the currently active -seat and the currently focused view's title. +which will be instantiated once for each seat, with tags representing +the seats' name and the title of the seats' currently focused view. It also specifies a _content_ template particle, which is instantiated once for all 32 river tags. This means you probably want to use a @@ -41,10 +41,10 @@ once for all 32 river tags. This means you probably want to use a : Set to *focused* if _focused_ is true, *unfocused* if _visible_ is true, but _focused_ is false, or *invisible* if the river tag is not visible on any monitors. | seat : string -: The name of the currently active seat (*title* particle only, see CONFIGURATION) +: The name of the seat (*title* particle only, see CONFIGURATION) | title : string -: The focused view's title (*title* particle only, see CONFIGURATION) +: The seat's focused view's title (*title* particle only, see CONFIGURATION) # CONFIGURATION @@ -60,6 +60,12 @@ once for all 32 river tags. This means you probably want to use a : particle : yes : Template particle that will be instantiated once for all of the 32 river tags. +| per-output +: bool +: no +: When set to false (the default), tags reflect the union of all + outputs. When set to true, tags reflect river tags and seats for + the output yambar is on only. # EXAMPLES diff --git a/modules/river.c b/modules/river.c index 0cdab38..9b494f3 100644 --- a/modules/river.c +++ b/modules/river.c @@ -48,6 +48,7 @@ struct private { struct zriver_status_manager_v1 *status_manager; struct particle *template; struct particle *title; + bool per_output; bool is_starting_up; tll(struct output) outputs; @@ -76,6 +77,8 @@ content(struct module *mod) { const struct private *m = mod->private; + const char *output_bar_is_on = mod->bar->output_name(mod->bar); + mtx_lock(&m->mod->lock); uint32_t output_focused = 0; @@ -85,6 +88,13 @@ content(struct module *mod) tll_foreach(m->outputs, it) { const struct output *output = &it->item; + if (m->per_output && + output_bar_is_on != NULL && output->name != NULL && + strcmp(output->name, output_bar_is_on) != 0) + { + continue; + } + output_focused |= output->focused; occupied |= output->occupied; @@ -348,7 +358,6 @@ unfocused_output(void *data, struct zriver_seat_status_v1 *zriver_seat_status_v1 mtx_lock(&mod->lock); { - struct output *output = NULL; tll_foreach(m->outputs, it) { if (it->item.wl_output == wl_output) { @@ -382,13 +391,21 @@ focused_view(void *data, struct zriver_seat_status_v1 *zriver_seat_status_v1, LOG_DBG("seat: %s: focused view: %s", seat->name, title); - mtx_lock(&mod->lock); + const char *output_bar_is_on = mod->bar->output_name(mod->bar); + + if (!seat->m->per_output || + (output_bar_is_on != NULL && + seat->output != NULL && seat->output->name != NULL && + strcmp(output_bar_is_on, seat->output->name) == 0)) { - free(seat->title); - seat->title = title != NULL ? strdup(title) : NULL; + mtx_lock(&mod->lock); + { + free(seat->title); + seat->title = title != NULL ? strdup(title) : NULL; + } + mtx_unlock(&mod->lock); + mod->bar->refresh(mod->bar); } - mtx_unlock(&mod->lock); - mod->bar->refresh(mod->bar); } static const struct zriver_seat_status_v1_listener river_seat_status_listener = { @@ -646,11 +663,12 @@ out: } static struct module * -river_new(struct particle *template, struct particle *title) +river_new(struct particle *template, struct particle *title, bool per_output) { struct private *m = calloc(1, sizeof(*m)); m->template = template; m->title = title; + m->per_output = per_output; m->is_starting_up = true; struct module *mod = module_common_new(); @@ -668,9 +686,12 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) { const struct yml_node *c = yml_get_value(node, "content"); const struct yml_node *title = yml_get_value(node, "title"); + const struct yml_node *per_output = yml_get_value(node, "per-output"); + return river_new( conf_to_particle(c, inherited), - title != NULL ? conf_to_particle(title, inherited) : NULL); + title != NULL ? conf_to_particle(title, inherited) : NULL, + per_output != NULL ? yml_value_as_bool(per_output) : false); } static bool @@ -678,6 +699,7 @@ verify_conf(keychain_t *chain, const struct yml_node *node) { static const struct attr_info attrs[] = { {"title", false, &conf_verify_particle}, + {"per-output", false, &conf_verify_bool}, MODULE_COMMON_ATTRS, }; From 910522262f9fa0fc9ad194620fe727dc23fe6a2c Mon Sep 17 00:00:00 2001 From: Nulo Date: Wed, 11 Aug 2021 17:03:34 -0300 Subject: [PATCH 198/611] Only add spacing if the module is not empty If the module is empty (width is 0) no spacing will be rendered for it. This makes modules that auto-hide (for example, network modules for interfaces not used all of the time) occupy no space in the bar. --- CHANGELOG.md | 5 +++++ bar/bar.c | 24 ++++++++++++++++++------ 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d27aea..b833b22 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,10 @@ ### Changed + +* bar: hide spacing of modules which are empty (width = 0) + + ### Deprecated ### Removed ### Fixed @@ -36,6 +40,7 @@ * [paemuri](https://codeberg.org/paemuri) * [ericonr](https://codeberg.org/ericonr) +* [Nulo](https://nulo.in) ## 1.6.2 diff --git a/bar/bar.c b/bar/bar.c index 9d1b0cc..9d07830 100644 --- a/bar/bar.c +++ b/bar/bar.c @@ -38,17 +38,20 @@ calculate_widths(const struct private *b, int *left, int *center, int *right) for (size_t i = 0; i < b->left.count; i++) { struct exposable *e = b->left.exps[i]; - *left += b->left_spacing + e->width + b->right_spacing; + if (e->width > 0) + *left += b->left_spacing + e->width + b->right_spacing; } for (size_t i = 0; i < b->center.count; i++) { struct exposable *e = b->center.exps[i]; - *center += b->left_spacing + e->width + b->right_spacing; + if (e->width > 0) + *center += b->left_spacing + e->width + b->right_spacing; } for (size_t i = 0; i < b->right.count; i++) { struct exposable *e = b->right.exps[i]; - *right += b->left_spacing + e->width + b->right_spacing; + if (e->width > 0) + *right += b->left_spacing + e->width + b->right_spacing; } /* No spacing on the edges (that's what the margins are for) */ @@ -122,14 +125,16 @@ expose(const struct bar *_bar) for (size_t i = 0; i < bar->left.count; i++) { const struct exposable *e = bar->left.exps[i]; e->expose(e, pix, x + bar->left_spacing, y, bar->height); - x += bar->left_spacing + e->width + bar->right_spacing; + if (e->width > 0) + x += bar->left_spacing + e->width + bar->right_spacing; } x = bar->width / 2 - center_width / 2 - bar->left_spacing; for (size_t i = 0; i < bar->center.count; i++) { const struct exposable *e = bar->center.exps[i]; e->expose(e, pix, x + bar->left_spacing, y, bar->height); - x += bar->left_spacing + e->width + bar->right_spacing; + if (e->width > 0) + x += bar->left_spacing + e->width + bar->right_spacing; } x = bar->width - ( @@ -141,7 +146,8 @@ expose(const struct bar *_bar) for (size_t i = 0; i < bar->right.count; i++) { const struct exposable *e = bar->right.exps[i]; e->expose(e, pix, x + bar->left_spacing, y, bar->height); - x += bar->left_spacing + e->width + bar->right_spacing; + if (e->width > 0) + x += bar->left_spacing + e->width + bar->right_spacing; } bar->backend.iface->commit(_bar); @@ -190,6 +196,8 @@ on_mouse(struct bar *_bar, enum mouse_event event, enum mouse_button btn, for (size_t i = 0; i < bar->left.count; i++) { struct exposable *e = bar->left.exps[i]; + if (e->width == 0) continue; + mx += bar->left_spacing; if (x >= mx && x < mx + e->width) { if (e->on_mouse != NULL) @@ -204,6 +212,8 @@ on_mouse(struct bar *_bar, enum mouse_event event, enum mouse_button btn, for (size_t i = 0; i < bar->center.count; i++) { struct exposable *e = bar->center.exps[i]; + if (e->width == 0) continue; + mx += bar->left_spacing; if (x >= mx && x < mx + e->width) { if (e->on_mouse != NULL) @@ -222,6 +232,8 @@ on_mouse(struct bar *_bar, enum mouse_event event, enum mouse_button btn, for (size_t i = 0; i < bar->right.count; i++) { struct exposable *e = bar->right.exps[i]; + if (e->width == 0) continue; + mx += bar->left_spacing; if (x >= mx && x < mx + e->width) { if (e->on_mouse != NULL) From 58a52512ddb366e6a4274206c794efc8bdd922af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 12 Aug 2021 19:31:22 +0200 Subject: [PATCH 199/611] changelog: slight rewording --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b833b22..e82a0f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,7 +22,7 @@ ### Changed -* bar: hide spacing of modules which are empty (width = 0) +* bar: do not add `spacing` around empty (zero-width) modules. ### Deprecated From 7d3851046e0dda9b24ebe2eb59f3a725bec1d14a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 15 Aug 2021 11:41:12 +0200 Subject: [PATCH 200/611] log: pull in log.{c,h} from foot --- log.c | 237 ++++++++++++++++++++++++++++------------------ log.h | 68 +++++++++---- main.c | 4 +- modules/network.c | 2 +- modules/script.c | 2 +- particle.c | 2 +- 6 files changed, 197 insertions(+), 118 deletions(-) diff --git a/log.c b/log.c index c5d9093..52595bc 100644 --- a/log.c +++ b/log.c @@ -1,41 +1,55 @@ #include "log.h" -#include -#include -#include -#include -#include #include -#include -#include - +#include +#include +#include +#include +#include +#include #include +#include +#include + +#include "debug.h" + +#define ALEN(v) (sizeof(v) / sizeof((v)[0])) +#define UNUSED __attribute__((unused)) static bool colorize = false; static bool do_syslog = true; +static enum log_class log_level = LOG_CLASS_NONE; + +static const struct { + const char name[8]; + const char log_prefix[7]; + uint8_t color; + int syslog_equivalent; +} log_level_map[] = { + [LOG_CLASS_NONE] = {"none", "none", 5, -1}, + [LOG_CLASS_ERROR] = {"error", " err", 31, LOG_ERR}, + [LOG_CLASS_WARNING] = {"warning", "warn", 33, LOG_WARNING}, + [LOG_CLASS_INFO] = {"info", "info", 97, LOG_INFO}, + [LOG_CLASS_DEBUG] = {"debug", " dbg", 36, LOG_DEBUG}, +}; void log_init(enum log_colorize _colorize, bool _do_syslog, - enum log_facility syslog_facility, enum log_class syslog_level) + enum log_facility syslog_facility, enum log_class _log_level) { static const int facility_map[] = { [LOG_FACILITY_USER] = LOG_USER, [LOG_FACILITY_DAEMON] = LOG_DAEMON, }; - static const int level_map[] = { - [LOG_CLASS_ERROR] = LOG_ERR, - [LOG_CLASS_WARNING] = LOG_WARNING, - [LOG_CLASS_INFO] = LOG_INFO, - [LOG_CLASS_DEBUG] = LOG_DEBUG, - }; - colorize = _colorize == LOG_COLORIZE_NEVER ? false : _colorize == LOG_COLORIZE_ALWAYS ? true : isatty(STDERR_FILENO); do_syslog = _do_syslog; + log_level = _log_level; - if (do_syslog) { + int slvl = log_level_map[_log_level].syslog_equivalent; + if (do_syslog && slvl != -1) { openlog(NULL, /*LOG_PID*/0, facility_map[syslog_facility]); - setlogmask(LOG_UPTO(level_map[syslog_level])); + setlogmask(LOG_UPTO(slvl)); } } @@ -50,117 +64,156 @@ static void _log(enum log_class log_class, const char *module, const char *file, int lineno, const char *fmt, int sys_errno, va_list va) { - const char *class = "abcd"; - int class_clr = 0; - switch (log_class) { - case LOG_CLASS_ERROR: class = " err"; class_clr = 31; break; - case LOG_CLASS_WARNING: class = "warn"; class_clr = 33; break; - case LOG_CLASS_INFO: class = "info"; class_clr = 97; break; - case LOG_CLASS_DEBUG: class = " dbg"; class_clr = 36; break; - } + assert(log_class > LOG_CLASS_NONE); + assert(log_class < ALEN(log_level_map)); + + if (log_class > log_level) + return; + + const char *prefix = log_level_map[log_class].log_prefix; + unsigned int class_clr = log_level_map[log_class].color; char clr[16]; - snprintf(clr, sizeof(clr), "\e[%dm", class_clr); - fprintf(stderr, "%s%s%s: ", colorize ? clr : "", class, colorize ? "\e[0m" : ""); + snprintf(clr, sizeof(clr), "\033[%um", class_clr); + fprintf(stderr, "%s%s%s: ", colorize ? clr : "", prefix, colorize ? "\033[0m" : ""); if (colorize) - fprintf(stderr, "\e[2m"); + fputs("\033[2m", stderr); fprintf(stderr, "%s:%d: ", file, lineno); if (colorize) - fprintf(stderr, "\e[0m"); + fputs("\033[0m", stderr); vfprintf(stderr, fmt, va); if (sys_errno != 0) fprintf(stderr, ": %s", strerror(sys_errno)); - fprintf(stderr, "\n"); + fputc('\n', stderr); } static void _sys_log(enum log_class log_class, const char *module, - const char *file __attribute__((unused)), - int lineno __attribute__((unused)), + const char UNUSED *file, int UNUSED lineno, const char *fmt, int sys_errno, va_list va) { + assert(log_class > LOG_CLASS_NONE); + assert(log_class < ALEN(log_level_map)); + if (!do_syslog) return; /* Map our log level to syslog's level */ - int level = -1; - switch (log_class) { - case LOG_CLASS_ERROR: level = LOG_ERR; break; - case LOG_CLASS_WARNING: level = LOG_WARNING; break; - case LOG_CLASS_INFO: level = LOG_INFO; break; - case LOG_CLASS_DEBUG: level = LOG_DEBUG; break; - } + int level = log_level_map[log_class].syslog_equivalent; - assert(level != -1); + char msg[4096]; + int n = vsnprintf(msg, sizeof(msg), fmt, va); + assert(n >= 0); - const char *sys_err = sys_errno != 0 ? strerror(sys_errno) : NULL; + if (sys_errno != 0 && (size_t)n < sizeof(msg)) + snprintf(msg + n, sizeof(msg) - n, ": %s", strerror(sys_errno)); + syslog(level, "%s: %s", module, msg); +} + +void +log_msg_va(enum log_class log_class, const char *module, + const char *file, int lineno, const char *fmt, va_list va) +{ va_list va2; va_copy(va2, va); - - /* Calculate required size of buffer holding the entire log message */ - int required_len = 0; - required_len += strlen(module) + 2; /* "%s: " */ - required_len += vsnprintf(NULL, 0, fmt, va2); va_end(va2); - - if (sys_errno != 0) - required_len += strlen(sys_err) + 2; /* ": %s" */ - - /* Format the msg */ - char *msg = malloc(required_len + 1); - int idx = 0; - - idx += snprintf(&msg[idx], required_len + 1 - idx, "%s: ", module); - idx += vsnprintf(&msg[idx], required_len + 1 - idx, fmt, va); - - if (sys_errno != 0) { - snprintf( - &msg[idx], required_len + 1 - idx, ": %s", strerror(sys_errno)); - } - - syslog(level, "%s", msg); - free(msg); + _log(log_class, module, file, lineno, fmt, 0, va); + _sys_log(log_class, module, file, lineno, fmt, 0, va2); + va_end(va2); } void log_msg(enum log_class log_class, const char *module, const char *file, int lineno, const char *fmt, ...) { - va_list ap1, ap2; - va_start(ap1, fmt); - va_copy(ap2, ap1); - _log(log_class, module, file, lineno, fmt, 0, ap1); - _sys_log(log_class, module, file, lineno, fmt, 0, ap2); - va_end(ap1); - va_end(ap2); + va_list va; + va_start(va, fmt); + log_msg_va(log_class, module, file, lineno, fmt, va); + va_end(va); } -void log_errno(enum log_class log_class, const char *module, - const char *file, int lineno, - const char *fmt, ...) +void +log_errno_va(enum log_class log_class, const char *module, + const char *file, int lineno, + const char *fmt, va_list va) { - va_list ap1, ap2; - va_start(ap1, fmt); - va_copy(ap2, ap1); - _log(log_class, module, file, lineno, fmt, errno, ap1); - _sys_log(log_class, module, file, lineno, fmt, errno, ap2); - va_end(ap1); - va_end(ap2); + log_errno_provided_va(log_class, module, file, lineno, errno, fmt, va); } -void log_errno_provided(enum log_class log_class, const char *module, - const char *file, int lineno, int _errno, - const char *fmt, ...) +void +log_errno(enum log_class log_class, const char *module, + const char *file, int lineno, + const char *fmt, ...) { - va_list ap1, ap2; - va_start(ap1, fmt); - va_copy(ap2, ap1); - _log(log_class, module, file, lineno, fmt, _errno, ap1); - _sys_log(log_class, module, file, lineno, fmt, _errno, ap2); - va_end(ap1); - va_end(ap2); + va_list va; + va_start(va, fmt); + log_errno_va(log_class, module, file, lineno, fmt, va); + va_end(va); +} + +void +log_errno_provided_va(enum log_class log_class, const char *module, + const char *file, int lineno, int errno_copy, + const char *fmt, va_list va) +{ + va_list va2; + va_copy(va2, va); + _log(log_class, module, file, lineno, fmt, errno_copy, va); + _sys_log(log_class, module, file, lineno, fmt, errno_copy, va2); + va_end(va2); +} + +void +log_errno_provided(enum log_class log_class, const char *module, + const char *file, int lineno, int errno_copy, + const char *fmt, ...) +{ + va_list va; + va_start(va, fmt); + log_errno_provided_va(log_class, module, file, lineno, errno_copy, fmt, va); + va_end(va); +} + +static size_t +map_len(void) +{ + size_t len = ALEN(log_level_map); +#ifndef _DEBUG + /* Exclude "debug" entry for non-debug builds */ + len--; +#endif + return len; +} + +int +log_level_from_string(const char *str) +{ + if (str[0] == '\0') + return -1; + + for (int i = 0, n = map_len(); i < n; i++) + if (strcmp(str, log_level_map[i].name) == 0) + return i; + + return -1; +} + +const char * +log_level_string_hint(void) +{ + static char buf[64]; + if (buf[0] != '\0') + return buf; + + for (size_t i = 0, pos = 0, n = map_len(); i < n; i++) { + const char *entry = log_level_map[i].name; + const char *delim = (i + 1 < n) ? ", " : ""; + pos += snprintf(buf + pos, sizeof(buf) - pos, "'%s'%s", entry, delim); + } + + return buf; } diff --git a/log.h b/log.h index dfddd76..94fd178 100644 --- a/log.h +++ b/log.h @@ -1,42 +1,68 @@ #pragma once #include +#include enum log_colorize { LOG_COLORIZE_NEVER, LOG_COLORIZE_ALWAYS, LOG_COLORIZE_AUTO }; enum log_facility { LOG_FACILITY_USER, LOG_FACILITY_DAEMON }; -enum log_class { LOG_CLASS_ERROR, LOG_CLASS_WARNING, LOG_CLASS_INFO, LOG_CLASS_DEBUG }; + +enum log_class { + LOG_CLASS_NONE, + LOG_CLASS_ERROR, + LOG_CLASS_WARNING, + LOG_CLASS_INFO, + LOG_CLASS_DEBUG +}; void log_init(enum log_colorize colorize, bool do_syslog, - enum log_facility syslog_facility, enum log_class syslog_level); + enum log_facility syslog_facility, enum log_class log_level); void log_deinit(void); -void log_msg(enum log_class log_class, const char *module, - const char *file, int lineno, - const char *fmt, ...) __attribute__((format (printf, 5, 6))); +void log_msg( + enum log_class log_class, const char *module, + const char *file, int lineno, + const char *fmt, ...) __attribute__((format (printf, 5, 6))); -void log_errno(enum log_class log_class, const char *module, - const char *file, int lineno, - const char *fmt, ...) __attribute__((format (printf, 5, 6))); +void log_errno( + enum log_class log_class, const char *module, + const char *file, int lineno, + const char *fmt, ...) __attribute__((format (printf, 5, 6))); void log_errno_provided( enum log_class log_class, const char *module, const char *file, int lineno, int _errno, const char *fmt, ...) __attribute__((format (printf, 6, 7))); -#define LOG_ERR(fmt, ...) \ - log_msg(LOG_CLASS_ERROR, LOG_MODULE, __FILE__, __LINE__, fmt, ## __VA_ARGS__) -#define LOG_ERRNO(fmt, ...) \ - log_errno(LOG_CLASS_ERROR, LOG_MODULE, __FILE__, __LINE__, fmt, ## __VA_ARGS__) -#define LOG_ERRNO_P(fmt, _errno, ...) \ +void log_msg_va( + enum log_class log_class, const char *module, + const char *file, int lineno, const char *fmt, va_list va) __attribute__((format (printf, 5, 0))); +void log_errno_va( + enum log_class log_class, const char *module, + const char *file, int lineno, + const char *fmt, va_list va) __attribute__((format (printf, 5, 0))); +void log_errno_provided_va( + enum log_class log_class, const char *module, + const char *file, int lineno, int _errno, + const char *fmt, va_list va) __attribute__((format (printf, 6, 0))); + + +int log_level_from_string(const char *str); +const char *log_level_string_hint(void); + +#define LOG_ERR(...) \ + log_msg(LOG_CLASS_ERROR, LOG_MODULE, __FILE__, __LINE__, __VA_ARGS__) +#define LOG_ERRNO(...) \ + log_errno(LOG_CLASS_ERROR, LOG_MODULE, __FILE__, __LINE__, __VA_ARGS__) +#define LOG_ERRNO_P(_errno, ...) \ log_errno_provided(LOG_CLASS_ERROR, LOG_MODULE, __FILE__, __LINE__, \ - _errno, fmt, ## __VA_ARGS__) -#define LOG_WARN(fmt, ...) \ - log_msg(LOG_CLASS_WARNING, LOG_MODULE, __FILE__, __LINE__, fmt, ## __VA_ARGS__) -#define LOG_INFO(fmt, ...) \ - log_msg(LOG_CLASS_INFO, LOG_MODULE, __FILE__, __LINE__, fmt, ## __VA_ARGS__) + _errno, __VA_ARGS__) +#define LOG_WARN(...) \ + log_msg(LOG_CLASS_WARNING, LOG_MODULE, __FILE__, __LINE__, __VA_ARGS__) +#define LOG_INFO(...) \ + log_msg(LOG_CLASS_INFO, LOG_MODULE, __FILE__, __LINE__, __VA_ARGS__) #if defined(LOG_ENABLE_DBG) && LOG_ENABLE_DBG - #define LOG_DBG(fmt, ...) \ - log_msg(LOG_CLASS_DEBUG, LOG_MODULE, __FILE__, __LINE__, fmt, ## __VA_ARGS__) + #define LOG_DBG(...) \ + log_msg(LOG_CLASS_DEBUG, LOG_MODULE, __FILE__, __LINE__, __VA_ARGS__) #else - #define LOG_DBG(fmt, ...) + #define LOG_DBG(...) #endif diff --git a/main.c b/main.c index cab5f9e..48a94f6 100644 --- a/main.c +++ b/main.c @@ -275,7 +275,7 @@ main(int argc, char *const *argv) log_init(log_colorize, log_syslog, LOG_FACILITY_DAEMON, LOG_CLASS_INFO); - _Static_assert(LOG_CLASS_ERROR + 1 == FCFT_LOG_CLASS_ERROR, + _Static_assert((int)LOG_CLASS_ERROR == (int)FCFT_LOG_CLASS_ERROR, "fcft log level enum offset"); _Static_assert((int)LOG_COLORIZE_ALWAYS == (int)FCFT_LOG_COLORIZE_ALWAYS, "fcft colorize enum mismatch"); @@ -367,7 +367,7 @@ done: int res; int r = thrd_join(bar_thread, &res); if (r != 0) - LOG_ERRNO_P("failed to join bar thread", r); + LOG_ERRNO_P(r, "failed to join bar thread"); bar->destroy(bar); close(abort_fd); diff --git a/modules/network.c b/modules/network.c index c6b32af..b9277d7 100644 --- a/modules/network.c +++ b/modules/network.c @@ -451,7 +451,7 @@ parse_reply(struct module *mod, const struct nlmsghdr *hdr, size_t len) case NLMSG_ERROR:{ const struct nlmsgerr *err = NLMSG_DATA(hdr); - LOG_ERRNO_P("netlink", err->error); + LOG_ERRNO_P(err->error, "netlink"); return false; } diff --git a/modules/script.c b/modules/script.c index 189f4b6..f323635 100644 --- a/modules/script.c +++ b/modules/script.c @@ -495,7 +495,7 @@ execute_script(struct module *mod) } if (r > 0) { - LOG_ERRNO_P("%s: failed to start", _errno, m->path); + LOG_ERRNO_P(_errno, "%s: failed to start", m->path); close(comm_pipe[0]); waitpid(pid, NULL, 0); return -1; diff --git a/particle.c b/particle.c index a1e2b2c..468bd28 100644 --- a/particle.c +++ b/particle.c @@ -203,7 +203,7 @@ exposable_default_on_mouse(struct exposable *exposable, struct bar *bar, if (WIFEXITED(wstatus)) { if (WEXITSTATUS(wstatus) != 0) - LOG_ERRNO_P("%s: failed to execute", WEXITSTATUS(wstatus), exposable->on_click[btn]); + LOG_ERRNO_P(WEXITSTATUS(wstatus), "%s: failed to execute", exposable->on_click[btn]); } else LOG_ERR("%s: did not exit normally", exposable->on_click[btn]); From be10465a3bc9fc4cdde67ff73ffe6bb4f8e4d575 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 15 Aug 2021 11:43:49 +0200 Subject: [PATCH 201/611] main: add -d,--log-level=info|warning|error|none Closes #84 --- CHANGELOG.md | 2 ++ completions/zsh/_yambar | 1 + doc/yambar.1.scd | 4 ++++ main.c | 37 +++++++++++++++++++++++++++---------- 4 files changed, 34 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e82a0f8..8316b08 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,8 @@ four borders to the same value (https://codeberg.org/dnkl/yambar/issues/77). * river: `per-output: false|true`. +* `-d,--log-level=info|warning|error|none` command line option + (https://codeberg.org/dnkl/yambar/issues/84). ### Changed diff --git a/completions/zsh/_yambar b/completions/zsh/_yambar index aa8172f..65b3100 100644 --- a/completions/zsh/_yambar +++ b/completions/zsh/_yambar @@ -8,5 +8,6 @@ _arguments \ '(-c --config)'{-c,--config}'[alternative configuration file]:filename:_files' \ '(-C --validate)'{-C,--validate}'[verify configuration then quit]' \ '(-p --print-pid)'{-p,--print-pid}'[print PID to this file or FD when up and running]:pidfile:_files' \ + '(-d --log-level)'{-d,--log-level}'[log level (info)]:loglevel:(info warning error none)' \ '(-l --log-colorize)'{-l,--log-colorize}'[enable or disable colorization of log output on stderr]:logcolor:(never always auto)' \ '(-s --log-no-syslog)'{-s,--log-no-syslog}'[disable syslog logging]' diff --git a/doc/yambar.1.scd b/doc/yambar.1.scd index f2526a2..a34f13c 100644 --- a/doc/yambar.1.scd +++ b/doc/yambar.1.scd @@ -27,6 +27,10 @@ yambar - modular status panel for X11 and Wayland (or FD) is closed immediately after writing the PID. When a _FILE_ as been specified, the file is unlinked exit. +*-d*,*--log-level*={*info*,*warning*,*error*,*none*} + Log level, used both for log output on stderr as well as + syslog. Default: _info_. + *-l*,*--log-colorize*=[{*never*,*always*,*auto*}] Enables or disables colorization of log output on stderr. diff --git a/main.c b/main.c index 48a94f6..e92e2e5 100644 --- a/main.c +++ b/main.c @@ -127,13 +127,14 @@ print_usage(const char *prog_name) printf("Usage: %s [OPTION]...\n", prog_name); printf("\n"); printf("Options:\n"); - printf(" -b,--backend={xcb,wayland,auto} backend to use (default: auto)\n" - " -c,--config=FILE alternative configuration file\n" - " -C,--validate verify configuration then quit\n" - " -p,--print-pid=FILE|FD print PID to file or FD\n" - " -l,--log-colorize=[never|always|auto] enable/disable colorization of log output on stderr\n" - " -s,--log-no-syslog disable syslog logging\n" - " -v,--version show the version number and quit\n"); + printf(" -b,--backend={xcb,wayland,auto} backend to use (default: auto)\n" + " -c,--config=FILE alternative configuration file\n" + " -C,--validate verify configuration then quit\n" + " -p,--print-pid=FILE|FD print PID to file or FD\n" + " -d,--log-level={info|warning|error|none} log level (info)\n" + " -l,--log-colorize=[never|always|auto] enable/disable colorization of log output on stderr\n" + " -s,--log-no-syslog disable syslog logging\n" + " -v,--version show the version number and quit\n"); } static bool @@ -181,6 +182,7 @@ main(int argc, char *const *argv) {"config", required_argument, 0, 'c'}, {"validate", no_argument, 0, 'C'}, {"print-pid", required_argument, 0, 'p'}, + {"log-level", required_argument, 0, 'd'}, {"log-colorize", optional_argument, 0, 'l'}, {"log-no-syslog", no_argument, 0, 's'}, {"version", no_argument, 0, 'v'}, @@ -195,11 +197,12 @@ main(int argc, char *const *argv) char *config_path = NULL; enum bar_backend backend = BAR_BACKEND_AUTO; + enum log_class log_level = LOG_CLASS_INFO; enum log_colorize log_colorize = LOG_COLORIZE_AUTO; bool log_syslog = true; while (true) { - int c = getopt_long(argc, argv, ":b:c:Cp:l::svh", longopts, NULL); + int c = getopt_long(argc, argv, ":b:c:Cp:d:l::svh", longopts, NULL); if (c == -1) break; @@ -238,6 +241,20 @@ main(int argc, char *const *argv) pid_file = optarg; break; + case 'd': { + int lvl = log_level_from_string(optarg); + if (lvl < 0) { + fprintf( + stderr, + "-d,--log-level: %s: argument must be one of %s\n", + optarg, + log_level_string_hint()); + return EXIT_FAILURE; + } + log_level = lvl; + break; + } + case 'l': if (optarg == NULL || strcmp(optarg, "auto") == 0) log_colorize = LOG_COLORIZE_AUTO; @@ -273,14 +290,14 @@ main(int argc, char *const *argv) } } - log_init(log_colorize, log_syslog, LOG_FACILITY_DAEMON, LOG_CLASS_INFO); + log_init(log_colorize, log_syslog, LOG_FACILITY_DAEMON, log_level); _Static_assert((int)LOG_CLASS_ERROR == (int)FCFT_LOG_CLASS_ERROR, "fcft log level enum offset"); _Static_assert((int)LOG_COLORIZE_ALWAYS == (int)FCFT_LOG_COLORIZE_ALWAYS, "fcft colorize enum mismatch"); fcft_log_init( - (enum fcft_log_colorize)log_colorize, log_syslog, FCFT_LOG_CLASS_INFO); + (enum fcft_log_colorize)log_colorize, log_syslog, (enum fcft_log_class)log_level); const struct sigaction sa = {.sa_handler = &signal_handler}; sigaction(SIGINT, &sa, NULL); From 495a4c8fb161801f256987a27fee7c3513d37a41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 15 Aug 2021 16:34:46 +0200 Subject: [PATCH 202/611] =?UTF-8?q?log:=20remove=20unused=20include=20?= =?UTF-8?q?=E2=80=9Cdebug.h=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- log.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/log.c b/log.c index 52595bc..c1adcd0 100644 --- a/log.c +++ b/log.c @@ -11,8 +11,6 @@ #include #include -#include "debug.h" - #define ALEN(v) (sizeof(v) / sizeof((v)[0])) #define UNUSED __attribute__((unused)) From 8b6b82f1e545ed76d605ef72a68fce7ffc6bce48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 19 Aug 2021 19:25:18 +0200 Subject: [PATCH 203/611] =?UTF-8?q?module/river:=20add=20support=20for=20r?= =?UTF-8?q?iver-status=20v2=20(=E2=80=98urgent=E2=80=99=20views)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 2 ++ doc/yambar-modules-river.5.scd | 5 ++- external/river-status-unstable-v1.xml | 17 +++++++--- modules/river.c | 49 +++++++++++++++++++++------ 4 files changed, 57 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8316b08..825b14c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,8 @@ * river: `per-output: false|true`. * `-d,--log-level=info|warning|error|none` command line option (https://codeberg.org/dnkl/yambar/issues/84). +* river: support for the river-status protocol, version 2 (‘urgent’ + views). ### Changed diff --git a/doc/yambar-modules-river.5.scd b/doc/yambar-modules-river.5.scd index 570e8a5..341176e 100644 --- a/doc/yambar-modules-river.5.scd +++ b/doc/yambar-modules-river.5.scd @@ -27,6 +27,9 @@ once for all 32 river tags. This means you probably want to use a | id : int : River tag number +| urgent +: bool +: True if the river tag has at least one urgent view. | visible : bool : True if the river tag is focused by at least one output (i.e. visible on at least one monitor). @@ -38,7 +41,7 @@ once for all 32 river tags. This means you probably want to use a : True if the river tag has views (i.e. windows). | state : string -: Set to *focused* if _focused_ is true, *unfocused* if _visible_ is true, but _focused_ is false, or *invisible* if the river tag is not visible on any monitors. +: Set to *urgent* if _urgent_ is true, *focused* if _focused_ is true, *unfocused* if _visible_ is true, but _focused_ is false, or *invisible* if the river tag is not visible on any monitors. | seat : string : The name of the seat (*title* particle only, see CONFIGURATION) diff --git a/external/river-status-unstable-v1.xml b/external/river-status-unstable-v1.xml index a4d6f4e..13affaa 100644 --- a/external/river-status-unstable-v1.xml +++ b/external/river-status-unstable-v1.xml @@ -1,7 +1,7 @@ - Copyright 2020 Isaac Freund + Copyright 2020 The River Developers Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above @@ -16,7 +16,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - + A global factory for objects that receive status information specific to river. It could be used to implement, for example, a status bar. @@ -47,7 +47,7 @@ - + This interface allows clients to receive information about the current windowing state of an output. @@ -75,12 +75,21 @@ + + + + Sent once on binding the interface and again whenever the set of + tags with at least one urgent view changes. + + + This interface allows clients to receive information about the current - focus of a seat. + focus of a seat. Note that (un)focused_output events will only be sent + if the client has bound the relevant wl_output globals. diff --git a/modules/river.c b/modules/river.c index 9b494f3..897dbcc 100644 --- a/modules/river.c +++ b/modules/river.c @@ -29,6 +29,7 @@ struct output { /* Tags */ uint32_t occupied; uint32_t focused; + uint32_t urgent; }; struct seat { @@ -81,9 +82,10 @@ content(struct module *mod) mtx_lock(&m->mod->lock); + uint32_t urgent = 0; + uint32_t occupied = 0; uint32_t output_focused = 0; uint32_t seat_focused = 0; - uint32_t occupied = 0; tll_foreach(m->outputs, it) { const struct output *output = &it->item; @@ -96,6 +98,7 @@ content(struct module *mod) } output_focused |= output->focused; + urgent |= output->urgent; occupied |= output->occupied; tll_foreach(m->seats, it2) { @@ -112,12 +115,18 @@ content(struct module *mod) for (unsigned i = 0; i < 32; i++) { /* It's visible if any output has it focused */ - bool visible = output_focused & (1u << i); + bool is_visible = output_focused & (1u << i); /* It's focused if any output that has seat focus has it focused */ - bool focused = seat_focused & (1u << i); + bool is_focused = seat_focused & (1u << i); - const char *state = visible ? focused ? "focused" : "unfocused" : "invisible"; + bool is_urgent = urgent & (1u << i); + bool is_occupied = occupied & (1u << i); + + const char *state = + is_urgent ? "urgent" : + is_visible ? is_focused ? "focused" : "unfocused" : + "invisible"; #if 0 LOG_DBG("tag: #%u, visible=%d, focused=%d, occupied=%d, state=%s", @@ -127,12 +136,13 @@ content(struct module *mod) struct tag_set tags = { .tags = (struct tag *[]){ tag_new_int(mod, "id", i + 1), - tag_new_bool(mod, "visible", visible), - tag_new_bool(mod, "focused", focused), - tag_new_bool(mod, "occupied", occupied & (1u << i)), + tag_new_bool(mod, "urgent", is_urgent), + tag_new_bool(mod, "visible", is_visible), + tag_new_bool(mod, "focused", is_focused), + tag_new_bool(mod, "occupied", is_occupied), tag_new_string(mod, "state", state), }, - .count = 5, + .count = 6, }; tag_parts[i] = m->template->instantiate(m->template, &tags); @@ -156,7 +166,7 @@ content(struct module *mod) tag_set_destroy(&tags); } } - + mtx_unlock(&m->mod->lock); return dynlist_exposable_new(tag_parts, 32 + seat_count, 0, 0); } @@ -230,16 +240,33 @@ view_tags(void *data, struct zriver_output_status_v1 *zriver_output_status_v1, wl_array_for_each(set, tags) { output->occupied |= *set; } - + LOG_DBG("output: %s: occupied tags: 0x%0x", output->name, output->occupied); } mtx_unlock(&mod->lock); mod->bar->refresh(mod->bar); } +static void +urgent_tags(void *data, struct zriver_output_status_v1 *zriver_output_status_v1, + uint32_t tags) +{ + struct output *output = data; + struct module *mod = output->m->mod; + + mtx_lock(&mod->lock); + { + output->urgent = tags; + } + mtx_unlock(&mod->lock); + mod->bar->refresh(mod->bar); +} + + static const struct zriver_output_status_v1_listener river_status_output_listener = { .focused_tags = &focused_tags, .view_tags = &view_tags, + .urgent_tags = &urgent_tags, }; static void @@ -519,7 +546,7 @@ handle_global(void *data, struct wl_registry *registry, } else if (strcmp(interface, zriver_status_manager_v1_interface.name) == 0) { - const uint32_t required = 1; + const uint32_t required = 2; if (!verify_iface_version(interface, version, required)) return; From be6e714eb09a130d3b434529009f95a54bc73310 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 18 Aug 2021 20:21:04 +0200 Subject: [PATCH 204/611] module/alsa: handle ALSA device disappearing With this patch, a non-existing ALSA device is no longer considered a fatal error. Instead, we keep retrying until we succeed. Furthermore, if we have successfully opened the ALSA device, and it then disappears, we a) no longer crash, or cause 100% CPU usage, and b) try to re-connect to the device. With this, we now handle e.g. USB soundcards being disconnected and then re-connected. We should also handle pseudo devices, like pipewire provides ones, when yambar is started before pipewire. Closes #59 Closes #61 Closes #86 --- CHANGELOG.md | 6 ++ doc/yambar-modules-alsa.5.scd | 3 + modules/alsa.c | 129 ++++++++++++++++++++++++++++++---- 3 files changed, 126 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 825b14c..81c3e39 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,11 +22,15 @@ (https://codeberg.org/dnkl/yambar/issues/84). * river: support for the river-status protocol, version 2 (‘urgent’ views). +* `online` tag to the `alsa` module. ### Changed * bar: do not add `spacing` around empty (zero-width) modules. +* alsa: do not error out if we fail to connect to the ALSA device, or + if we get disconnected. Instead, keep retrying until we succeed + (https://codeberg.org/dnkl/yambar/issues/86). ### Deprecated @@ -37,6 +41,8 @@ compiled without the Wayland backend”_. * Regression: `{where}` tag not being expanded in progress-bar `on-click` handlers. +* `alsa` module causing yambar to use 100% CPU if the ALSA device is + disconnected (https://codeberg.org/dnkl/yambar/issues/61). ### Security diff --git a/doc/yambar-modules-alsa.5.scd b/doc/yambar-modules-alsa.5.scd index 23f3291..e8b5e1a 100644 --- a/doc/yambar-modules-alsa.5.scd +++ b/doc/yambar-modules-alsa.5.scd @@ -8,6 +8,9 @@ alsa - Monitors an alsa soundcard for volume and mute/unmute changes [[ *Name* :[ *Type* :[ *Description* +| online +: bool +: True when the ALSA device has successfully been opened | volume : range : Volume level, with min and max as start and end range values diff --git a/modules/alsa.c b/modules/alsa.c index 3b71a25..a5b05fd 100644 --- a/modules/alsa.c +++ b/modules/alsa.c @@ -1,6 +1,7 @@ #include #include #include +#include #include @@ -21,6 +22,7 @@ struct private { tll(snd_mixer_selem_channel_id_t) channels; + bool online; long vol_min; long vol_max; long vol_cur; @@ -60,11 +62,12 @@ content(struct module *mod) mtx_lock(&mod->lock); struct tag_set tags = { .tags = (struct tag *[]){ + tag_new_bool(mod, "online", m->online), tag_new_int_range(mod, "volume", m->vol_cur, m->vol_min, m->vol_max), tag_new_int_range(mod, "percent", percent, 0, 100), tag_new_bool(mod, "muted", m->muted), }, - .count = 3, + .count = 4, }; mtx_unlock(&mod->lock); @@ -186,22 +189,30 @@ update_state(struct module *mod, snd_mixer_elem_t *elem) m->vol_min = min; m->vol_max = max; m->vol_cur = cur[0]; + m->online = true; m->muted = !unmuted[0]; mtx_unlock(&mod->lock); mod->bar->refresh(mod->bar); } -static int -run(struct module *mod) +enum run_state { + RUN_ERROR, + RUN_FAILED_CONNECT, + RUN_DISCONNECTED, + RUN_DONE, +}; + +static enum run_state +run_while_online(struct module *mod) { struct private *m = mod->private; - int ret = 1; + enum run_state ret = RUN_ERROR; snd_mixer_t *handle; if (snd_mixer_open(&handle, 0) != 0) { LOG_ERR("failed to open handle"); - return 1; + return ret; } if (snd_mixer_attach(handle, m->card) != 0 || @@ -209,6 +220,7 @@ run(struct module *mod) snd_mixer_load(handle) != 0) { LOG_ERR("failed to attach to card"); + ret = RUN_FAILED_CONNECT; goto err; } @@ -260,29 +272,122 @@ run(struct module *mod) fds[0] = (struct pollfd){.fd = mod->abort_fd, .events = POLLIN}; snd_mixer_poll_descriptors(handle, &fds[1], fd_count); - poll(fds, fd_count + 1, -1); + int r = poll(fds, fd_count + 1, -1); + if (r < 0) { + if (errno == EINTR) + continue; - if (fds[0].revents & POLLIN) + LOG_ERRNO("failed to poll"); break; + } - if (fds[1].revents & POLLHUP) { - /* Don't know if this can happen */ - LOG_ERR("disconnected from alsa"); + if (fds[0].revents & POLLIN) { + ret = RUN_DONE; break; } + for (size_t i = 0; i < fd_count; i++) { + if (fds[1 + i].revents & (POLLHUP | POLLERR | POLLNVAL)) { + LOG_ERR("disconnected from alsa"); + + mtx_lock(&mod->lock); + m->online = false; + mtx_unlock(&mod->lock); + mod->bar->refresh(mod->bar); + + ret = RUN_DISCONNECTED; + goto err; + } + } + snd_mixer_handle_events(handle); update_state(mod, elem); } - ret = 0; - err: snd_mixer_close(handle); snd_config_update_free_global(); return ret; } +static int +run(struct module *mod) +{ + static const int min_timeout_ms = 500; + static const int max_timeout_ms = 30000; + int timeout_ms = min_timeout_ms; + + while (true) { + enum run_state state = run_while_online(mod); + + switch (state) { + case RUN_DONE: + return 0; + + case RUN_ERROR: + return 1; + + case RUN_FAILED_CONNECT: + timeout_ms *= 2; + break; + + case RUN_DISCONNECTED: + timeout_ms = min_timeout_ms; + break; + } + + if (timeout_ms > max_timeout_ms) + timeout_ms = max_timeout_ms; + + struct timeval now; + gettimeofday(&now, NULL); + + struct timeval timeout = { + .tv_sec = timeout_ms / 1000, + .tv_usec = (timeout_ms % 1000) * 1000, + }; + + struct timeval deadline; + timeradd(&now, &timeout, &deadline); + + LOG_DBG("timeout is now %dms", timeout_ms); + + while (true) { + + struct timeval n; + gettimeofday(&n, NULL); + + struct timeval left; + timersub(&deadline, &n, &left); + + int poll_timeout = timercmp(&left, &(struct timeval){0}, <) + ? 0 + : left.tv_sec * 1000 + left.tv_usec / 1000; + + LOG_DBG( + "polling for alsa device to become available (timeout=%dms)", + poll_timeout); + + struct pollfd fds[] = {{.fd = mod->abort_fd, .events = POLLIN}}; + int r = poll(fds, 1, poll_timeout); + + if (r < 0) { + if (errno == EINTR) + continue; + + LOG_ERRNO("failed to poll"); + return 1; + } + + if (fds[0].revents & POLLIN) + return 0; + + assert(r == 0); + break; + } + } +} + static struct module * alsa_new(const char *card, const char *mixer, struct particle *label) { From ae7d54fb80dd579bc81f38e1dcb37fdd4eeb7f2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 20 Aug 2021 20:24:44 +0200 Subject: [PATCH 205/611] =?UTF-8?q?module/alsa:=20add=20=E2=80=98volume?= =?UTF-8?q?=E2=80=99=20and=20=E2=80=98muted=E2=80=99=20options?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These options allows you to select which channel to use as volume source, and which channel to use as the source for the muted state. With this, we can also remove the check for *all* (playback) channels having the same volume/muted state. And with that, we no longer need to warn when not all channels have the same volume/muted state. --- CHANGELOG.md | 2 + doc/yambar-modules-alsa.5.scd | 11 +++ modules/alsa.c | 131 ++++++++++++++++++++-------------- 3 files changed, 89 insertions(+), 55 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 81c3e39..160575c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,8 @@ * river: support for the river-status protocol, version 2 (‘urgent’ views). * `online` tag to the `alsa` module. +* alsa: `volume` and `muted` options, allowing you to configure which + channels to use as source for the volume level and muted state. ### Changed diff --git a/doc/yambar-modules-alsa.5.scd b/doc/yambar-modules-alsa.5.scd index e8b5e1a..ca4ea9a 100644 --- a/doc/yambar-modules-alsa.5.scd +++ b/doc/yambar-modules-alsa.5.scd @@ -36,6 +36,17 @@ alsa - Monitors an alsa soundcard for volume and mute/unmute changes : string : yes : Mixer channel to monitor. _Master_ might work. +| volume +: string +: no +: The name of the channel to use as source for the volume level + (default: *Front Left*). +| muted +: string +: no +: The name of the channel to use as source for the muted state + (default: *Front Left*). + # EXAMPLES diff --git a/modules/alsa.c b/modules/alsa.c index a5b05fd..6952a3b 100644 --- a/modules/alsa.c +++ b/modules/alsa.c @@ -18,6 +18,8 @@ struct private { char *card; char *mixer; + char *volume_channel; + char *muted_channel; struct particle *label; tll(snd_mixer_selem_channel_id_t) channels; @@ -37,6 +39,8 @@ destroy(struct module *mod) m->label->destroy(m->label); free(m->card); free(m->mixer); + free(m->volume_channel); + free(m->muted_channel); free(m); module_default_destroy(mod); } @@ -82,8 +86,6 @@ update_state(struct module *mod, snd_mixer_elem_t *elem) { struct private *m = mod->private; - int idx = 0; - /* Get min/max volume levels */ long min = 0, max = 0; int r = snd_mixer_selem_get_playback_volume_range(elem, &min, &max); @@ -101,96 +103,72 @@ update_state(struct module *mod, snd_mixer_elem_t *elem) min = max; } - long cur[tll_length(m->channels)]; - memset(cur, 0, sizeof(cur)); + long cur = 0; /* If volume level can be changed (i.e. this isn't just a switch; * e.g. a digital channel), get current level */ if (max > 0) { tll_foreach(m->channels, it) { - int r = snd_mixer_selem_get_playback_volume( - elem, it->item, &cur[idx]); + const char *name = snd_mixer_selem_channel_name(it->item); + if (strcmp(name, m->volume_channel) != 0) + continue; + + int r = snd_mixer_selem_get_playback_volume(elem, it->item, &cur); if (r < 0) { LOG_WARN("%s,%s: %s: failed to get current volume", - m->card, m->mixer, - snd_mixer_selem_channel_name(it->item)); + m->card, m->mixer, name); } - LOG_DBG("%s,%s: %s: volume: %ld", m->card, m->mixer, - snd_mixer_selem_channel_name(it->item), cur[idx]); - idx++; + LOG_DBG("%s,%s: %s: volume: %ld", m->card, m->mixer, name, cur); } } - int unmuted[tll_length(m->channels)]; - memset(unmuted, 0, sizeof(unmuted)); + int unmuted = 0; /* Get muted state */ - idx = 0; tll_foreach(m->channels, it) { - int r = snd_mixer_selem_get_playback_switch( - elem, it->item, &unmuted[idx]); + const char *name = snd_mixer_selem_channel_name(it->item); + if (strcmp(name, m->muted_channel) != 0) + continue; + + int r = snd_mixer_selem_get_playback_switch(elem, it->item, &unmuted); if (r < 0) { LOG_WARN("%s,%s: %s: failed to get muted state", - m->card, m->mixer, snd_mixer_selem_channel_name(it->item)); - unmuted[idx] = 1; + m->card, m->mixer, name); + unmuted = 1; } - LOG_DBG("%s,%s: %s: muted: %d", m->card, m->mixer, - snd_mixer_selem_channel_name(it->item), !unmuted[idx]); - - idx++; - } - - /* Warn if volume level is inconsistent across the channels */ - for (size_t i = 1; i < tll_length(m->channels); i++) { - if (cur[i] != cur[i - 1]) { - LOG_WARN("%s,%s: channel volume mismatch, using value from %s", - m->card, m->mixer, - snd_mixer_selem_channel_name(tll_front(m->channels))); - break; - } - } - - /* Warn if muted state is inconsistent across the channels */ - for (size_t i = 1; i < tll_length(m->channels); i++) { - if (unmuted[i] != unmuted[i - 1]) { - LOG_WARN("%s,%s: channel muted mismatch, using value from %s", - m->card, m->mixer, - snd_mixer_selem_channel_name(tll_front(m->channels))); - break; - } + LOG_DBG("%s,%s: %s: muted: %d", m->card, m->mixer, name, !unmuted); } /* Make sure min <= cur <= max */ - if (cur[0] < min) { + if (cur < min) { LOG_WARN( "%s,%s: current volume is less than the indicated minimum: " - "%ld < %ld", m->card, m->mixer, cur[0], min); - cur[0] = min; + "%ld < %ld", m->card, m->mixer, cur, min); + cur = min; } - if (cur[0] > max) { + if (cur > max) { LOG_WARN( "%s,%s: current volume is greater than the indicated maximum: " - "%ld > %ld", m->card, m->mixer, cur[0], max); - cur[0] = max; + "%ld > %ld", m->card, m->mixer, cur, max); + cur = max; } - assert(cur[0] >= min); - assert(cur[0] <= max); + assert(cur >= min); + assert(cur <= max); - LOG_DBG( - "muted=%d, cur=%ld, min=%ld, max=%ld", !unmuted[0], cur[0], min, max); + LOG_DBG("muted=%d, cur=%ld, min=%ld, max=%ld", !unmuted, cur, min, max); mtx_lock(&mod->lock); m->vol_min = min; m->vol_max = max; - m->vol_cur = cur[0]; + m->vol_cur = cur; m->online = true; - m->muted = !unmuted[0]; + m->muted = !unmuted; mtx_unlock(&mod->lock); mod->bar->refresh(mod->bar); @@ -254,6 +232,39 @@ run_while_online(struct module *mod) LOG_INFO("%s,%s: channels: %s", m->card, m->mixer, channels_str); + /* Verify volume/muted channel names are valid and exists */ + bool have_volume_channel = false; + bool have_muted_channel = false; + tll_foreach(m->channels, it) { + const char *chan_name = snd_mixer_selem_channel_name(it->item); + if (strcmp(chan_name, m->volume_channel) == 0) + have_volume_channel = true; + if (strcmp(chan_name, m->muted_channel) == 0) + have_muted_channel = true; + } + + if (!have_volume_channel) { + const char *first_chan_name = + snd_mixer_selem_channel_name(tll_front(m->channels)); + + LOG_WARN("%s: not a valid channel name, using '%s' as volume source", + m->volume_channel, first_chan_name); + + free(m->volume_channel); + m->volume_channel = strdup(first_chan_name); + } + + if (!have_muted_channel) { + const char *first_chan_name = + snd_mixer_selem_channel_name(tll_front(m->channels)); + + LOG_WARN("%s: not a valid channel name, using '%s' as muted source", + m->muted_channel, first_chan_name); + + free(m->muted_channel); + m->muted_channel = strdup(first_chan_name); + } + /* Initial state */ update_state(mod, elem); @@ -389,12 +400,16 @@ run(struct module *mod) } static struct module * -alsa_new(const char *card, const char *mixer, struct particle *label) +alsa_new(const char *card, const char *mixer, + const char *volume_channel, const char *muted_channel, + struct particle *label) { struct private *priv = calloc(1, sizeof(*priv)); priv->label = label; priv->card = strdup(card); priv->mixer = strdup(mixer); + priv->volume_channel = strdup(volume_channel); + priv->muted_channel = strdup(muted_channel); struct module *mod = module_common_new(); mod->private = priv; @@ -410,11 +425,15 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) { const struct yml_node *card = yml_get_value(node, "card"); const struct yml_node *mixer = yml_get_value(node, "mixer"); + const struct yml_node *volume = yml_get_value(node, "volume"); + const struct yml_node *muted = yml_get_value(node, "muted"); const struct yml_node *content = yml_get_value(node, "content"); return alsa_new( yml_value_as_string(card), yml_value_as_string(mixer), + volume != NULL ? yml_value_as_string(volume) : "Front Left", + muted != NULL ? yml_value_as_string(muted) : "Front Left", conf_to_particle(content, inherited)); } @@ -424,6 +443,8 @@ verify_conf(keychain_t *chain, const struct yml_node *node) static const struct attr_info attrs[] = { {"card", true, &conf_verify_string}, {"mixer", true, &conf_verify_string}, + {"volume", false, &conf_verify_string}, + {"muted", false, &conf_verify_string}, MODULE_COMMON_ATTRS, }; From db12ceb026282ab78b1da9b97313f90fc6bf7eff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 20 Aug 2021 21:41:21 +0200 Subject: [PATCH 206/611] =?UTF-8?q?module/alsa:=20volume/muted:=20default?= =?UTF-8?q?=20to=20=E2=80=9Cunset=E2=80=9D;=20use=20first=20available=20ch?= =?UTF-8?q?annel?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/yambar-modules-alsa.5.scd | 4 +-- modules/alsa.c | 51 +++++++++++++++-------------------- 2 files changed, 23 insertions(+), 32 deletions(-) diff --git a/doc/yambar-modules-alsa.5.scd b/doc/yambar-modules-alsa.5.scd index ca4ea9a..58a4343 100644 --- a/doc/yambar-modules-alsa.5.scd +++ b/doc/yambar-modules-alsa.5.scd @@ -40,12 +40,12 @@ alsa - Monitors an alsa soundcard for volume and mute/unmute changes : string : no : The name of the channel to use as source for the volume level - (default: *Front Left*). + (default: first available channel, usually "Front Left"). | muted : string : no : The name of the channel to use as source for the muted state - (default: *Front Left*). + (default: first available channel, usually "Front Left"). # EXAMPLES diff --git a/modules/alsa.c b/modules/alsa.c index 6952a3b..54b9124 100644 --- a/modules/alsa.c +++ b/modules/alsa.c @@ -110,7 +110,7 @@ update_state(struct module *mod, snd_mixer_elem_t *elem) if (max > 0) { tll_foreach(m->channels, it) { const char *name = snd_mixer_selem_channel_name(it->item); - if (strcmp(name, m->volume_channel) != 0) + if (m->volume_channel != NULL && strcmp(name, m->volume_channel) != 0) continue; int r = snd_mixer_selem_get_playback_volume(elem, it->item, &cur); @@ -129,7 +129,7 @@ update_state(struct module *mod, snd_mixer_elem_t *elem) /* Get muted state */ tll_foreach(m->channels, it) { const char *name = snd_mixer_selem_channel_name(it->item); - if (strcmp(name, m->muted_channel) != 0) + if (m->muted_channel != NULL && strcmp(name, m->muted_channel) != 0) continue; int r = snd_mixer_selem_get_playback_switch(elem, it->item, &unmuted); @@ -233,36 +233,27 @@ run_while_online(struct module *mod) LOG_INFO("%s,%s: channels: %s", m->card, m->mixer, channels_str); /* Verify volume/muted channel names are valid and exists */ - bool have_volume_channel = false; - bool have_muted_channel = false; + bool volume_channel_is_valid = m->volume_channel == NULL; + bool muted_channel_is_valid = m->muted_channel == NULL; + tll_foreach(m->channels, it) { const char *chan_name = snd_mixer_selem_channel_name(it->item); - if (strcmp(chan_name, m->volume_channel) == 0) - have_volume_channel = true; - if (strcmp(chan_name, m->muted_channel) == 0) - have_muted_channel = true; + if (m->volume_channel != NULL && strcmp(chan_name, m->volume_channel) == 0) + volume_channel_is_valid = true; + if (m->muted_channel != NULL && strcmp(chan_name, m->muted_channel) == 0) + muted_channel_is_valid = true; } - if (!have_volume_channel) { - const char *first_chan_name = - snd_mixer_selem_channel_name(tll_front(m->channels)); - - LOG_WARN("%s: not a valid channel name, using '%s' as volume source", - m->volume_channel, first_chan_name); - - free(m->volume_channel); - m->volume_channel = strdup(first_chan_name); + if (!volume_channel_is_valid) { + assert(m->volume_channel != NULL); + LOG_ERR("volume: invalid channel name: %s", m->volume_channel); + goto err; } - if (!have_muted_channel) { - const char *first_chan_name = - snd_mixer_selem_channel_name(tll_front(m->channels)); - - LOG_WARN("%s: not a valid channel name, using '%s' as muted source", - m->muted_channel, first_chan_name); - - free(m->muted_channel); - m->muted_channel = strdup(first_chan_name); + if (!muted_channel_is_valid) { + assert(m->muted_channel != NULL); + LOG_ERR("muted: invalid channel name: %s", m->muted_channel); + goto err; } /* Initial state */ @@ -408,8 +399,8 @@ alsa_new(const char *card, const char *mixer, priv->label = label; priv->card = strdup(card); priv->mixer = strdup(mixer); - priv->volume_channel = strdup(volume_channel); - priv->muted_channel = strdup(muted_channel); + priv->volume_channel = volume_channel != NULL ? strdup(volume_channel) : NULL; + priv->muted_channel = muted_channel != NULL ? strdup(muted_channel) : NULL; struct module *mod = module_common_new(); mod->private = priv; @@ -432,8 +423,8 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) return alsa_new( yml_value_as_string(card), yml_value_as_string(mixer), - volume != NULL ? yml_value_as_string(volume) : "Front Left", - muted != NULL ? yml_value_as_string(muted) : "Front Left", + volume != NULL ? yml_value_as_string(volume) : NULL, + muted != NULL ? yml_value_as_string(muted) : NULL, conf_to_particle(content, inherited)); } From 25c20e5534f6671a27b0218d6c0882e1fbb46e06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 21 Aug 2021 10:52:12 +0200 Subject: [PATCH 207/611] module/alsa: use inotify on /dev/snd instead of a poll timeout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While waiting for the configured ALSA card to become available, use inotify and watch for CREATE events on /dev/snd instead of polling (using a timeout in the poll(3) call). Note that we don’t know the actual names of the files that (will) be created. This means: * Every time we see a CREATE event on /dev/snd, we *try* to connect to ALSA. If we fail, we go back to watching /dev/snd again. * ALSA (not yambar) will log an error message each time we fail. --- modules/alsa.c | 118 +++++++++++++++++++++++++++++-------------------- 1 file changed, 70 insertions(+), 48 deletions(-) diff --git a/modules/alsa.c b/modules/alsa.c index 54b9124..aa985b0 100644 --- a/modules/alsa.c +++ b/modules/alsa.c @@ -2,6 +2,7 @@ #include #include #include +#include #include @@ -315,79 +316,100 @@ err: static int run(struct module *mod) { - static const int min_timeout_ms = 500; - static const int max_timeout_ms = 30000; - int timeout_ms = min_timeout_ms; + int ret = 1; + + int wd = -1; + int ifd = inotify_init(); + if (ifd < 0) { + LOG_ERRNO("failed to inotify"); + return 1; + } while (true) { enum run_state state = run_while_online(mod); switch (state) { case RUN_DONE: - return 0; - - case RUN_ERROR: - return 1; - - case RUN_FAILED_CONNECT: - timeout_ms *= 2; + ret = 0; break; + case RUN_ERROR: + ret = 1; + break; + + case RUN_FAILED_CONNECT: case RUN_DISCONNECTED: - timeout_ms = min_timeout_ms; break; } - if (timeout_ms > max_timeout_ms) - timeout_ms = max_timeout_ms; - - struct timeval now; - gettimeofday(&now, NULL); - - struct timeval timeout = { - .tv_sec = timeout_ms / 1000, - .tv_usec = (timeout_ms % 1000) * 1000, - }; - - struct timeval deadline; - timeradd(&now, &timeout, &deadline); - - LOG_DBG("timeout is now %dms", timeout_ms); + wd = inotify_add_watch(ifd, "/dev/snd", IN_CREATE); + if (wd < 0) { + LOG_ERRNO("failed to create inotify watcher for /dev/snd"); + ret = 1; + break; + } while (true) { - - struct timeval n; - gettimeofday(&n, NULL); - - struct timeval left; - timersub(&deadline, &n, &left); - - int poll_timeout = timercmp(&left, &(struct timeval){0}, <) - ? 0 - : left.tv_sec * 1000 + left.tv_usec / 1000; - - LOG_DBG( - "polling for alsa device to become available (timeout=%dms)", - poll_timeout); - - struct pollfd fds[] = {{.fd = mod->abort_fd, .events = POLLIN}}; - int r = poll(fds, 1, poll_timeout); + struct pollfd fds[] = {{.fd = mod->abort_fd, .events = POLLIN}, + {.fd = ifd, .events = POLLIN}}; + int r = poll(fds, sizeof(fds) / sizeof(fds[0]), -1); if (r < 0) { if (errno == EINTR) continue; LOG_ERRNO("failed to poll"); - return 1; + ret = 1; + goto out; } - if (fds[0].revents & POLLIN) - return 0; + if (fds[0].revents & POLLIN) { + ret = 0; + goto out; + } - assert(r == 0); - break; + if (fds[1].revents & POLLIN) { + char buf[1024]; + ssize_t len = read(ifd, buf, sizeof(buf)); + + if (len < 0) { + LOG_ERRNO("failed to read inotify events"); + ret = 1; + goto out; + } + + if (len == 0) { + LOG_ERR("inotify FD closed"); + ret = 1; + goto out; + } + + /* Consume inotify data */ + bool have_create_event = false; + for (const char *ptr = buf; ptr < buf + len; ) { + const struct inotify_event *e = (const struct inotify_event *)ptr; + if (e->mask & IN_CREATE) { + LOG_DBG("inotify: CREATED: /dev/snd/%.*s", e->len, e->name); + have_create_event = true; + } + ptr += sizeof(*e) + e->len; + } + + if (have_create_event) { + inotify_rm_watch(ifd, wd); + wd = -1; + break; + } + } } } + +out: + if (wd >= 0) + inotify_rm_watch(ifd, wd); + if (ifd >= 0) + close (ifd); + return ret; } static struct module * From 5af070ee1db524e914799e2116363a6c38adbed8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 21 Aug 2021 11:08:06 +0200 Subject: [PATCH 208/611] log: LOG_ERRNO: include numeric value of errno --- log.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/log.c b/log.c index c1adcd0..467b6fd 100644 --- a/log.c +++ b/log.c @@ -84,7 +84,7 @@ _log(enum log_class log_class, const char *module, const char *file, int lineno, vfprintf(stderr, fmt, va); if (sys_errno != 0) - fprintf(stderr, ": %s", strerror(sys_errno)); + fprintf(stderr, ": %s (%d)", strerror(sys_errno), sys_errno); fputc('\n', stderr); } From 360e1fbada721a21a725b003a928263f49aa5b93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 21 Aug 2021 11:08:45 +0200 Subject: [PATCH 209/611] =?UTF-8?q?module/alsa:=20don=E2=80=99t=20re-creat?= =?UTF-8?q?e=20the=20/dev/snd=20inotify=20watcher=20after=20each=20connect?= =?UTF-8?q?=20failure?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When e.g. a USB soundcard is inserted, we get several CREATE events. In my experiments, we only succeed in connecting to ALSA after the last event. This means, we’ll have several CREATE events that we receive, remove the watcher, attempt to connect, fail, and then re-add the watcher. What if that “last” CREATE event occurs while our watcher has been removed? That’s right, we miss it, and will get stuck waiting forever. The solution is keep the watcher around. Now, if we’ve been successfully connected to ALSA for a long time, chances are we’ve built up events (for other cards, for example). We don’t want to trigger a storm of re-connect attempts, so drain the event queue after having been disconnected from ALSA. There *is* a small race here - if a card is removed and re-added *very* fast, we _may_ accidentally drain the CREATE event. I don’t see this happening in reality though. --- modules/alsa.c | 79 ++++++++++++++++++++++++++++++++++---------------- 1 file changed, 54 insertions(+), 25 deletions(-) diff --git a/modules/alsa.c b/modules/alsa.c index aa985b0..368b1ee 100644 --- a/modules/alsa.c +++ b/modules/alsa.c @@ -318,38 +318,64 @@ run(struct module *mod) { int ret = 1; - int wd = -1; - int ifd = inotify_init(); + int ifd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC); if (ifd < 0) { LOG_ERRNO("failed to inotify"); return 1; } + int wd = inotify_add_watch(ifd, "/dev/snd", IN_CREATE); + if (wd < 0) { + LOG_ERRNO("failed to create inotify watcher for /dev/snd"); + close(ifd); + return 1; + } + while (true) { enum run_state state = run_while_online(mod); switch (state) { case RUN_DONE: ret = 0; - break; + goto out; case RUN_ERROR: ret = 1; - break; + goto out; case RUN_FAILED_CONNECT: + break; + case RUN_DISCONNECTED: + /* + * We’ve been connected - drain the watcher + * + * We don’t want old, un-releated events (for other + * soundcards, for example) to trigger a storm of + * re-connect attempts. + */ + while (true) { + uint8_t buf[1024]; + ssize_t amount = read(ifd, buf, sizeof(buf)); + if (amount < 0) { + if (errno == EAGAIN) + break; + + LOG_ERRNO("failed to drain inotify watcher"); + ret = 1; + goto out; + } + + if (amount == 0) + break; + } + break; } - wd = inotify_add_watch(ifd, "/dev/snd", IN_CREATE); - if (wd < 0) { - LOG_ERRNO("failed to create inotify watcher for /dev/snd"); - ret = 1; - break; - } + bool have_create_event = false; - while (true) { + while (!have_create_event) { struct pollfd fds[] = {{.fd = mod->abort_fd, .events = POLLIN}, {.fd = ifd, .events = POLLIN}}; int r = poll(fds, sizeof(fds) / sizeof(fds[0]), -1); @@ -363,42 +389,45 @@ run(struct module *mod) goto out; } - if (fds[0].revents & POLLIN) { + if (fds[0].revents & (POLLIN | POLLHUP)) { ret = 0; goto out; } - if (fds[1].revents & POLLIN) { + if (fds[1].revents & POLLHUP) { + LOG_ERR("inotify socket closed"); + ret = 1; + goto out; + } + + assert(fds[1].revents & POLLIN); + + while (true) { char buf[1024]; ssize_t len = read(ifd, buf, sizeof(buf)); if (len < 0) { + if (errno == EAGAIN) + break; + LOG_ERRNO("failed to read inotify events"); ret = 1; goto out; } - if (len == 0) { - LOG_ERR("inotify FD closed"); - ret = 1; - goto out; - } + if (len == 0) + break; /* Consume inotify data */ - bool have_create_event = false; for (const char *ptr = buf; ptr < buf + len; ) { const struct inotify_event *e = (const struct inotify_event *)ptr; + if (e->mask & IN_CREATE) { LOG_DBG("inotify: CREATED: /dev/snd/%.*s", e->len, e->name); have_create_event = true; } - ptr += sizeof(*e) + e->len; - } - if (have_create_event) { - inotify_rm_watch(ifd, wd); - wd = -1; - break; + ptr += sizeof(*e) + e->len; } } } From a5be5509645fcdd88eb08005ac900c82ff2d34d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 21 Aug 2021 15:26:53 +0200 Subject: [PATCH 210/611] module/alsa: free channel list on each connect attempt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Otherwise we’ll keep adding the same channel(s) over and over again, for each (successful) connect attempt. I.e. if you plug and unplug an USB soundcard repeatedly, we’ll keep extending the channel list each time. --- modules/alsa.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/alsa.c b/modules/alsa.c index 368b1ee..d3f14e5 100644 --- a/modules/alsa.c +++ b/modules/alsa.c @@ -188,6 +188,9 @@ run_while_online(struct module *mod) struct private *m = mod->private; enum run_state ret = RUN_ERROR; + /* Make sure we aren’t still tracking channels from previous connects */ + tll_free(m->channels); + snd_mixer_t *handle; if (snd_mixer_open(&handle, 0) != 0) { LOG_ERR("failed to open handle"); From 0963afd2824b3e1b578babe6b062d4b42fe5bc5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 24 Aug 2021 16:57:22 +0200 Subject: [PATCH 211/611] =?UTF-8?q?example/conf:=20alsa:=20use=20the=20new?= =?UTF-8?q?=20=E2=80=98online=E2=80=99=20tag?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/configurations/laptop.conf | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/examples/configurations/laptop.conf b/examples/configurations/laptop.conf index 593d9f3..f079f2a 100644 --- a/examples/configurations/laptop.conf +++ b/examples/configurations/laptop.conf @@ -227,19 +227,24 @@ bar: mixer: Master content: map: - on-click: /bin/sh -c "amixer -q sset Speaker unmute && amixer -q sset Headphone unmute && amixer -q sset Master toggle" - tag: muted + tag: online values: - true: {string: {text: , font: *awesome, foreground: ffffff66}} - false: - ramp: - tag: volume - items: - - string: {text: , font: *awesome} - - string: {text: , font: *awesome} - - string: {text: , font: *awesome} - - string: {text: , font: *awesome} - - string: {text: , font: *awesome} + false: {string: {text: , font: *awesome, foreground: ff0000ff}} + true: + map: + on-click: /bin/sh -c "amixer -q sset Speaker unmute && amixer -q sset Headphone unmute && amixer -q sset Master toggle" + tag: muted + values: + true: {string: {text: , font: *awesome, foreground: ffffff66}} + false: + ramp: + tag: volume + items: + - string: {text: , font: *awesome} + - string: {text: , font: *awesome} + - string: {text: , font: *awesome} + - string: {text: , font: *awesome} + - string: {text: , font: *awesome} - backlight: name: intel_backlight content: [ string: {text: , font: *awesome}, string: {text: "{percent}%"}] From 7e7c011126967e2eca8f9bb979f8eab47c115820 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 25 Aug 2021 09:42:38 +0200 Subject: [PATCH 212/611] module/river: use wl_output_release() instead of wl_output_destroy() --- modules/river.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/modules/river.c b/modules/river.c index 897dbcc..ebc1e35 100644 --- a/modules/river.c +++ b/modules/river.c @@ -191,7 +191,7 @@ output_destroy(struct output *output) if (output->xdg_output != NULL) zxdg_output_v1_destroy(output->xdg_output); if (output->wl_output != NULL) - wl_output_destroy(output->wl_output); + wl_output_release(output->wl_output); } static void @@ -495,7 +495,7 @@ handle_global(void *data, struct wl_registry *registry, struct private *m = data; if (strcmp(interface, wl_output_interface.name) == 0) { - const uint32_t required = 1; + const uint32_t required = 3; if (!verify_iface_version(interface, version, required)) return; @@ -505,8 +505,14 @@ handle_global(void *data, struct wl_registry *registry, if (wl_output == NULL) return; + struct output output = { + .m = m, + .wl_output = wl_output, + .wl_name = name, + }; + mtx_lock(&m->mod->lock); - tll_push_back(m->outputs, ((struct output){.m = m, .wl_output = wl_output, .wl_name = name})); + tll_push_back(m->outputs, output); instantiate_output(&tll_back(m->outputs)); mtx_unlock(&m->mod->lock); } From eb94c8cceb743a9f87781b1f89c1d3f542bf0333 Mon Sep 17 00:00:00 2001 From: Nulo Date: Wed, 25 Aug 2021 13:20:33 -0300 Subject: [PATCH 213/611] Add layer option Only applies to Wayland and the default is still bottom. --- CHANGELOG.md | 2 ++ bar/bar.c | 1 + bar/bar.h | 2 ++ bar/private.h | 1 + bar/wayland.c | 6 +++++- config-verify.c | 7 +++++++ config.c | 13 +++++++++++++ doc/yambar.5.scd | 4 ++++ 8 files changed, 35 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 160575c..48a0e9b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,8 @@ individually. `border.width` is now a short-hand for setting all four borders to the same value (https://codeberg.org/dnkl/yambar/issues/77). +* bar: `layer: top|bottom`, allowing the layer which the bar is + rendered on to be changed. Wayland only - ignored on X11. * river: `per-output: false|true`. * `-d,--log-level=info|warning|error|none` command line option (https://codeberg.org/dnkl/yambar/issues/84). diff --git a/bar/bar.c b/bar/bar.c index 9d07830..21d95c5 100644 --- a/bar/bar.c +++ b/bar/bar.c @@ -433,6 +433,7 @@ bar_new(const struct bar_config *config) struct private *priv = calloc(1, sizeof(*priv)); priv->monitor = config->monitor != NULL ? strdup(config->monitor) : NULL; + priv->layer = config->layer; priv->location = config->location; priv->height = config->height; priv->background = config->background; diff --git a/bar/bar.h b/bar/bar.h index 45fb328..c9f94f4 100644 --- a/bar/bar.h +++ b/bar/bar.h @@ -17,12 +17,14 @@ struct bar { }; enum bar_location { BAR_TOP, BAR_BOTTOM }; +enum bar_layer { BAR_LAYER_TOP, BAR_LAYER_BOTTOM }; enum bar_backend { BAR_BACKEND_AUTO, BAR_BACKEND_XCB, BAR_BACKEND_WAYLAND }; struct bar_config { enum bar_backend backend; const char *monitor; + enum bar_layer layer; enum bar_location location; int height; int left_spacing, right_spacing; diff --git a/bar/private.h b/bar/private.h index 56e59bb..f216c99 100644 --- a/bar/private.h +++ b/bar/private.h @@ -6,6 +6,7 @@ struct private { /* From bar_config */ char *monitor; + enum bar_layer layer; enum bar_location location; int height; int left_spacing, right_spacing; diff --git a/bar/wayland.c b/bar/wayland.c index cb10eae..270b4ff 100644 --- a/bar/wayland.c +++ b/bar/wayland.c @@ -988,10 +988,14 @@ setup(struct bar *_bar) wl_surface_add_listener(backend->surface, &surface_listener, backend); + enum zwlr_layer_shell_v1_layer layer = bar->layer == BAR_LAYER_BOTTOM + ? ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM + : ZWLR_LAYER_SHELL_V1_LAYER_TOP; + backend->layer_surface = zwlr_layer_shell_v1_get_layer_surface( backend->layer_shell, backend->surface, backend->monitor != NULL ? backend->monitor->output : NULL, - ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM, "panel"); + layer, "panel"); if (backend->layer_surface == NULL) { LOG_ERR("failed to create layer shell surface"); diff --git a/config-verify.c b/config-verify.c index 5d4089a..67b21e1 100644 --- a/config-verify.c +++ b/config-verify.c @@ -404,6 +404,12 @@ verify_bar_location(keychain_t *chain, const struct yml_node *node) return conf_verify_enum(chain, node, (const char *[]){"top", "bottom"}, 2); } +static bool +verify_bar_layer(keychain_t *chain, const struct yml_node *node) +{ + return conf_verify_enum(chain, node, (const char *[]){"top", "bottom"}, 2); +} + bool conf_verify_bar(const struct yml_node *bar) { @@ -421,6 +427,7 @@ conf_verify_bar(const struct yml_node *bar) {"background", true, &conf_verify_color}, {"monitor", false, &conf_verify_string}, + {"layer", false, &verify_bar_layer}, {"spacing", false, &conf_verify_int}, {"left-spacing", false, &conf_verify_int}, diff --git a/config.c b/config.c index 241e25b..7f9583e 100644 --- a/config.c +++ b/config.c @@ -204,6 +204,7 @@ conf_to_bar(const struct yml_node *bar, enum bar_backend backend) struct bar_config conf = { .backend = backend, + .layer = BAR_LAYER_BOTTOM, }; /* @@ -228,6 +229,18 @@ conf_to_bar(const struct yml_node *bar, enum bar_backend backend) if (monitor != NULL) conf.monitor = yml_value_as_string(monitor); + const struct yml_node *layer = yml_get_value(bar, "layer"); + if (layer != NULL) { + const char *tmp = yml_value_as_string(layer); + if (strcmp(tmp, "top") == 0) + conf.layer = BAR_LAYER_TOP; + else if (strcmp(tmp, "bottom") == 0) + conf.layer = BAR_LAYER_BOTTOM; + else + assert(false); + } + + const struct yml_node *spacing = yml_get_value(bar, "spacing"); if (spacing != NULL) conf.left_spacing = conf.right_spacing = yml_value_as_int(spacing); diff --git a/doc/yambar.5.scd b/doc/yambar.5.scd index 9e1eefa..47041fb 100644 --- a/doc/yambar.5.scd +++ b/doc/yambar.5.scd @@ -45,6 +45,10 @@ types that are frequently used: : no : Monitor to place the bar on. If not specified, the primary monitor will be used +| layer +: string +: no +: Layer to put bar on. One of _top_ or _bottom_. Wayland only | left-spacing : int : no From 560d7464b4fd163c6f11c19b169f82c4b4145d41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 24 Aug 2021 19:55:09 +0200 Subject: [PATCH 214/611] module/foreign-toplevel: initial support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ‘content’ is a template; the module returns a list of toplevels, each one instantiated using the content template. * Each toplevel has 6 tags: - app-id (string) - title (string) - maximized (bool) - minimized (bool) - activated (bool) - fullscreen (bool) To show the application name and title of the currently active window, one can do: - foreign-toplevel: content: map: tag: activated values: false: {empty: {}} true: {string: {text: "{app-id}: {title}"}} --- ...oreign-toplevel-management-unstable-v1.xml | 270 ++++++++++++ modules/foreign-toplevel.c | 395 ++++++++++++++++++ modules/meson.build | 22 + plugin.c | 4 + 4 files changed, 691 insertions(+) create mode 100644 external/wlr-foreign-toplevel-management-unstable-v1.xml create mode 100644 modules/foreign-toplevel.c diff --git a/external/wlr-foreign-toplevel-management-unstable-v1.xml b/external/wlr-foreign-toplevel-management-unstable-v1.xml new file mode 100644 index 0000000..1081337 --- /dev/null +++ b/external/wlr-foreign-toplevel-management-unstable-v1.xml @@ -0,0 +1,270 @@ + + + + Copyright © 2018 Ilia Bozhinov + + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that the above copyright notice appear in + all copies and that both that copyright notice and this permission + notice appear in supporting documentation, and that the name of + the copyright holders not be used in advertising or publicity + pertaining to distribution of the software without specific, + written prior permission. The copyright holders make no + representations about the suitability of this software for any + purpose. It is provided "as is" without express or implied + warranty. + + THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + THIS SOFTWARE. + + + + + The purpose of this protocol is to enable the creation of taskbars + and docks by providing them with a list of opened applications and + letting them request certain actions on them, like maximizing, etc. + + After a client binds the zwlr_foreign_toplevel_manager_v1, each opened + toplevel window will be sent via the toplevel event + + + + + This event is emitted whenever a new toplevel window is created. It + is emitted for all toplevels, regardless of the app that has created + them. + + All initial details of the toplevel(title, app_id, states, etc.) will + be sent immediately after this event via the corresponding events in + zwlr_foreign_toplevel_handle_v1. + + + + + + + Indicates the client no longer wishes to receive events for new toplevels. + However the compositor may emit further toplevel_created events, until + the finished event is emitted. + + The client must not send any more requests after this one. + + + + + + This event indicates that the compositor is done sending events to the + zwlr_foreign_toplevel_manager_v1. The server will destroy the object + immediately after sending this request, so it will become invalid and + the client should free any resources associated with it. + + + + + + + A zwlr_foreign_toplevel_handle_v1 object represents an opened toplevel + window. Each app may have multiple opened toplevels. + + Each toplevel has a list of outputs it is visible on, conveyed to the + client with the output_enter and output_leave events. + + + + + This event is emitted whenever the title of the toplevel changes. + + + + + + + This event is emitted whenever the app-id of the toplevel changes. + + + + + + + This event is emitted whenever the toplevel becomes visible on + the given output. A toplevel may be visible on multiple outputs. + + + + + + + This event is emitted whenever the toplevel stops being visible on + the given output. It is guaranteed that an entered-output event + with the same output has been emitted before this event. + + + + + + + Requests that the toplevel be maximized. If the maximized state actually + changes, this will be indicated by the state event. + + + + + + Requests that the toplevel be unmaximized. If the maximized state actually + changes, this will be indicated by the state event. + + + + + + Requests that the toplevel be minimized. If the minimized state actually + changes, this will be indicated by the state event. + + + + + + Requests that the toplevel be unminimized. If the minimized state actually + changes, this will be indicated by the state event. + + + + + + Request that this toplevel be activated on the given seat. + There is no guarantee the toplevel will be actually activated. + + + + + + + The different states that a toplevel can have. These have the same meaning + as the states with the same names defined in xdg-toplevel + + + + + + + + + + + This event is emitted immediately after the zlw_foreign_toplevel_handle_v1 + is created and each time the toplevel state changes, either because of a + compositor action or because of a request in this protocol. + + + + + + + + This event is sent after all changes in the toplevel state have been + sent. + + This allows changes to the zwlr_foreign_toplevel_handle_v1 properties + to be seen as atomic, even if they happen via multiple events. + + + + + + Send a request to the toplevel to close itself. The compositor would + typically use a shell-specific method to carry out this request, for + example by sending the xdg_toplevel.close event. However, this gives + no guarantees the toplevel will actually be destroyed. If and when + this happens, the zwlr_foreign_toplevel_handle_v1.closed event will + be emitted. + + + + + + The rectangle of the surface specified in this request corresponds to + the place where the app using this protocol represents the given toplevel. + It can be used by the compositor as a hint for some operations, e.g + minimizing. The client is however not required to set this, in which + case the compositor is free to decide some default value. + + If the client specifies more than one rectangle, only the last one is + considered. + + The dimensions are given in surface-local coordinates. + Setting width=height=0 removes the already-set rectangle. + + + + + + + + + + + + + + + + This event means the toplevel has been destroyed. It is guaranteed there + won't be any more events for this zwlr_foreign_toplevel_handle_v1. The + toplevel itself becomes inert so any requests will be ignored except the + destroy request. + + + + + + Destroys the zwlr_foreign_toplevel_handle_v1 object. + + This request should be called either when the client does not want to + use the toplevel anymore or after the closed event to finalize the + destruction of the object. + + + + + + + + Requests that the toplevel be fullscreened on the given output. If the + fullscreen state and/or the outputs the toplevel is visible on actually + change, this will be indicated by the state and output_enter/leave + events. + + The output parameter is only a hint to the compositor. Also, if output + is NULL, the compositor should decide which output the toplevel will be + fullscreened on, if at all. + + + + + + + Requests that the toplevel be unfullscreened. If the fullscreen state + actually changes, this will be indicated by the state event. + + + + + + + + This event is emitted whenever the parent of the toplevel changes. + + No event is emitted when the parent handle is destroyed by the client. + + + + + diff --git a/modules/foreign-toplevel.c b/modules/foreign-toplevel.c new file mode 100644 index 0000000..dafc60f --- /dev/null +++ b/modules/foreign-toplevel.c @@ -0,0 +1,395 @@ +#include +#include +#include +#include + +#include + +#include +#include + +#define LOG_MODULE "foreign-toplevel" +#define LOG_ENABLE_DBG 1 +#include "../log.h" +#include "../plugin.h" +#include "../particles/dynlist.h" + +#include "wlr-foreign-toplevel-management-unstable-v1.h" + +struct toplevel { + struct module *mod; + struct zwlr_foreign_toplevel_handle_v1 *handle; + + char *app_id; + char *title; + + bool maximized; + bool minimized; + bool activated; + bool fullscreen; +}; + +struct private { + struct particle *template; + struct zwlr_foreign_toplevel_manager_v1 *manager; + tll(struct toplevel) toplevels; +}; + +static void +toplevel_free(struct toplevel *top) +{ + if (top->handle != NULL) + zwlr_foreign_toplevel_handle_v1_destroy(top->handle); + + free(top->app_id); + free(top->title); +} + +static void +destroy(struct module *mod) +{ + struct private *m = mod->private; + m->template->destroy(m->template); + + tll_foreach(m->toplevels, it) { + toplevel_free(&it->item); + tll_remove(m->toplevels, it); + } + + free(m); + module_default_destroy(mod); +} + +static const char * +description(struct module *mod) +{ + return "toplevel"; +} + +static struct exposable * +content(struct module *mod) +{ + const struct private *m = mod->private; + + mtx_lock(&mod->lock); + + size_t toplevel_count = tll_length(m->toplevels); + struct exposable *toplevels[toplevel_count]; + + size_t idx = 0; + tll_foreach(m->toplevels, it) { + struct tag_set tags = { + .tags = (struct tag *[]){ + tag_new_string(mod, "app-id", it->item.app_id), + tag_new_string(mod, "title", it->item.title), + tag_new_bool(mod, "maximized", it->item.maximized), + tag_new_bool(mod, "minimized", it->item.minimized), + tag_new_bool(mod, "activated", it->item.activated), + tag_new_bool(mod, "fullscreen", it->item.fullscreen), + }, + .count = 6, + }; + + toplevels[idx++] = m->template->instantiate(m->template, &tags); + tag_set_destroy(&tags); + } + + mtx_unlock(&mod->lock); + return dynlist_exposable_new(toplevels, toplevel_count, 0, 0); +} + +static bool +verify_iface_version(const char *iface, uint32_t version, uint32_t wanted) +{ + if (version >= wanted) + return true; + + LOG_ERR("%s: need interface version %u, but compositor only implements %u", + iface, wanted, version); + + return false; +} + +static void +title(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, const char *title) +{ + struct toplevel *top = data; + + mtx_lock(&top->mod->lock); + { + free(top->title); + top->title = title != NULL ? strdup(title) : NULL; + } + mtx_unlock(&top->mod->lock); +} + +static void +app_id(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, const char *app_id) +{ + struct toplevel *top = data; + + mtx_lock(&top->mod->lock); + { + free(top->app_id); + top->app_id = app_id != NULL ? strdup(app_id) : NULL; + } + mtx_unlock(&top->mod->lock); +} + +static void +output_enter(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, struct wl_output *output) +{ +} + +static void +output_leave(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, struct wl_output *output) +{ +} + +static void +state(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, struct wl_array *states) +{ + struct toplevel *top = data; + + bool maximized = false; + bool minimized = false; + bool activated = false; + bool fullscreen = false; + + enum zwlr_foreign_toplevel_handle_v1_state *state; + wl_array_for_each(state, states) { + switch (*state) { + case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED: maximized = true; break; + case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MINIMIZED: minimized = true; break; + case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED: activated = true; break; + case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN: fullscreen = true; break; + } + } + + mtx_lock(&top->mod->lock); + { + top->maximized = maximized; + top->minimized = minimized; + top->activated = activated; + top->fullscreen = fullscreen; + } + mtx_unlock(&top->mod->lock); +} + +static void +done(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle) +{ + struct toplevel *top = data; + const struct bar *bar = top->mod->bar; + bar->refresh(bar); +} + +static void +closed(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle) +{ + struct toplevel *top = data; + struct private *m = top->mod->private; + + tll_foreach(m->toplevels, it) { + if (it->item.handle == handle) { + toplevel_free(top); + tll_remove(m->toplevels, it); + break; + } + } +} + +static void +parent(void *data, + struct zwlr_foreign_toplevel_handle_v1 *handle, + struct zwlr_foreign_toplevel_handle_v1 *parent) +{ +} + +static const struct zwlr_foreign_toplevel_handle_v1_listener toplevel_listener = { + .title = &title, + .app_id = &app_id, + .output_enter = &output_enter, + .output_leave = &output_leave, + .state = &state, + .done = &done, + .closed = &closed, + .parent = &parent, +}; + +static void +toplevel(void *data, + struct zwlr_foreign_toplevel_manager_v1 *manager, + struct zwlr_foreign_toplevel_handle_v1 *handle) +{ + struct module *mod = data; + struct private *m = mod->private; + + struct toplevel toplevel = { + .mod = mod, + .handle = handle, + }; + + tll_push_back(m->toplevels, toplevel); + + zwlr_foreign_toplevel_handle_v1_add_listener( + handle, &toplevel_listener, &tll_back(m->toplevels)); +} + +static void +finished(void *data, + struct zwlr_foreign_toplevel_manager_v1 *manager) +{ + struct module *mod = data; + struct private *m = mod->private; + + assert(m->manager == manager); + zwlr_foreign_toplevel_manager_v1_destroy(m->manager); + m->manager = NULL; +} + +static const struct zwlr_foreign_toplevel_manager_v1_listener manager_listener = { + .toplevel = &toplevel, + .finished = &finished, +}; + +static void +handle_global(void *data, struct wl_registry *registry, + uint32_t name, const char *interface, uint32_t version) +{ + struct module *mod = data; + struct private *m = mod->private; + + if (strcmp(interface, zwlr_foreign_toplevel_manager_v1_interface.name) == 0) { + const uint32_t required = 2; + if (!verify_iface_version(interface, version, required)) + return; + + m->manager = wl_registry_bind( + registry, name, + &zwlr_foreign_toplevel_manager_v1_interface, required); + + zwlr_foreign_toplevel_manager_v1_add_listener( + m->manager, &manager_listener, mod); + } +} + +static void +handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) +{ +} + +static const struct wl_registry_listener registry_listener = { + .global = &handle_global, + .global_remove = &handle_global_remove, +}; + +static int +run(struct module *mod) +{ + struct private *m = mod->private; + int ret = -1; + + struct wl_display *display = NULL; + struct wl_registry *registry = NULL; + + if ((display = wl_display_connect(NULL)) == NULL) { + LOG_ERR("no Wayland compositor running"); + goto out; + } + + if ((registry = wl_display_get_registry(display)) == NULL || + wl_registry_add_listener(registry, ®istry_listener, mod) != 0) + { + LOG_ERR("failed to get Wayland registry"); + goto out; + } + + wl_display_roundtrip(display); + + while (true) { + wl_display_flush(display); + + struct pollfd fds[] = { + {.fd = mod->abort_fd, .events = POLLIN}, + {.fd = wl_display_get_fd(display), .events = POLLIN}, + }; + + int r = poll(fds, sizeof(fds) / sizeof(fds[0]), -1); + if (r < 0) { + if (errno == EINTR) + continue; + + LOG_ERRNO("failed to poll"); + break; + } + + if (fds[0].revents & (POLLIN | POLLHUP)) { + ret = 0; + break; + } + + if (fds[1].revents & POLLHUP) { + LOG_ERR("disconnected from the Wayland compositor"); + break; + } + + assert(fds[1].revents & POLLIN); + wl_display_dispatch(display); + } + +out: + tll_foreach(m->toplevels, it) { + toplevel_free(&it->item); + tll_remove(m->toplevels, it); + } + + if (m->manager != NULL) + zwlr_foreign_toplevel_manager_v1_destroy(m->manager); + if (registry != NULL) + wl_registry_destroy(registry); + if (display != NULL) + wl_display_disconnect(display); + return ret; +} + +static struct module * +ftop_new(struct particle *label) +{ + struct private *m = calloc(1, sizeof(*m)); + m->template = label; + + struct module *mod = module_common_new(); + mod->private = m; + mod->run = &run; + mod->destroy = &destroy; + mod->content = &content; + mod->description = &description; + return mod; +} + +static struct module * +from_conf(const struct yml_node *node, struct conf_inherit inherited) +{ + const struct yml_node *c = yml_get_value(node, "content"); + return ftop_new(conf_to_particle(c, inherited)); +} + +static bool +verify_conf(keychain_t *chain, const struct yml_node *node) +{ + static const struct attr_info attrs[] = { + MODULE_COMMON_ATTRS, + }; + + return conf_verify_dict(chain, node, attrs); +} + +const struct module_iface module_foreign_toplevel_iface = { + .verify_conf = &verify_conf, + .from_conf = &from_conf, +}; + +#if defined(CORE_PLUGINS_AS_SHARED_LIBRARIES) +extern const struct module_iface iface __attribute__((weak, alias("module_foreign_toplevel_iface"))); +#endif diff --git a/modules/meson.build b/modules/meson.build index 5b480d7..23111ae 100644 --- a/modules/meson.build +++ b/modules/meson.build @@ -52,6 +52,28 @@ if backend_wayland mod_data += { 'river': [[wl_proto_src + wl_proto_headers + river_proto_src + river_proto_headers], [dynlist]], } + + ftop_proto_headers = [] + ftop_proto_src = [] + + foreach prot : ['../external/wlr-foreign-toplevel-management-unstable-v1.xml'] + + ftop_proto_headers += custom_target( + prot.underscorify() + '-client-header', + output: '@BASENAME@.h', + input: prot, + command: [wscanner_prog, 'client-header', '@INPUT@', '@OUTPUT@']) + + ftop_proto_src += custom_target( + prot.underscorify() + '-private-code', + output: '@BASENAME@.c', + input: prot, + command: [wscanner_prog, 'private-code', '@INPUT@', '@OUTPUT@']) + endforeach + + mod_data += { + 'foreign-toplevel': [[ftop_proto_headers + ftop_proto_src], [dynlist]], + } endif foreach mod, data : mod_data diff --git a/plugin.c b/plugin.c index 05a2c1a..5e0e4bc 100644 --- a/plugin.c +++ b/plugin.c @@ -36,6 +36,7 @@ EXTERN_MODULE(alsa); EXTERN_MODULE(backlight); EXTERN_MODULE(battery); EXTERN_MODULE(clock); +EXTERN_MODULE(foreign_toplevel); EXTERN_MODULE(i3); EXTERN_MODULE(label); EXTERN_MODULE(mpd); @@ -111,6 +112,9 @@ init(void) REGISTER_CORE_MODULE(backlight, backlight); REGISTER_CORE_MODULE(battery, battery); REGISTER_CORE_MODULE(clock, clock); +#if defined(HAVE_PLUGIN_foreign_toplevel) + REGISTER_CORE_MODULE(foreign-toplevel, foreign_toplevel); +#endif REGISTER_CORE_MODULE(i3, i3); REGISTER_CORE_MODULE(label, label); REGISTER_CORE_MODULE(mpd, mpd); From 2173e0dc4d5dc0cdf07b603dc04026d25595c8f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 24 Aug 2021 20:08:20 +0200 Subject: [PATCH 215/611] doc: add man page for the new foreign-toplevel module --- doc/meson.build | 1 + doc/yambar-modules-foreign-toplevel.5.scd | 74 +++++++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 doc/yambar-modules-foreign-toplevel.5.scd diff --git a/doc/meson.build b/doc/meson.build index 4598ab2..a1550c4 100644 --- a/doc/meson.build +++ b/doc/meson.build @@ -6,6 +6,7 @@ scdoc_prog = find_program(scdoc.get_pkgconfig_variable('scdoc'), native: true) foreach man_src : ['yambar.1.scd', 'yambar.5.scd', 'yambar-decorations.5.scd', 'yambar-modules-alsa.5.scd', 'yambar-modules-backlight.5.scd', 'yambar-modules-battery.5.scd', 'yambar-modules-clock.5.scd', + 'yambar-modules-foreign-toplevel.5.scd', 'yambar-modules-i3.5.scd', 'yambar-modules-label.5.scd', 'yambar-modules-mpd.5.scd', 'yambar-modules-network.5.scd', 'yambar-modules-removables.5.scd', 'yambar-modules-river.5.scd', diff --git a/doc/yambar-modules-foreign-toplevel.5.scd b/doc/yambar-modules-foreign-toplevel.5.scd new file mode 100644 index 0000000..bb63024 --- /dev/null +++ b/doc/yambar-modules-foreign-toplevel.5.scd @@ -0,0 +1,74 @@ +yambar-modules-foreign-toplevel(5) + +# NAME +foreign-toplevel - This module provides information about toplevel windows in Wayland + +# DESCRIPTION + +This module uses the _wlr foreign toplevel management_ Wayland +protocol to provide information about currently open windows, such as +their application ID, window title, and their current state +(maximized/minimized/fullscreen/activated). + +The configuration for the foreign-toplevel module specifies a +_template_ particle. This particle will be instantiated once for each +window. + +Note: Wayland only. + + +# TAGS + +[[ *Name* +:[ *Type* +:[ *Description* +| app-id +: string +: The application ID (typically the application name) +| title +: string +: The window title +| maximized +: bool +: True if the window is currently maximized +| minimized +: bool +: True if the window is currently minimized +| fullscreen +: bool +: True if the window is currently fullscreened +| activated +: bool +: True if the window is currently activated (i.e. has focus) + + +# CONFIGURATION + +[[ *Name* +:[ *Type* +:[ *Req* +:[ *Description* +| content +: particle +: yes +: Template particle that will be instantiated once for each window + + +# EXAMPLES + +``` +bar: + left: + - foreign-toplevel: + content: + map: + tag: activated + values: + false: {empty: {}} + true: + - string: {text: "{app-id}: {title}"} +``` + +# SEE ALSO + +*yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5) From fe6cc43ad867577cb3dd96982b762c689d57d926 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 24 Aug 2021 20:08:45 +0200 Subject: [PATCH 216/611] doc: river: provide -> provides --- doc/yambar-modules-river.5.scd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/yambar-modules-river.5.scd b/doc/yambar-modules-river.5.scd index 341176e..c9327d5 100644 --- a/doc/yambar-modules-river.5.scd +++ b/doc/yambar-modules-river.5.scd @@ -1,7 +1,7 @@ yambar-modules-river(5) # NAME -river - This module provide information about the river tags +river - This module provides information about the river tags # DESCRIPTION From 01c6fc5f523337bec93436da9f2a430b46ad93f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 24 Aug 2021 20:08:53 +0200 Subject: [PATCH 217/611] changelog: foreign-toplevel module --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 48a0e9b..6248817 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,8 @@ * `online` tag to the `alsa` module. * alsa: `volume` and `muted` options, allowing you to configure which channels to use as source for the volume level and muted state. +* foreign-toplevel: Wayland module that provides information about + currently opened windows. ### Changed From 589a6f528ae31b5753812498e175b080f1ce4ec3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 25 Aug 2021 09:38:26 +0200 Subject: [PATCH 218/611] module/foreign-toplevel: track outputs each toplevel is mapped on MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Bind the foreign-toplevel-manager object *after* the first round of global objects. This ensures we bind all pre-existing wl-output objects before binding the toplevel manager. This is important, since otherwise we wont get any output_enter() events for the initial set of toplevels. * Bind xdg-output-manager, to be able to bind xdg-output objects for each wl-output. * Add xdg-output-listener to each wl/xdg-output, to be able to get the outputs’ names. * Add a list of outputs to each toplevel. The output_enter() event adds to this list, and output_leave() removes from it. * Add option ‘all-monitors’. When not set (the default), toplevels are only included in the generated content if they are mapped on the same output as the bar itself. When *not* set, all toplevels are always included in the generated content. --- doc/yambar-modules-foreign-toplevel.5.scd | 5 + modules/foreign-toplevel.c | 328 ++++++++++++++++++++-- modules/meson.build | 2 +- 3 files changed, 312 insertions(+), 23 deletions(-) diff --git a/doc/yambar-modules-foreign-toplevel.5.scd b/doc/yambar-modules-foreign-toplevel.5.scd index bb63024..6a65ec3 100644 --- a/doc/yambar-modules-foreign-toplevel.5.scd +++ b/doc/yambar-modules-foreign-toplevel.5.scd @@ -52,6 +52,11 @@ Note: Wayland only. : particle : yes : Template particle that will be instantiated once for each window +| all-monitors +: bool +: no +: When set to true, only windows on the same monitor the bar will be + used. The default is false. # EXAMPLES diff --git a/modules/foreign-toplevel.c b/modules/foreign-toplevel.c index dafc60f..fbe9342 100644 --- a/modules/foreign-toplevel.c +++ b/modules/foreign-toplevel.c @@ -15,6 +15,22 @@ #include "../particles/dynlist.h" #include "wlr-foreign-toplevel-management-unstable-v1.h" +#include "xdg-output-unstable-v1.h" + +#define min(x, y) ((x) < (y) ? (x) : (y)) + +static const int required_manager_interface_version = 2; + +struct output { + struct module *mod; + + uint32_t wl_name; + struct wl_output *wl_output; + struct zxdg_output_v1 *xdg_output; + + char *name; + bool use_output_release; +}; struct toplevel { struct module *mod; @@ -27,14 +43,35 @@ struct toplevel { bool minimized; bool activated; bool fullscreen; + + tll(const struct output *) outputs; }; struct private { struct particle *template; + uint32_t manager_wl_name; struct zwlr_foreign_toplevel_manager_v1 *manager; + struct zxdg_output_manager_v1 *xdg_output_manager; + + bool all_monitors; tll(struct toplevel) toplevels; + tll(struct output) outputs; }; +static void +output_free(struct output *output) +{ + free(output->name); + if (output->xdg_output != NULL) + zxdg_output_v1_destroy(output->xdg_output); + if (output->wl_output != NULL) { + if (output->use_output_release) + wl_output_release(output->wl_output); + else + wl_output_destroy(output->wl_output); + } +} + static void toplevel_free(struct toplevel *top) { @@ -43,6 +80,7 @@ toplevel_free(struct toplevel *top) free(top->app_id); free(top->title); + tll_free(top->outputs); } static void @@ -51,10 +89,8 @@ destroy(struct module *mod) struct private *m = mod->private; m->template->destroy(m->template); - tll_foreach(m->toplevels, it) { - toplevel_free(&it->item); - tll_remove(m->toplevels, it); - } + assert(tll_length(m->toplevels) == 0); + assert(tll_length(m->outputs) == 0); free(m); module_default_destroy(mod); @@ -73,11 +109,34 @@ content(struct module *mod) mtx_lock(&mod->lock); - size_t toplevel_count = tll_length(m->toplevels); + const size_t toplevel_count = tll_length(m->toplevels); + size_t show_count = 0; struct exposable *toplevels[toplevel_count]; - size_t idx = 0; + const char *current_output = mod->bar->output_name(mod->bar); + tll_foreach(m->toplevels, it) { + const struct toplevel *top = &it->item; + + bool show = false; + + if (m->all_monitors) + show = true; + else if (current_output != NULL) { + tll_foreach(top->outputs, it2) { + const struct output *output = it2->item; + if (output->name != NULL && + strcmp(output->name, current_output) == 0) + { + show = true; + break; + } + } + } + + if (!show) + continue; + struct tag_set tags = { .tags = (struct tag *[]){ tag_new_string(mod, "app-id", it->item.app_id), @@ -90,12 +149,12 @@ content(struct module *mod) .count = 6, }; - toplevels[idx++] = m->template->instantiate(m->template, &tags); + toplevels[show_count++] = m->template->instantiate(m->template, &tags); tag_set_destroy(&tags); } mtx_unlock(&mod->lock); - return dynlist_exposable_new(toplevels, toplevel_count, 0, 0); + return dynlist_exposable_new(toplevels, show_count, 0, 0); } static bool @@ -110,6 +169,53 @@ verify_iface_version(const char *iface, uint32_t version, uint32_t wanted) return false; } +static void +xdg_output_handle_logical_position(void *data, + struct zxdg_output_v1 *xdg_output, + int32_t x, int32_t y) +{ +} + +static void +xdg_output_handle_logical_size(void *data, struct zxdg_output_v1 *xdg_output, + int32_t width, int32_t height) +{ +} + +static void +xdg_output_handle_done(void *data, struct zxdg_output_v1 *xdg_output) +{ +} + +static void +xdg_output_handle_name(void *data, struct zxdg_output_v1 *xdg_output, + const char *name) +{ + struct output *output = data; + struct module *mod = output->mod; + + mtx_lock(&mod->lock); + { + free(output->name); + output->name = name != NULL ? strdup(name) : NULL; + } + mtx_unlock(&mod->lock); +} + +static void +xdg_output_handle_description(void *data, struct zxdg_output_v1 *xdg_output, + const char *description) +{ +} + +static struct zxdg_output_v1_listener xdg_output_listener = { + .logical_position = xdg_output_handle_logical_position, + .logical_size = xdg_output_handle_logical_size, + .done = xdg_output_handle_done, + .name = xdg_output_handle_name, + .description = xdg_output_handle_description, +}; + static void title(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, const char *title) { @@ -137,17 +243,88 @@ app_id(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, const char *a } static void -output_enter(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, struct wl_output *output) +output_enter(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, + struct wl_output *wl_output) { + struct toplevel *top = data; + struct module *mod = top->mod; + struct private *m = mod->private; + + mtx_lock(&mod->lock); + + const struct output *output = NULL; + tll_foreach(m->outputs, it) { + if (it->item.wl_output == wl_output) { + output = &it->item; + break; + } + } + + if (output == NULL) { + LOG_ERR("output-enter event on untracked output"); + goto out; + } + + tll_foreach(top->outputs, it) { + if (it->item == output) { + LOG_ERR("output-enter event on output we're already on"); + goto out; + } + } + + LOG_DBG("mapped: %s:%s on %s", top->app_id, top->title, output->name); + tll_push_back(top->outputs, output); + +out: + mtx_unlock(&mod->lock); } static void -output_leave(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, struct wl_output *output) +output_leave(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, + struct wl_output *wl_output) { + struct toplevel *top = data; + struct module *mod = top->mod; + struct private *m = mod->private; + + mtx_lock(&mod->lock); + + const struct output *output = NULL; + tll_foreach(m->outputs, it) { + if (it->item.wl_output == wl_output) { + output = &it->item; + break; + } + } + + if (output == NULL) { + LOG_ERR("output-leave event on untracked output"); + goto out; + } + + bool output_removed = false; + tll_foreach(top->outputs, it) { + if (it->item == output) { + LOG_DBG("unmapped: %s:%s from %s", + top->app_id, top->title, output->name); + tll_remove(top->outputs, it); + output_removed = true; + break; + } + } + + if (!output_removed) { + LOG_ERR("output-leave event on an output we're not on"); + goto out; + } + +out: + mtx_unlock(&mod->lock); } static void -state(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, struct wl_array *states) +state(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, + struct wl_array *states) { struct toplevel *top = data; @@ -188,8 +365,10 @@ static void closed(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle) { struct toplevel *top = data; - struct private *m = top->mod->private; + struct module *mod = top->mod; + struct private *m = mod->private; + mtx_lock(&mod->lock); tll_foreach(m->toplevels, it) { if (it->item.handle == handle) { toplevel_free(top); @@ -197,6 +376,7 @@ closed(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle) break; } } + mtx_unlock(&mod->lock); } static void @@ -230,10 +410,14 @@ toplevel(void *data, .handle = handle, }; - tll_push_back(m->toplevels, toplevel); + mtx_lock(&mod->lock); + { + tll_push_back(m->toplevels, toplevel); - zwlr_foreign_toplevel_handle_v1_add_listener( - handle, &toplevel_listener, &tll_back(m->toplevels)); + zwlr_foreign_toplevel_handle_v1_add_listener( + handle, &toplevel_listener, &tll_back(m->toplevels)); + } + mtx_unlock(&mod->lock); } static void @@ -253,6 +437,22 @@ static const struct zwlr_foreign_toplevel_manager_v1_listener manager_listener = .finished = &finished, }; +static void +output_xdg_output(struct output *output) +{ + struct private *m = output->mod->private; + + if (m->xdg_output_manager == NULL) + return; + if (output->xdg_output != NULL) + return; + + output->xdg_output = zxdg_output_manager_v1_get_xdg_output( + m->xdg_output_manager, output->wl_output); + zxdg_output_v1_add_listener( + output->xdg_output, &xdg_output_listener, output); +} + static void handle_global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) @@ -261,22 +461,79 @@ handle_global(void *data, struct wl_registry *registry, struct private *m = mod->private; if (strcmp(interface, zwlr_foreign_toplevel_manager_v1_interface.name) == 0) { + if (!verify_iface_version(interface, version, required_manager_interface_version)) + return; + + m->manager_wl_name = name; + } + + else if (strcmp(interface, wl_output_interface.name) == 0) { + const uint32_t required = 1; + if (!verify_iface_version(interface, version, required)) + return; + + struct output output = { + .mod = mod, + .wl_name = name, + .wl_output = wl_registry_bind( + registry, name, + &wl_output_interface, min(version, WL_OUTPUT_RELEASE_SINCE_VERSION)), + .use_output_release = version >= WL_OUTPUT_RELEASE_SINCE_VERSION, + }; + + mtx_lock(&mod->lock); + tll_push_back(m->outputs, output); + output_xdg_output(&tll_back(m->outputs)); + mtx_unlock(&mod->lock); + } + + else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0) { const uint32_t required = 2; if (!verify_iface_version(interface, version, required)) return; - m->manager = wl_registry_bind( - registry, name, - &zwlr_foreign_toplevel_manager_v1_interface, required); + m->xdg_output_manager = wl_registry_bind( + registry, name, &zxdg_output_manager_v1_interface, required); - zwlr_foreign_toplevel_manager_v1_add_listener( - m->manager, &manager_listener, mod); + mtx_lock(&mod->lock); + tll_foreach(m->outputs, it) + output_xdg_output(&it->item); + mtx_unlock(&mod->lock); } } static void handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) { + struct module *mod = data; + struct private *m = mod->private; + + mtx_lock(&mod->lock); + + tll_foreach(m->outputs, it) { + const struct output *output = &it->item; + if (output->wl_name == name) { + + /* Loop all toplevels */ + tll_foreach(m->toplevels, it2) { + + /* And remove this output from their list of tracked + * outputs */ + tll_foreach(it2->item.outputs, it3) { + if (it3->item == output) { + tll_remove(it2->item.outputs, it3); + break; + } + } + } + + tll_remove(m->outputs, it); + goto out; + } + } + +out: + mtx_unlock(&mod->lock); } static const struct wl_registry_listener registry_listener = { @@ -307,6 +564,20 @@ run(struct module *mod) wl_display_roundtrip(display); + if (m->manager_wl_name == 0) { + LOG_ERR( + "compositor does not implement the foreign-toplevel-manager interface"); + goto out; + } + + m->manager = wl_registry_bind( + registry, m->manager_wl_name, + &zwlr_foreign_toplevel_manager_v1_interface, + required_manager_interface_version); + + zwlr_foreign_toplevel_manager_v1_add_listener( + m->manager, &manager_listener, mod); + while (true) { wl_display_flush(display); @@ -344,6 +615,13 @@ out: tll_remove(m->toplevels, it); } + tll_foreach(m->outputs, it) { + output_free(&it->item); + tll_remove(m->outputs, it); + } + + if (m->xdg_output_manager != NULL) + zxdg_output_manager_v1_destroy(m->xdg_output_manager); if (m->manager != NULL) zwlr_foreign_toplevel_manager_v1_destroy(m->manager); if (registry != NULL) @@ -354,10 +632,11 @@ out: } static struct module * -ftop_new(struct particle *label) +ftop_new(struct particle *label, bool all_monitors) { struct private *m = calloc(1, sizeof(*m)); m->template = label; + m->all_monitors = all_monitors; struct module *mod = module_common_new(); mod->private = m; @@ -372,13 +651,18 @@ static struct module * from_conf(const struct yml_node *node, struct conf_inherit inherited) { const struct yml_node *c = yml_get_value(node, "content"); - return ftop_new(conf_to_particle(c, inherited)); + const struct yml_node *all_monitors = yml_get_value(node, "all-monitors"); + + return ftop_new( + conf_to_particle(c, inherited), + all_monitors != NULL ? yml_value_as_bool(all_monitors) : false); } static bool verify_conf(keychain_t *chain, const struct yml_node *node) { static const struct attr_info attrs[] = { + {"all-monitors", false, &conf_verify_bool}, MODULE_COMMON_ATTRS, }; diff --git a/modules/meson.build b/modules/meson.build index 23111ae..dbaa7a7 100644 --- a/modules/meson.build +++ b/modules/meson.build @@ -72,7 +72,7 @@ if backend_wayland endforeach mod_data += { - 'foreign-toplevel': [[ftop_proto_headers + ftop_proto_src], [dynlist]], + 'foreign-toplevel': [[wl_proto_src + wl_proto_headers + ftop_proto_headers + ftop_proto_src], [dynlist]], } endif From 9681e0aabef1922894aff6587388c5baa72352ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 25 Aug 2021 09:46:23 +0200 Subject: [PATCH 219/611] module/foreign-toplevel: require version 3 of wl-output interface The bar itself already does this, and doing so means we can always use wl_output_release() (instead of wl_output_destroy()). --- modules/foreign-toplevel.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/modules/foreign-toplevel.c b/modules/foreign-toplevel.c index fbe9342..e71e825 100644 --- a/modules/foreign-toplevel.c +++ b/modules/foreign-toplevel.c @@ -29,7 +29,6 @@ struct output { struct zxdg_output_v1 *xdg_output; char *name; - bool use_output_release; }; struct toplevel { @@ -64,12 +63,8 @@ output_free(struct output *output) free(output->name); if (output->xdg_output != NULL) zxdg_output_v1_destroy(output->xdg_output); - if (output->wl_output != NULL) { - if (output->use_output_release) - wl_output_release(output->wl_output); - else - wl_output_destroy(output->wl_output); - } + if (output->wl_output != NULL) + wl_output_release(output->wl_output); } static void @@ -468,7 +463,7 @@ handle_global(void *data, struct wl_registry *registry, } else if (strcmp(interface, wl_output_interface.name) == 0) { - const uint32_t required = 1; + const uint32_t required = 3; if (!verify_iface_version(interface, version, required)) return; @@ -476,9 +471,7 @@ handle_global(void *data, struct wl_registry *registry, .mod = mod, .wl_name = name, .wl_output = wl_registry_bind( - registry, name, - &wl_output_interface, min(version, WL_OUTPUT_RELEASE_SINCE_VERSION)), - .use_output_release = version >= WL_OUTPUT_RELEASE_SINCE_VERSION, + registry, name, &wl_output_interface, required), }; mtx_lock(&mod->lock); From 103c3102a99477a50efc701f6babdeb9ee20c602 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 25 Aug 2021 10:47:40 +0200 Subject: [PATCH 220/611] =?UTF-8?q?module/river:=20rename=20the=20?= =?UTF-8?q?=E2=80=98per-output=E2=80=99=20option=20to=20=E2=80=98all-monit?= =?UTF-8?q?ors=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This also inverts its meaning. --- CHANGELOG.md | 2 +- doc/yambar-modules-river.5.scd | 8 ++++---- modules/river.c | 16 ++++++++-------- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6248817..08a7ddb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,7 @@ (https://codeberg.org/dnkl/yambar/issues/77). * bar: `layer: top|bottom`, allowing the layer which the bar is rendered on to be changed. Wayland only - ignored on X11. -* river: `per-output: false|true`. +* river: `all-monitors: false|true`. * `-d,--log-level=info|warning|error|none` command line option (https://codeberg.org/dnkl/yambar/issues/84). * river: support for the river-status protocol, version 2 (‘urgent’ diff --git a/doc/yambar-modules-river.5.scd b/doc/yambar-modules-river.5.scd index c9327d5..96be605 100644 --- a/doc/yambar-modules-river.5.scd +++ b/doc/yambar-modules-river.5.scd @@ -63,12 +63,12 @@ once for all 32 river tags. This means you probably want to use a : particle : yes : Template particle that will be instantiated once for all of the 32 river tags. -| per-output +| all-monitors : bool : no -: When set to false (the default), tags reflect the union of all - outputs. When set to true, tags reflect river tags and seats for - the output yambar is on only. +: When set to false (the default), tags reflect river tags and seats + for the monitor yambar is on only. When set to true, tags reflect + the union of all monitors. # EXAMPLES diff --git a/modules/river.c b/modules/river.c index ebc1e35..f9854f2 100644 --- a/modules/river.c +++ b/modules/river.c @@ -49,7 +49,7 @@ struct private { struct zriver_status_manager_v1 *status_manager; struct particle *template; struct particle *title; - bool per_output; + bool all_monitors; bool is_starting_up; tll(struct output) outputs; @@ -90,7 +90,7 @@ content(struct module *mod) tll_foreach(m->outputs, it) { const struct output *output = &it->item; - if (m->per_output && + if (!m->all_monitors && output_bar_is_on != NULL && output->name != NULL && strcmp(output->name, output_bar_is_on) != 0) { @@ -420,7 +420,7 @@ focused_view(void *data, struct zriver_seat_status_v1 *zriver_seat_status_v1, const char *output_bar_is_on = mod->bar->output_name(mod->bar); - if (!seat->m->per_output || + if (seat->m->all_monitors || (output_bar_is_on != NULL && seat->output != NULL && seat->output->name != NULL && strcmp(output_bar_is_on, seat->output->name) == 0)) @@ -696,12 +696,12 @@ out: } static struct module * -river_new(struct particle *template, struct particle *title, bool per_output) +river_new(struct particle *template, struct particle *title, bool all_monitors) { struct private *m = calloc(1, sizeof(*m)); m->template = template; m->title = title; - m->per_output = per_output; + m->all_monitors = all_monitors; m->is_starting_up = true; struct module *mod = module_common_new(); @@ -719,12 +719,12 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) { const struct yml_node *c = yml_get_value(node, "content"); const struct yml_node *title = yml_get_value(node, "title"); - const struct yml_node *per_output = yml_get_value(node, "per-output"); + const struct yml_node *all_monitors = yml_get_value(node, "all-monitors"); return river_new( conf_to_particle(c, inherited), title != NULL ? conf_to_particle(title, inherited) : NULL, - per_output != NULL ? yml_value_as_bool(per_output) : false); + all_monitors != NULL ? yml_value_as_bool(all_monitors) : false); } static bool @@ -732,7 +732,7 @@ verify_conf(keychain_t *chain, const struct yml_node *node) { static const struct attr_info attrs[] = { {"title", false, &conf_verify_particle}, - {"per-output", false, &conf_verify_bool}, + {"all-monitors", false, &conf_verify_bool}, MODULE_COMMON_ATTRS, }; From ca43eb3016ef5ebe5cbdfe9bea2fbbb6410eeede Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 25 Aug 2021 13:56:01 +0200 Subject: [PATCH 221/611] =?UTF-8?q?particle/dynlist:=20don=E2=80=99t=20adj?= =?UTF-8?q?ust=20spacing=20if=20all=20sub-items=20are=20zero-width?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit That is, if all sub-items are zero-width, make sure *we* return a zero width, instead of a negative width. --- particles/dynlist.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/particles/dynlist.c b/particles/dynlist.c index 8c6319c..5b64dbe 100644 --- a/particles/dynlist.c +++ b/particles/dynlist.c @@ -1,6 +1,7 @@ #include "dynlist.h" #include +#include #define LOG_MODULE "dynlist" #include "../log.h" @@ -36,15 +37,24 @@ dynlist_begin_expose(struct exposable *exposable) const struct private *e = exposable->private; exposable->width = 0; + bool have_at_least_one = false; for (size_t i = 0; i < e->count; i++) { struct exposable *ee = e->exposables[i]; e->widths[i] = ee->begin_expose(ee); - exposable->width += e->left_spacing + e->widths[i] + e->right_spacing; + assert(e->widths[i] >= 0); + + if (e->widths[i] > 0) { + exposable->width += e->left_spacing + e->widths[i] + e->right_spacing; + have_at_least_one = true; + } } - exposable->width -= e->left_spacing + e->right_spacing; + if (have_at_least_one) + exposable->width -= e->left_spacing + e->right_spacing; + else + assert(exposable->width == 0); return exposable->width; } From 73e1d328c3d81491d4ac214b269b8c6c1138b5d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 25 Aug 2021 13:57:19 +0200 Subject: [PATCH 222/611] =?UTF-8?q?particle/list:=20don=E2=80=99t=20adjust?= =?UTF-8?q?=20spacing=20if=20all=20sub-items=20are=20zero-width?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit That is, if all sub-items are zero-width, make sure *we* return a zero width, instead of a negative width. --- particles/list.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/particles/list.c b/particles/list.c index 9f03231..19ff15b 100644 --- a/particles/list.c +++ b/particles/list.c @@ -39,18 +39,29 @@ static int begin_expose(struct exposable *exposable) { const struct eprivate *e = exposable->private; + bool have_at_least_one = false; - exposable->width = exposable->particle->left_margin; + exposable->width = 0; for (size_t i = 0; i < e->count; i++) { struct exposable *ee = e->exposables[i]; e->widths[i] = ee->begin_expose(ee); - exposable->width += e->left_spacing + e->widths[i] + e->right_spacing; + assert(e->widths[i] >= 0); + + if (e->widths[i] > 0) { + exposable->width += e->left_spacing + e->widths[i] + e->right_spacing; + have_at_least_one = true; + } } - exposable->width -= e->left_spacing + e->right_spacing; - exposable->width += exposable->particle->right_margin; + if (have_at_least_one) { + exposable->width -= e->left_spacing + e->right_spacing; + exposable->width += exposable->particle->left_margin; + exposable->width += exposable->particle->right_margin; + } else + assert(exposable->width == 0); + return exposable->width; } From dab6428859057e870012dd01f4f131d56ccab2ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 25 Aug 2021 13:57:35 +0200 Subject: [PATCH 223/611] =?UTF-8?q?particle/map:=20don=E2=80=99t=20add=20m?= =?UTF-8?q?argins=20if=20all=20sub-items=20are=20zero-width?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit That is, if all sub-items are zero-width, make sure *we* return a zero width. --- particles/map.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/particles/map.c b/particles/map.c index e2d8c03..980bdd1 100644 --- a/particles/map.c +++ b/particles/map.c @@ -41,11 +41,13 @@ begin_expose(struct exposable *exposable) { struct eprivate *e = exposable->private; - exposable->width = ( - exposable->particle->left_margin + - e->exposable->begin_expose(e->exposable) + - exposable->particle->right_margin); + int width = e->exposable->begin_expose(e->exposable); + assert(width >= 0); + if (width > 0) + width += exposable->particle->left_margin + exposable->particle->right_margin; + + exposable->width = width; return exposable->width; } From def90edde1d13f6876dd1fdc8670fffbe591c8b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 25 Aug 2021 13:58:07 +0200 Subject: [PATCH 224/611] =?UTF-8?q?particle/progress-bar:=20don=E2=80=99t?= =?UTF-8?q?=20add=20margins=20if=20all=20sub-items=20are=20zero-width?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit That is, if all sub-items are zero-width, make sure *we* return a zero width. --- particles/progress-bar.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/particles/progress-bar.c b/particles/progress-bar.c index 52d1e74..7011feb 100644 --- a/particles/progress-bar.c +++ b/particles/progress-bar.c @@ -57,14 +57,27 @@ static int begin_expose(struct exposable *exposable) { struct eprivate *e = exposable->private; + bool have_at_least_one = false; - /* Margins */ - exposable->width = exposable->particle->left_margin + - exposable->particle->right_margin; + exposable->width = 0; /* Sub-exposables */ - for (size_t i = 0; i < e->count; i++) - exposable->width += e->exposables[i]->begin_expose(e->exposables[i]); + for (size_t i = 0; i < e->count; i++) { + int width = e->exposables[i]->begin_expose(e->exposables[i]); + + assert(width >= 0); + if (width >= 0) { + exposable->width += width; + have_at_least_one = true; + } + } + + /* Margins */ + if (have_at_least_one) { + exposable->width = exposable->particle->left_margin + + exposable->particle->right_margin; + } else + assert(exposable->width == 0); return exposable->width; } From af0b7e57d8fdcc3abf9ce685d45104ee3efdd5e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 25 Aug 2021 13:58:20 +0200 Subject: [PATCH 225/611] =?UTF-8?q?particle/ramp:=20don=E2=80=99t=20add=20?= =?UTF-8?q?margins=20if=20all=20sub-items=20are=20zero-width?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit That is, if all sub-items are zero-width, make sure *we* return a zero width. --- particles/ramp.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/particles/ramp.c b/particles/ramp.c index 3fa2fc8..1d4cb59 100644 --- a/particles/ramp.c +++ b/particles/ramp.c @@ -37,11 +37,13 @@ begin_expose(struct exposable *exposable) { struct eprivate *e = exposable->private; - exposable->width = ( - exposable->particle->left_margin + - e->exposable->begin_expose(e->exposable) + - exposable->particle->right_margin); + int width = e->exposable->begin_expose(e->exposable); + assert(width >= 0); + if (width > 0) + width += exposable->particle->left_margin + exposable->particle->right_margin; + + exposable->width = width; return exposable->width; } From 7ca22a6dabab0c7daa0475e41e259a3eaf64f58e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 25 Aug 2021 13:58:40 +0200 Subject: [PATCH 226/611] =?UTF-8?q?bar:=20don=E2=80=99t=20adjust=20spacing?= =?UTF-8?q?=20for=20left/center/right=20widths=20if=20all=20exposables=20a?= =?UTF-8?q?re=20zero-width?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bar/bar.c | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/bar/bar.c b/bar/bar.c index 21d95c5..b29936b 100644 --- a/bar/bar.c +++ b/bar/bar.c @@ -55,9 +55,16 @@ calculate_widths(const struct private *b, int *left, int *center, int *right) } /* No spacing on the edges (that's what the margins are for) */ - *left -= b->left_spacing + b->right_spacing; - *center -= b->left_spacing + b->right_spacing; - *right -= b->left_spacing + b->right_spacing; + if (*left > 0) + *left -= b->left_spacing + b->right_spacing; + if (*center > 0) + *center -= b->left_spacing + b->right_spacing; + if (*right > 0) + *right -= b->left_spacing + b->right_spacing; + + assert(*left >= 0); + assert(*center >= 0); + assert(*right >= 0); } static void @@ -99,6 +106,7 @@ expose(const struct bar *_bar) if (e != NULL) e->destroy(e); bar->left.exps[i] = module_begin_expose(m); + assert(bar->left.exps[i]->width >= 0); } for (size_t i = 0; i < bar->center.count; i++) { @@ -107,6 +115,7 @@ expose(const struct bar *_bar) if (e != NULL) e->destroy(e); bar->center.exps[i] = module_begin_expose(m); + assert(bar->center.exps[i]->width >= 0); } for (size_t i = 0; i < bar->right.count; i++) { @@ -115,6 +124,7 @@ expose(const struct bar *_bar) if (e != NULL) e->destroy(e); bar->right.exps[i] = module_begin_expose(m); + assert(bar->right.exps[i]->width >= 0); } int left_width, center_width, right_width; @@ -196,7 +206,8 @@ on_mouse(struct bar *_bar, enum mouse_event event, enum mouse_button btn, for (size_t i = 0; i < bar->left.count; i++) { struct exposable *e = bar->left.exps[i]; - if (e->width == 0) continue; + if (e->width == 0) + continue; mx += bar->left_spacing; if (x >= mx && x < mx + e->width) { @@ -212,7 +223,8 @@ on_mouse(struct bar *_bar, enum mouse_event event, enum mouse_button btn, for (size_t i = 0; i < bar->center.count; i++) { struct exposable *e = bar->center.exps[i]; - if (e->width == 0) continue; + if (e->width == 0) + continue; mx += bar->left_spacing; if (x >= mx && x < mx + e->width) { @@ -224,15 +236,16 @@ on_mouse(struct bar *_bar, enum mouse_event event, enum mouse_button btn, mx += e->width + bar->right_spacing; } - mx = bar->width - (right_width - + bar->left_spacing + + mx = bar->width - (right_width + + bar->left_spacing + bar->right_margin + bar->border.right_width); for (size_t i = 0; i < bar->right.count; i++) { struct exposable *e = bar->right.exps[i]; - if (e->width == 0) continue; + if (e->width == 0) + continue; mx += bar->left_spacing; if (x >= mx && x < mx + e->width) { From b8dd2471117d1f24d2a77b9d9839d165c8b00f33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 25 Aug 2021 19:03:32 +0200 Subject: [PATCH 227/611] meson: run generate_version.sh in a C locale MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, only the date command inside the script was run with LC_TIME=C. But there’s no reason to be that conservative; we absolutely do not want _anything_ in that script to generate locale dependent output. --- generate-version.sh | 2 +- meson.build | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/generate-version.sh b/generate-version.sh index d17cd40..458f4a1 100755 --- a/generate-version.sh +++ b/generate-version.sh @@ -17,7 +17,7 @@ if [ -d "${src_dir}/.git" ] && command -v git > /dev/null; then git_branch=$(git rev-parse --abbrev-ref HEAD) cd "${workdir}" - new_version="${git_version} ($(env LC_TIME=C date "+%b %d %Y"), branch '${git_branch}')" + new_version="${git_version} ($(date "+%b %d %Y"), branch '${git_branch}')" else new_version="${default_version}" fi diff --git a/meson.build b/meson.build index 23f6731..f621b43 100644 --- a/meson.build +++ b/meson.build @@ -95,12 +95,13 @@ subdir('decorations') subdir('particles') subdir('modules') +env = find_program('env', native: true) generate_version_sh = files('generate-version.sh') version = custom_target( 'generate_version', build_always_stale: true, output: 'version.h', - command: [generate_version_sh, meson.project_version(), '@SOURCE_ROOT@', '@OUTPUT@']) + command: [env, 'LC_ALL=C', generate_version_sh, meson.project_version(), '@SOURCE_ROOT@', '@OUTPUT@']) yambar = executable( 'yambar', From 7c7c4e7ce9031af946ce0f3f2068a133db1cec1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 26 Aug 2021 09:42:41 +0200 Subject: [PATCH 228/611] module/alsa: rename {volume,muted}_channel -> {volume,muted}_name --- modules/alsa.c | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/modules/alsa.c b/modules/alsa.c index d3f14e5..deee3de 100644 --- a/modules/alsa.c +++ b/modules/alsa.c @@ -19,8 +19,8 @@ struct private { char *card; char *mixer; - char *volume_channel; - char *muted_channel; + char *volume_name; + char *muted_name; struct particle *label; tll(snd_mixer_selem_channel_id_t) channels; @@ -40,8 +40,8 @@ destroy(struct module *mod) m->label->destroy(m->label); free(m->card); free(m->mixer); - free(m->volume_channel); - free(m->muted_channel); + free(m->volume_name); + free(m->muted_name); free(m); module_default_destroy(mod); } @@ -111,7 +111,7 @@ update_state(struct module *mod, snd_mixer_elem_t *elem) if (max > 0) { tll_foreach(m->channels, it) { const char *name = snd_mixer_selem_channel_name(it->item); - if (m->volume_channel != NULL && strcmp(name, m->volume_channel) != 0) + if (m->volume_name != NULL && strcmp(name, m->volume_name) != 0) continue; int r = snd_mixer_selem_get_playback_volume(elem, it->item, &cur); @@ -130,7 +130,7 @@ update_state(struct module *mod, snd_mixer_elem_t *elem) /* Get muted state */ tll_foreach(m->channels, it) { const char *name = snd_mixer_selem_channel_name(it->item); - if (m->muted_channel != NULL && strcmp(name, m->muted_channel) != 0) + if (m->muted_name != NULL && strcmp(name, m->muted_name) != 0) continue; int r = snd_mixer_selem_get_playback_switch(elem, it->item, &unmuted); @@ -237,26 +237,26 @@ run_while_online(struct module *mod) LOG_INFO("%s,%s: channels: %s", m->card, m->mixer, channels_str); /* Verify volume/muted channel names are valid and exists */ - bool volume_channel_is_valid = m->volume_channel == NULL; - bool muted_channel_is_valid = m->muted_channel == NULL; + bool volume_channel_is_valid = m->volume_name == NULL; + bool muted_channel_is_valid = m->muted_name == NULL; tll_foreach(m->channels, it) { const char *chan_name = snd_mixer_selem_channel_name(it->item); - if (m->volume_channel != NULL && strcmp(chan_name, m->volume_channel) == 0) + if (m->volume_name != NULL && strcmp(chan_name, m->volume_name) == 0) volume_channel_is_valid = true; - if (m->muted_channel != NULL && strcmp(chan_name, m->muted_channel) == 0) + if (m->muted_name != NULL && strcmp(chan_name, m->muted_name) == 0) muted_channel_is_valid = true; } if (!volume_channel_is_valid) { - assert(m->volume_channel != NULL); - LOG_ERR("volume: invalid channel name: %s", m->volume_channel); + assert(m->volume_name != NULL); + LOG_ERR("volume: invalid channel name: %s", m->volume_name); goto err; } if (!muted_channel_is_valid) { - assert(m->muted_channel != NULL); - LOG_ERR("muted: invalid channel name: %s", m->muted_channel); + assert(m->muted_name != NULL); + LOG_ERR("muted: invalid channel name: %s", m->muted_name); goto err; } @@ -446,15 +446,17 @@ out: static struct module * alsa_new(const char *card, const char *mixer, - const char *volume_channel, const char *muted_channel, + const char *volume_channel_name, const char *muted_channel_name, struct particle *label) { struct private *priv = calloc(1, sizeof(*priv)); priv->label = label; priv->card = strdup(card); priv->mixer = strdup(mixer); - priv->volume_channel = volume_channel != NULL ? strdup(volume_channel) : NULL; - priv->muted_channel = muted_channel != NULL ? strdup(muted_channel) : NULL; + priv->volume_name = + volume_channel_name != NULL ? strdup(volume_channel_name) : NULL; + priv->muted_name = + muted_channel_name != NULL ? strdup(muted_channel_name) : NULL; struct module *mod = module_common_new(); mod->private = priv; From d1c7647b038900535930012b93d12fae73c662ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 26 Aug 2021 10:55:10 +0200 Subject: [PATCH 229/611] module/alsa: add support for capture devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This mostly comes down to tracking whether each channel is a playback, or capture channel, and using the appropriate APIs when dealing with it. Some cleanup related to this: * Add a channel struct, for per-channel data. Previously, our channel list was just a list of ALSA channel IDs. * We now store current volume per-channel (but volume min/max is per-device) * Muted state is stored per-channel * Track both the device’s playback and capture volume ranges, as well as whether the device *has* playback or capture volume. * Get the playback/capture volume ranges once, during init, instead of at each update. * Use struct pointers for the volume/muted channels. This way we don’t have to iterate all channels and to string comparisons on the name each time we update our state. --- CHANGELOG.md | 1 + modules/alsa.c | 284 +++++++++++++++++++++++++++++++++---------------- 2 files changed, 195 insertions(+), 90 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 08a7ddb..501716a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ channels to use as source for the volume level and muted state. * foreign-toplevel: Wayland module that provides information about currently opened windows. +* alsa: support for capture devices. ### Changed diff --git a/modules/alsa.c b/modules/alsa.c index deee3de..f22b340 100644 --- a/modules/alsa.c +++ b/modules/alsa.c @@ -16,6 +16,17 @@ #include "../config.h" #include "../plugin.h" +enum channel_type { CHANNEL_PLAYBACK, CHANNEL_CAPTURE }; + +struct channel { + snd_mixer_selem_channel_id_t id; + enum channel_type type; + char *name; + + long vol_cur; + bool muted; +}; + struct private { char *card; char *mixer; @@ -23,20 +34,36 @@ struct private { char *muted_name; struct particle *label; - tll(snd_mixer_selem_channel_id_t) channels; + tll(struct channel) channels; bool online; - long vol_min; - long vol_max; - long vol_cur; - bool muted; + + bool has_playback_volume; + long playback_vol_min; + long playback_vol_max; + + bool has_capture_volume; + long capture_vol_min; + long capture_vol_max; + + const struct channel *volume_chan; + const struct channel *muted_chan; }; +static void +channel_free(struct channel *chan) +{ + free(chan->name); +} + static void destroy(struct module *mod) { struct private *m = mod->private; - tll_free(m->channels); + tll_foreach(m->channels, it) { + channel_free(&it->item); + tll_remove(m->channels, it); + } m->label->destroy(m->label); free(m->card); free(m->mixer); @@ -60,17 +87,35 @@ content(struct module *mod) { struct private *m = mod->private; - int percent = m->vol_max - m->vol_min > 0 - ? round(100. * m->vol_cur / (m->vol_max - m->vol_min)) + mtx_lock(&mod->lock); + + const struct channel *volume_chan = m->volume_chan; + const struct channel *muted_chan = m->muted_chan; + + bool muted = muted_chan != NULL ? muted_chan->muted : false; + long vol_min = 0, vol_max = 0, vol_cur = 0; + + if (volume_chan != NULL) { + if (volume_chan->type == CHANNEL_PLAYBACK) { + vol_min = m->playback_vol_min; + vol_max = m->playback_vol_max; + } else { + vol_min = m->capture_vol_min; + vol_max = m->capture_vol_max; + } + vol_cur = volume_chan->vol_cur; + } + + int percent = vol_max - vol_min > 0 + ? round(100. * vol_cur / (vol_max - vol_min)) : 0; - mtx_lock(&mod->lock); struct tag_set tags = { .tags = (struct tag *[]){ tag_new_bool(mod, "online", m->online), - tag_new_int_range(mod, "volume", m->vol_cur, m->vol_min, m->vol_max), + tag_new_int_range(mod, "volume", vol_cur, vol_min, vol_max), tag_new_int_range(mod, "percent", percent, 0, 100), - tag_new_bool(mod, "muted", m->muted), + tag_new_bool(mod, "muted", muted), }, .count = 4, }; @@ -87,89 +132,75 @@ update_state(struct module *mod, snd_mixer_elem_t *elem) { struct private *m = mod->private; - /* Get min/max volume levels */ - long min = 0, max = 0; - int r = snd_mixer_selem_get_playback_volume_range(elem, &min, &max); - - if (r < 0) { - LOG_DBG("%s,%s: failed to get volume min/max (mixer is digital?)", - m->card, m->mixer); - } - - /* Make sure min <= max */ - if (min > max) { - LOG_WARN( - "%s,%s: indicated minimum volume is greater than the maximum: " - "%ld > %ld", m->card, m->mixer, min, max); - min = max; - } - - long cur = 0; - /* If volume level can be changed (i.e. this isn't just a switch; - * e.g. a digital channel), get current level */ - if (max > 0) { - tll_foreach(m->channels, it) { - const char *name = snd_mixer_selem_channel_name(it->item); - if (m->volume_name != NULL && strcmp(name, m->volume_name) != 0) - continue; - - int r = snd_mixer_selem_get_playback_volume(elem, it->item, &cur); - - if (r < 0) { - LOG_WARN("%s,%s: %s: failed to get current volume", - m->card, m->mixer, name); - } - - LOG_DBG("%s,%s: %s: volume: %ld", m->card, m->mixer, name, cur); - } - } - - int unmuted = 0; - - /* Get muted state */ + * e.g. a digital channel), get current channel levels */ tll_foreach(m->channels, it) { - const char *name = snd_mixer_selem_channel_name(it->item); - if (m->muted_name != NULL && strcmp(name, m->muted_name) != 0) + struct channel *chan = &it->item; + + const bool has_volume = chan->type == CHANNEL_PLAYBACK + ? m->has_playback_volume : m->has_capture_volume; + const long min = chan->type == CHANNEL_PLAYBACK + ? m->playback_vol_min : m->capture_vol_min; + const long max = chan->type == CHANNEL_PLAYBACK + ? m->playback_vol_max : m->capture_vol_max; + + if (!has_volume) continue; - int r = snd_mixer_selem_get_playback_switch(elem, it->item, &unmuted); + assert(min <= max); + + int r = chan->type == CHANNEL_PLAYBACK + ? snd_mixer_selem_get_playback_volume(elem, chan->id, &chan->vol_cur) + : snd_mixer_selem_get_capture_volume(elem, chan->id, &chan->vol_cur); + + if (r < 0) { + LOG_ERR("%s,%s: %s: failed to get current volume", + m->card, m->mixer, chan->name); + } + + if (chan->vol_cur < min) { + LOG_WARN( + "%s,%s: %s: current volume is less than the indicated minimum: " + "%ld < %ld", m->card, m->mixer, chan->name, chan->vol_cur, min); + chan->vol_cur = min; + } + + if (chan->vol_cur > max) { + LOG_WARN( + "%s,%s: %s: current volume is greater than the indicated maximum: " + "%ld > %ld", m->card, m->mixer, chan->name, chan->vol_cur, max); + chan->vol_cur = max; + } + + assert(chan->vol_cur >= min); + assert(chan->vol_cur <= max ); + + LOG_DBG("%s,%s: %s: volume: %ld", + m->card, m->mixer, chan->name, chan->vol_cur); + } + + /* Get channels’ muted state */ + tll_foreach(m->channels, it) { + struct channel *chan = &it->item; + + int unmuted; + + int r = chan->type == CHANNEL_PLAYBACK + ? snd_mixer_selem_get_playback_switch(elem, chan->id, &unmuted) + : snd_mixer_selem_get_capture_switch(elem, chan->id, &unmuted); if (r < 0) { LOG_WARN("%s,%s: %s: failed to get muted state", - m->card, m->mixer, name); + m->card, m->mixer, chan->name); unmuted = 1; } - LOG_DBG("%s,%s: %s: muted: %d", m->card, m->mixer, name, !unmuted); + chan->muted = !unmuted; + LOG_DBG("%s,%s: %s: muted: %d", m->card, m->mixer, chan->name, !unmuted); } - /* Make sure min <= cur <= max */ - if (cur < min) { - LOG_WARN( - "%s,%s: current volume is less than the indicated minimum: " - "%ld < %ld", m->card, m->mixer, cur, min); - cur = min; - } - - if (cur > max) { - LOG_WARN( - "%s,%s: current volume is greater than the indicated maximum: " - "%ld > %ld", m->card, m->mixer, cur, max); - cur = max; - } - - assert(cur >= min); - assert(cur <= max); - - LOG_DBG("muted=%d, cur=%ld, min=%ld, max=%ld", !unmuted, cur, min, max); - mtx_lock(&mod->lock); - m->vol_min = min; - m->vol_max = max; - m->vol_cur = cur; m->online = true; - m->muted = !unmuted; mtx_unlock(&mod->lock); mod->bar->refresh(mod->bar); @@ -217,20 +248,77 @@ run_while_online(struct module *mod) goto err; } + /* Get playback volume range */ + m->has_playback_volume = snd_mixer_selem_has_playback_volume(elem) > 0; + if (m->has_playback_volume) { + if (snd_mixer_selem_get_playback_volume_range( + elem, &m->playback_vol_min, &m->playback_vol_max) < 0) + { + LOG_ERR("%s,%s: failed to get playback volume range", + m->card, m->mixer); + assert(m->playback_vol_min == 0); + assert(m->playback_vol_max == 0); + } + + if (m->playback_vol_min > m->playback_vol_max) { + LOG_WARN( + "%s,%s: indicated minimum playback volume is greater than the " + "maximum: %ld > %ld", + m->card, m->mixer, m->playback_vol_min, m->playback_vol_max); + m->playback_vol_min = m->playback_vol_max; + } + } + + /* Get capture volume range */ + m->has_capture_volume = snd_mixer_selem_has_capture_volume(elem) > 0; + if (m->has_capture_volume) { + if (snd_mixer_selem_get_capture_volume_range( + elem, &m->capture_vol_min, &m->capture_vol_max) < 0) + { + LOG_ERR("%s,%s: failed to get capture volume range", + m->card, m->mixer); + assert(m->capture_vol_min == 0); + assert(m->capture_vol_max == 0); + } + + if (m->capture_vol_min > m->capture_vol_max) { + LOG_WARN( + "%s,%s: indicated minimum capture volume is greater than the " + "maximum: %ld > %ld", + m->card, m->mixer, m->capture_vol_min, m->capture_vol_max); + m->capture_vol_min = m->capture_vol_max; + } + } + /* Get available channels */ for (size_t i = 0; i < SND_MIXER_SCHN_LAST; i++) { - if (snd_mixer_selem_has_playback_channel(elem, i)) { - tll_push_back(m->channels, i); + bool is_playback = snd_mixer_selem_has_playback_channel(elem, i) == 1; + bool is_capture = snd_mixer_selem_has_capture_channel(elem, i) == 1; + + if (is_playback || is_capture) { + struct channel chan = { + .id = i, + .type = is_playback ? CHANNEL_PLAYBACK : CHANNEL_CAPTURE, + .name = strdup(snd_mixer_selem_channel_name( i)), + }; + tll_push_back(m->channels, chan); } } + if (tll_length(m->channels) == 0) { + LOG_ERR("%s,%s: no channels", m->card, m->mixer); + goto err; + } + char channels_str[1024]; int channels_idx = 0; tll_foreach(m->channels, it) { + const struct channel *chan = &it->item; + channels_idx += snprintf( &channels_str[channels_idx], sizeof(channels_str) - channels_idx, - channels_idx == 0 ? "%s" : ", %s", - snd_mixer_selem_channel_name(it->item)); + channels_idx == 0 ? "%s (%s)" : ", %s (%s)", + chan->name, chan->type == CHANNEL_PLAYBACK ? "🔊" : "🎤"); assert(channels_idx <= sizeof(channels_str)); } @@ -241,13 +329,22 @@ run_while_online(struct module *mod) bool muted_channel_is_valid = m->muted_name == NULL; tll_foreach(m->channels, it) { - const char *chan_name = snd_mixer_selem_channel_name(it->item); - if (m->volume_name != NULL && strcmp(chan_name, m->volume_name) == 0) + const struct channel *chan = &it->item; + if (m->volume_name != NULL && strcmp(chan->name, m->volume_name) == 0) { + m->volume_chan = chan; volume_channel_is_valid = true; - if (m->muted_name != NULL && strcmp(chan_name, m->muted_name) == 0) + } + if (m->muted_name != NULL && strcmp(chan->name, m->muted_name) == 0) { + m->muted_chan = chan; muted_channel_is_valid = true; + } } + if (m->volume_name == NULL) + m->volume_chan = &tll_front(m->channels); + if (m->muted_name == NULL) + m->muted_chan = &tll_front(m->channels); + if (!volume_channel_is_valid) { assert(m->volume_name != NULL); LOG_ERR("volume: invalid channel name: %s", m->volume_name); @@ -263,9 +360,16 @@ run_while_online(struct module *mod) /* Initial state */ update_state(mod, elem); - LOG_INFO("%s,%s: volume min=%ld, max=%ld, current=%ld%s", - m->card, m->mixer, m->vol_min, m->vol_max, m->vol_cur, - m->muted ? ", muted" : ""); + LOG_INFO( + "%s,%s: volume range=%ld-%ld, current=%ld%s (sources: volume=%s, muted=%s)", + m->card, m->mixer, + m->volume_chan->type == CHANNEL_PLAYBACK + ? m->playback_vol_min : m->capture_vol_min, + m->volume_chan->type == CHANNEL_PLAYBACK + ? m->playback_vol_max : m->capture_vol_max, + m->volume_chan->vol_cur, + m->muted_chan->muted ? " (muted)" : "", + m->volume_chan->name, m->muted_chan->name); mod->bar->refresh(mod->bar); From 1079bca3eb3a0943f116078be884c0d8d3188b08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 26 Aug 2021 13:27:29 +0200 Subject: [PATCH 230/611] =?UTF-8?q?Silence=20=E2=80=9Cvariable=20length=20?= =?UTF-8?q?array=20bound=20evaluates=20to=20non-positive=20value=200?= =?UTF-8?q?=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bar/bar.c | 8 +++++--- modules/removables.c | 6 ++++-- modules/sway-xkb.c | 5 ++++- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/bar/bar.c b/bar/bar.c index b29936b..2399368 100644 --- a/bar/bar.c +++ b/bar/bar.c @@ -25,6 +25,8 @@ #include "wayland.h" #endif +#define max(x, y) ((x) > (y) ? (x) : (y)) + /* * Calculate total width of left/center/rigth groups. * Note: begin_expose() must have been called @@ -293,9 +295,9 @@ run(struct bar *_bar) set_cursor(_bar, "left_ptr"); /* Start modules */ - thrd_t thrd_left[bar->left.count]; - thrd_t thrd_center[bar->center.count]; - thrd_t thrd_right[bar->right.count]; + thrd_t thrd_left[max(bar->left.count, 1)]; + thrd_t thrd_center[max(bar->center.count, 1)]; + thrd_t thrd_right[max(bar->right.count, 1)]; for (size_t i = 0; i < bar->left.count; i++) { struct module *mod = bar->left.mods[i]; diff --git a/modules/removables.c b/modules/removables.c index 4446f3d..a676ceb 100644 --- a/modules/removables.c +++ b/modules/removables.c @@ -23,6 +23,8 @@ #include "../particles/dynlist.h" #include "../plugin.h" +#define max(x, y) ((x) > (y) ? (x) : (y)) + typedef tll(char *) mount_point_list_t; struct partition { @@ -116,7 +118,7 @@ content(struct module *mod) } } - struct exposable *exposables[tll_length(partitions)]; + struct exposable *exposables[max(tll_length(partitions), 1)]; size_t idx = 0; tll_foreach(partitions, it) { @@ -621,7 +623,7 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) right_spacing != NULL ? yml_value_as_int(right_spacing) : 0; size_t ignore_count = ignore_list != NULL ? yml_list_length(ignore_list) : 0; - const char *ignore[ignore_count]; + const char *ignore[max(ignore_count, 1)]; if (ignore_list != NULL) { size_t i = 0; diff --git a/modules/sway-xkb.c b/modules/sway-xkb.c index 295b976..f4e7577 100644 --- a/modules/sway-xkb.c +++ b/modules/sway-xkb.c @@ -13,6 +13,8 @@ #include "i3-ipc.h" #include "i3-common.h" +#define max(x, y) ((x) > (y) ? (x) : (y)) + struct input { bool exists; char *identifier; @@ -65,7 +67,8 @@ content(struct module *mod) mtx_lock(&mod->lock); - struct exposable *particles[m->num_existing_inputs]; + struct exposable *particles[max(m->num_existing_inputs, 1)]; + for (size_t i = 0, j = 0; i < m->num_inputs; i++) { const struct input *input = &m->inputs[i]; From e9d762fa0323d6d6f72a1560517ae9fd7d3862c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 27 Aug 2021 10:14:54 +0200 Subject: [PATCH 231/611] module/foreign-toplevel: disable debug logging --- modules/foreign-toplevel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/foreign-toplevel.c b/modules/foreign-toplevel.c index e71e825..ef45b3b 100644 --- a/modules/foreign-toplevel.c +++ b/modules/foreign-toplevel.c @@ -9,7 +9,7 @@ #include #define LOG_MODULE "foreign-toplevel" -#define LOG_ENABLE_DBG 1 +#define LOG_ENABLE_DBG 0 #include "../log.h" #include "../plugin.h" #include "../particles/dynlist.h" From e4e95873220a023c6e30a18cad5871e754938eef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 30 Aug 2021 18:00:41 +0200 Subject: [PATCH 232/611] particle/progress-bar: fix regression in width calculation: = -> += --- particles/progress-bar.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/particles/progress-bar.c b/particles/progress-bar.c index 7011feb..ebc51c3 100644 --- a/particles/progress-bar.c +++ b/particles/progress-bar.c @@ -74,7 +74,7 @@ begin_expose(struct exposable *exposable) /* Margins */ if (have_at_least_one) { - exposable->width = exposable->particle->left_margin + + exposable->width += exposable->particle->left_margin + exposable->particle->right_margin; } else assert(exposable->width == 0); From d39e6b8b9469d215c4565824fa8525e1076619a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 26 Aug 2021 18:52:30 +0200 Subject: [PATCH 233/611] module/network: initial support for Wifi extensions Currently capable of getting the SSID. --- modules/network.c | 404 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 348 insertions(+), 56 deletions(-) diff --git a/modules/network.c b/modules/network.c index b9277d7..784546b 100644 --- a/modules/network.c +++ b/modules/network.c @@ -2,8 +2,10 @@ #include #include #include +#include #include #include +#include #include #include @@ -11,7 +13,9 @@ #include #include #include +#include #include +#include #include @@ -24,6 +28,8 @@ #include "../module.h" #include "../plugin.h" +#define UNUSED __attribute__((unused)) + struct af_addr { int family; union { @@ -36,7 +42,13 @@ struct private { char *iface; struct particle *label; - int nl_sock; + int genl_sock; + int rt_sock; + + struct { + uint16_t family_id; + uint32_t seq_nr; + } nl80211; bool get_addresses; @@ -47,6 +59,9 @@ struct private { /* IPv4 and IPv6 addresses */ tll(struct af_addr) addrs; + + /* WiFi extensions */ + char *ssid; }; static void @@ -54,12 +69,12 @@ destroy(struct module *mod) { struct private *m = mod->private; - assert(m->nl_sock == -1); + assert(m->rt_sock == -1); m->label->destroy(m->label); tll_free(m->addrs); - + free(m->ssid); free(m->iface); free(m); @@ -120,8 +135,9 @@ content(struct module *mod) tag_new_string(mod, "mac", mac_str), tag_new_string(mod, "ipv4", ipv4_str), tag_new_string(mod, "ipv6", ipv6_str), + tag_new_string(mod, "ssid", m->ssid), }, - .count = 7, + .count = 8, }; mtx_unlock(&mod->lock); @@ -140,35 +156,48 @@ nl_pid_value(void) /* Connect and bind to netlink socket. Returns socket fd, or -1 on error */ static int -netlink_connect(void) +netlink_connect(int protocol, bool bind_socket) { - int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + int sock = socket(AF_NETLINK, SOCK_RAW, protocol); if (sock == -1) { LOG_ERRNO("failed to create netlink socket"); return -1; } - const struct sockaddr_nl addr = { - .nl_family = AF_NETLINK, - .nl_pid = nl_pid_value(), - .nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR, - }; + if (bind_socket) { + const struct sockaddr_nl addr = { + .nl_family = AF_NETLINK, + .nl_pid = nl_pid_value(), + .nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR, + }; - if (bind(sock, (const struct sockaddr *)&addr, sizeof(addr)) == -1) { - LOG_ERRNO("failed to bind netlink socket"); - close(sock); - return -1; + if (bind(sock, (const struct sockaddr *)&addr, sizeof(addr)) == -1) { + LOG_ERRNO("failed to bind netlink socket"); + close(sock); + return -1; + } } return sock; } static bool -send_rt_request(int nl_sock, int request) +send_nlmsg(int sock, const void *nlmsg, size_t len) +{ + int r = sendto( + sock, nlmsg, len, 0, + (struct sockaddr *)&(struct sockaddr_nl){.nl_family = AF_NETLINK}, + sizeof(struct sockaddr_nl)); + + return r == len; +} + +static bool +send_rt_request(struct private *m, int request) { struct { struct nlmsghdr hdr; - struct rtgenmsg rt; + struct rtgenmsg rt __attribute__((aligned(NLMSG_ALIGNTO))); } req = { .hdr = { .nlmsg_len = NLMSG_LENGTH(sizeof(req.rt)), @@ -183,18 +212,126 @@ send_rt_request(int nl_sock, int request) }, }; - int r = sendto( - nl_sock, &req, req.hdr.nlmsg_len, 0, - (struct sockaddr *)&(struct sockaddr_nl){.nl_family = AF_NETLINK}, - sizeof(struct sockaddr_nl)); - - if (r == -1) { - LOG_ERRNO("failed to send netlink request"); + if (!send_nlmsg(m->rt_sock, &req, req.hdr.nlmsg_len)) { + LOG_ERRNO("%s: failed to send netlink RT request (%d)", + m->iface, request); return false; } return true; } + +static bool +send_ctrl_get_family_request(struct private *m) +{ + const struct { + struct nlmsghdr hdr; + struct { + struct genlmsghdr genl; + struct { + struct nlattr hdr; + char data[8] __attribute__((aligned(NLA_ALIGNTO))); + } family_name_attr __attribute__((aligned(NLA_ALIGNTO))); + } msg __attribute__((aligned(NLMSG_ALIGNTO))); + } req = { + .hdr = { + .nlmsg_len = NLMSG_LENGTH(sizeof(req.msg)), + .nlmsg_type = GENL_ID_CTRL, + .nlmsg_flags = NLM_F_REQUEST, + .nlmsg_seq = 1, + .nlmsg_pid = nl_pid_value(), + }, + + .msg = { + .genl = { + .cmd = CTRL_CMD_GETFAMILY, + .version = 1, + }, + + .family_name_attr = { + .hdr = { + .nla_type = CTRL_ATTR_FAMILY_NAME, + .nla_len = sizeof(req.msg.family_name_attr), + }, + + .data = NL80211_GENL_NAME, + }, + }, + }; + + _Static_assert( + sizeof(req.msg.family_name_attr) == + NLA_HDRLEN + NLA_ALIGN(sizeof(req.msg.family_name_attr.data)), + ""); + + if (!send_nlmsg(m->genl_sock, &req, req.hdr.nlmsg_len)) { + LOG_ERRNO("%s: failed to send netlink ctrl-get-family request", + m->iface); + return false; + } + + return true; +} + +static bool +send_nl80211_get_interface_request(struct private *m) +{ + assert(m->ifindex >= 0); + + if (m->nl80211.seq_nr > 0) { + LOG_DBG( + "%s: nl80211 get-interface request already in progress", m->iface); + return true; + } + + m->nl80211.seq_nr = time(NULL); + LOG_DBG("%s: sending nl80211 get-interface request", m->iface); + + const struct { + struct nlmsghdr hdr; + struct { + struct genlmsghdr genl; + struct { + struct nlattr attr; + int index __attribute__((aligned(NLA_ALIGNTO))); + } ifindex __attribute__((aligned(NLA_ALIGNTO))); + } msg __attribute__((aligned(NLMSG_ALIGNTO))); + } req = { + .hdr = { + .nlmsg_len = NLMSG_LENGTH(sizeof(req.msg)), + .nlmsg_type = m->nl80211.family_id, + .nlmsg_flags = NLM_F_REQUEST, + .nlmsg_seq = m->nl80211.seq_nr, + .nlmsg_pid = nl_pid_value(), + }, + + .msg = { + .genl = { + .cmd = NL80211_CMD_GET_INTERFACE, + .version = 1, + }, + + .ifindex = { + .attr = { + .nla_type = NL80211_ATTR_IFINDEX, + .nla_len = sizeof(req.msg.ifindex), + }, + + .index = m->ifindex, + }, + }, + }; + + if (!send_nlmsg(m->genl_sock, &req, req.hdr.nlmsg_len)) { + LOG_ERRNO("%s: failed to send netlink nl80211 get-inteface request", + m->iface); + m->nl80211.seq_nr = 0; + return false; + } + + return true; +} + static bool find_my_ifindex(struct module *mod, const struct ifinfomsg *msg, size_t len) { @@ -212,6 +349,9 @@ find_my_ifindex(struct module *mod, const struct ifinfomsg *msg, size_t len) mtx_lock(&mod->lock); m->ifindex = msg->ifi_index; mtx_unlock(&mod->lock); + + if (m->nl80211.family_id >= 0) + send_nl80211_get_interface_request(m); return true; } @@ -261,9 +401,18 @@ handle_link(struct module *mod, uint16_t type, LOG_DBG("%s: IFLA_OPERSTATE: %hhu -> %hhu", m->iface, m->state, operstate); mtx_lock(&mod->lock); - m->state = operstate; + { + m->state = operstate; + if (operstate != IF_OPER_UP) { + free(m->ssid); + m->ssid = NULL; + } + } mtx_unlock(&mod->lock); update_bar = true; + + if (operstate == IF_OPER_UP) + send_nl80211_get_interface_request(m); break; } @@ -411,7 +560,7 @@ netlink_receive_messages(int sock, void **reply, size_t *len) } static bool -parse_reply(struct module *mod, const struct nlmsghdr *hdr, size_t len) +parse_rt_reply(struct module *mod, const struct nlmsghdr *hdr, size_t len) { struct private *m = mod->private; @@ -427,7 +576,7 @@ parse_reply(struct module *mod, const struct nlmsghdr *hdr, size_t len) /* Request initial list of IPv4/6 addresses */ if (m->get_addresses && m->ifindex != -1) { m->get_addresses = false; - send_rt_request(m->nl_sock, RTM_GETADDR); + send_rt_request(m, RTM_GETADDR); } break; @@ -451,14 +600,128 @@ parse_reply(struct module *mod, const struct nlmsghdr *hdr, size_t len) case NLMSG_ERROR:{ const struct nlmsgerr *err = NLMSG_DATA(hdr); - LOG_ERRNO_P(err->error, "netlink"); + LOG_ERRNO_P(-err->error, "%s: netlink RT reply", m->iface); return false; } default: LOG_WARN( - "unrecognized netlink message type: 0x%x", hdr->nlmsg_type); - break; + "%s: unrecognized netlink message type: 0x%x", + m->iface, hdr->nlmsg_type); + return false; + } + } + + return true; +} + +static bool +parse_genl_reply(struct module *mod, const struct nlmsghdr *hdr, size_t len) +{ + struct private *m = mod->private; + + /* Process response */ + for (; NLMSG_OK(hdr, len); hdr = NLMSG_NEXT(hdr, len)) { + if (hdr->nlmsg_seq == m->nl80211.seq_nr) { + /* Current request is now considered complete */ + m->nl80211.seq_nr = 0; + } + + if (hdr->nlmsg_type == GENL_ID_CTRL) { + const struct genlmsghdr *genl = NLMSG_DATA(hdr); + const size_t attr_size = NLMSG_PAYLOAD(hdr, 0); + + const uint8_t *p = (const uint8_t *)(genl + 1); + const uint8_t *end = (const uint8_t *)genl + attr_size; + const struct nlattr *attr = (const struct nlattr *)p; + + for (; p < end; + p += NLA_ALIGN(attr->nla_len), + attr = (const struct nlattr *)p) + { + bool nested UNUSED = attr->nla_type & NLA_F_NESTED; + uint16_t type = attr->nla_type & NLA_TYPE_MASK; + + switch (type) { + case CTRL_ATTR_FAMILY_ID: { + m->nl80211.family_id = *(const uint16_t *)(attr + 1); + if (m->ifindex >= 0) + send_nl80211_get_interface_request(m); + break; + } + + case CTRL_ATTR_FAMILY_NAME: { +#if 0 + unsigned name_len = attr->nla_len - NLA_HDRLEN; + const char *name = (const char *)(attr + 1); + LOG_INFO("NAME: %.*s (%u bytes)", name_len, name, name_len); +#endif + break; + } + + default: + LOG_DBG("%s: unrecognized GENL CTRL attribute: " + "%hu%s (size: %hu bytes)", m->iface, + type, nested ? " (nested)" : "", attr->nla_len); + break; + } + } + } + + else if (hdr->nlmsg_type == m->nl80211.family_id) { + const struct genlmsghdr *genl = NLMSG_DATA(hdr); + const size_t attr_size = NLMSG_PAYLOAD(hdr, 0); + + const uint8_t *p = (const uint8_t *)(genl + 1); + const uint8_t *end = (const uint8_t *)genl + attr_size; + const struct nlattr *attr = (const struct nlattr *)p; + + for (;p < end && attr->nla_len > 0; + p += NLA_ALIGN(attr->nla_len), + attr = (const struct nlattr *)p) + { + bool nested UNUSED = attr->nla_type & NLA_F_NESTED; + uint16_t type = attr->nla_type & NLA_TYPE_MASK; + + switch (type) { + case NL80211_ATTR_SSID: { + unsigned ssid_len = attr->nla_len - NLA_HDRLEN; + const char *ssid = (const char *)(attr + 1); + LOG_INFO("%s: SSID: %.*s", m->iface, ssid_len, ssid); + + mtx_lock(&mod->lock); + free(m->ssid); + m->ssid = strndup(ssid, ssid_len); + mtx_unlock(&mod->lock); + + mod->bar->refresh(mod->bar); + break; + } + + default: + LOG_DBG("%s: unrecognized nl80211 attribute: " + "type=%hu%s, len=%hu", m->iface, + type, nested ? " (nested)" : "", attr->nla_len); + break; + } + } + } + + else if (hdr->nlmsg_type == NLMSG_ERROR) { + const struct nlmsgerr *err = NLMSG_DATA(hdr); + if (-err->error == ENODEV) + ; /* iface is not an nl80211 device */ + else if (-err->error == ENOENT) + ; /* iface down? */ + else + LOG_ERRNO_P(-err->error, "%s: nl80211 reply", m->iface); + } + + else { + LOG_WARN( + "%s: unrecognized netlink message type: 0x%x", + m->iface, hdr->nlmsg_type); + return false; } } @@ -468,55 +731,82 @@ parse_reply(struct module *mod, const struct nlmsghdr *hdr, size_t len) static int run(struct module *mod) { + int ret = 1; struct private *m = mod->private; - m->nl_sock = netlink_connect(); - if (m->nl_sock == -1) - return 1; + m->rt_sock = netlink_connect(NETLINK_ROUTE, true); + m->genl_sock = netlink_connect(NETLINK_GENERIC, false); - if (!send_rt_request(m->nl_sock, RTM_GETLINK)) { - close(m->nl_sock); - m->nl_sock = -1; - return 1; + if (m->rt_sock < 0 || m->genl_sock < 0) + goto out; + + if (!send_rt_request(m, RTM_GETLINK) || + !send_ctrl_get_family_request(m)) + { + goto out; } /* Main loop */ while (true) { struct pollfd fds[] = { {.fd = mod->abort_fd, .events = POLLIN}, - {.fd = m->nl_sock, .events = POLLIN} + {.fd = m->rt_sock, .events = POLLIN}, + {.fd = m->genl_sock, .events = POLLIN}, }; - poll(fds, 2, -1); + poll(fds, 3, -1); - if (fds[0].revents & POLLIN) + if (fds[0].revents & (POLLIN | POLLHUP)) break; - if (fds[1].revents & POLLHUP) { - LOG_ERR("disconnected from netlink socket"); + if ((fds[1].revents & POLLHUP) || + (fds[2].revents & POLLHUP)) + { + LOG_ERR("%s: disconnected from netlink socket", m->iface); break; } - assert(fds[1].revents & POLLIN); + if (fds[1].revents & POLLIN) { + /* Read one (or more) messages */ + void *reply; + size_t len; + if (!netlink_receive_messages(m->rt_sock, &reply, &len)) + break; - /* Read one (or more) messages */ - void *reply; - size_t len; - if (!netlink_receive_messages(m->nl_sock, &reply, &len)) - break; + /* Parse (and act upon) the received message(s) */ + if (!parse_rt_reply(mod, (const struct nlmsghdr *)reply, len)) { + free(reply); + break; + } - /* Parse (and act upon) the received message(s) */ - if (!parse_reply(mod, (const struct nlmsghdr *)reply, len)) { free(reply); - break; } - free(reply); + if (fds[2].revents & POLLIN) { + /* Read one (or more) messages */ + void *reply; + size_t len; + if (!netlink_receive_messages(m->genl_sock, &reply, &len)) + break; + + if (!parse_genl_reply(mod, (const struct nlmsghdr *)reply, len)) { + free(reply); + break; + } + + free(reply); + } } - close(m->nl_sock); - m->nl_sock = -1; - return 0; + ret = 0; + + out: + if (m->rt_sock >= 0) + close(m->rt_sock); + if (m->genl_sock >= 0) + close(m->genl_sock); + m->rt_sock = m->genl_sock = -1; + return ret; } static struct module * @@ -526,7 +816,9 @@ network_new(const char *iface, struct particle *label) priv->iface = strdup(iface); priv->label = label; - priv->nl_sock = -1; + priv->genl_sock = -1; + priv->rt_sock = -1; + priv->nl80211.family_id = -1; priv->get_addresses = true; priv->ifindex = -1; priv->state = IF_OPER_DOWN; From 5249d9ef795ba72525603298586d4ee0b7d21179 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 26 Aug 2021 18:58:21 +0200 Subject: [PATCH 234/611] module/network: use separate functions to connect to RT/GENL netlink --- modules/network.c | 53 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 15 deletions(-) diff --git a/modules/network.c b/modules/network.c index 784546b..1986d3c 100644 --- a/modules/network.c +++ b/modules/network.c @@ -156,31 +156,54 @@ nl_pid_value(void) /* Connect and bind to netlink socket. Returns socket fd, or -1 on error */ static int -netlink_connect(int protocol, bool bind_socket) +netlink_connect_rt(void) { - int sock = socket(AF_NETLINK, SOCK_RAW, protocol); + int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (sock == -1) { LOG_ERRNO("failed to create netlink socket"); return -1; } - if (bind_socket) { - const struct sockaddr_nl addr = { - .nl_family = AF_NETLINK, - .nl_pid = nl_pid_value(), - .nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR, - }; + const struct sockaddr_nl addr = { + .nl_family = AF_NETLINK, + .nl_pid = nl_pid_value(), + .nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR, + }; - if (bind(sock, (const struct sockaddr *)&addr, sizeof(addr)) == -1) { - LOG_ERRNO("failed to bind netlink socket"); - close(sock); - return -1; - } + if (bind(sock, (const struct sockaddr *)&addr, sizeof(addr)) == -1) { + LOG_ERRNO("failed to bind netlink socket"); + close(sock); + return -1; } return sock; } +static int +netlink_connect_genl(void) +{ + int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); + if (sock == -1) { + LOG_ERRNO("failed to create netlink socket"); + return -1; + } + +#if 0 + const struct sockaddr_nl addr = { + .nl_family = AF_NETLINK, + .nl_pid = nl_pid_value(), + .nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR, + }; + + if (bind(sock, (const struct sockaddr *)&addr, sizeof(addr)) == -1) { + LOG_ERRNO("failed to bind netlink socket"); + close(sock); + return -1; + } +#endif + return sock; +} + static bool send_nlmsg(int sock, const void *nlmsg, size_t len) { @@ -734,8 +757,8 @@ run(struct module *mod) int ret = 1; struct private *m = mod->private; - m->rt_sock = netlink_connect(NETLINK_ROUTE, true); - m->genl_sock = netlink_connect(NETLINK_GENERIC, false); + m->rt_sock = netlink_connect_rt(); + m->genl_sock = netlink_connect_genl(); if (m->rt_sock < 0 || m->genl_sock < 0) goto out; From 9b3548736a8803ace32e6cfd48bd077ddf6759eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 27 Aug 2021 07:14:36 +0200 Subject: [PATCH 235/611] module/network: refactor: add foreach_nlattr() This function iterates all attributes of a NETLINK_GENERIC message, and calls the provided callback for each attribute. --- modules/network.c | 158 ++++++++++++++++++++++++---------------------- 1 file changed, 82 insertions(+), 76 deletions(-) diff --git a/modules/network.c b/modules/network.c index 1986d3c..ba00fca 100644 --- a/modules/network.c +++ b/modules/network.c @@ -544,6 +544,79 @@ handle_address(struct module *mod, uint16_t type, mod->bar->refresh(mod->bar); } +static void +foreach_nlattr(struct module *mod, const struct genlmsghdr *genl, size_t len, + void (*cb)(struct module *mod, uint16_t type, bool nested, + const void *payload, size_t len)) +{ + const uint8_t *raw = (const uint8_t *)genl + NLA_HDRLEN; + const uint8_t *end = raw + len; + + for (const struct nlattr *attr = (const struct nlattr *)raw; + raw < end; + raw += NLA_ALIGN(attr->nla_len), attr = (const struct nlattr *)raw) + { + uint16_t type = attr->nla_type & NLA_TYPE_MASK; + bool nested = !!(attr->nla_type & NLA_F_NESTED); + + cb(mod, type, nested, attr + 1, attr->nla_len - NLA_HDRLEN); + } +} + +static void +handle_genl_ctrl(struct module *mod, uint16_t type, bool nested, + const void *payload, size_t len) +{ + struct private *m = mod->private; + + switch (type) { + case CTRL_ATTR_FAMILY_ID: { + m->nl80211.family_id = *(const uint16_t *)payload; + if (m->ifindex >= 0) + send_nl80211_get_interface_request(m); + break; + } + + case CTRL_ATTR_FAMILY_NAME: + //LOG_INFO("NAME: %.*s (%zu bytes)", (int)len, (const char *)payload, len); + break; + + default: + LOG_DBG("%s: unrecognized GENL CTRL attribute: " + "%hu%s (size: %hu bytes)", m->iface, + type, nested ? " (nested)" : "", len); + break; + } +} + +static void +handle_nl80211(struct module *mod, uint16_t type, bool nested, + const void *payload, size_t len) +{ + struct private *m = mod->private; + + switch (type) { + case NL80211_ATTR_SSID: { + const char *ssid = payload; + LOG_INFO("%s: SSID: %.*s", m->iface, (int)len, ssid); + + mtx_lock(&mod->lock); + free(m->ssid); + m->ssid = strndup(ssid, len); + mtx_unlock(&mod->lock); + + mod->bar->refresh(mod->bar); + break; + } + + default: + LOG_DBG("%s: unrecognized nl80211 attribute: " + "type=%hu%s, len=%hu", m->iface, + type, nested ? " (nested)" : "", len); + break; + } +} + /* * Reads at least one (possibly more) message. * @@ -643,7 +716,6 @@ parse_genl_reply(struct module *mod, const struct nlmsghdr *hdr, size_t len) { struct private *m = mod->private; - /* Process response */ for (; NLMSG_OK(hdr, len); hdr = NLMSG_NEXT(hdr, len)) { if (hdr->nlmsg_seq == m->nl80211.seq_nr) { /* Current request is now considered complete */ @@ -652,92 +724,26 @@ parse_genl_reply(struct module *mod, const struct nlmsghdr *hdr, size_t len) if (hdr->nlmsg_type == GENL_ID_CTRL) { const struct genlmsghdr *genl = NLMSG_DATA(hdr); - const size_t attr_size = NLMSG_PAYLOAD(hdr, 0); - - const uint8_t *p = (const uint8_t *)(genl + 1); - const uint8_t *end = (const uint8_t *)genl + attr_size; - const struct nlattr *attr = (const struct nlattr *)p; - - for (; p < end; - p += NLA_ALIGN(attr->nla_len), - attr = (const struct nlattr *)p) - { - bool nested UNUSED = attr->nla_type & NLA_F_NESTED; - uint16_t type = attr->nla_type & NLA_TYPE_MASK; - - switch (type) { - case CTRL_ATTR_FAMILY_ID: { - m->nl80211.family_id = *(const uint16_t *)(attr + 1); - if (m->ifindex >= 0) - send_nl80211_get_interface_request(m); - break; - } - - case CTRL_ATTR_FAMILY_NAME: { -#if 0 - unsigned name_len = attr->nla_len - NLA_HDRLEN; - const char *name = (const char *)(attr + 1); - LOG_INFO("NAME: %.*s (%u bytes)", name_len, name, name_len); -#endif - break; - } - - default: - LOG_DBG("%s: unrecognized GENL CTRL attribute: " - "%hu%s (size: %hu bytes)", m->iface, - type, nested ? " (nested)" : "", attr->nla_len); - break; - } - } + const size_t msg_size = NLMSG_PAYLOAD(hdr, 0); + foreach_nlattr(mod, genl, msg_size, &handle_genl_ctrl); } else if (hdr->nlmsg_type == m->nl80211.family_id) { const struct genlmsghdr *genl = NLMSG_DATA(hdr); - const size_t attr_size = NLMSG_PAYLOAD(hdr, 0); - - const uint8_t *p = (const uint8_t *)(genl + 1); - const uint8_t *end = (const uint8_t *)genl + attr_size; - const struct nlattr *attr = (const struct nlattr *)p; - - for (;p < end && attr->nla_len > 0; - p += NLA_ALIGN(attr->nla_len), - attr = (const struct nlattr *)p) - { - bool nested UNUSED = attr->nla_type & NLA_F_NESTED; - uint16_t type = attr->nla_type & NLA_TYPE_MASK; - - switch (type) { - case NL80211_ATTR_SSID: { - unsigned ssid_len = attr->nla_len - NLA_HDRLEN; - const char *ssid = (const char *)(attr + 1); - LOG_INFO("%s: SSID: %.*s", m->iface, ssid_len, ssid); - - mtx_lock(&mod->lock); - free(m->ssid); - m->ssid = strndup(ssid, ssid_len); - mtx_unlock(&mod->lock); - - mod->bar->refresh(mod->bar); - break; - } - - default: - LOG_DBG("%s: unrecognized nl80211 attribute: " - "type=%hu%s, len=%hu", m->iface, - type, nested ? " (nested)" : "", attr->nla_len); - break; - } - } + const size_t msg_size = NLMSG_PAYLOAD(hdr, 0); + foreach_nlattr(mod, genl, msg_size, &handle_nl80211); } else if (hdr->nlmsg_type == NLMSG_ERROR) { const struct nlmsgerr *err = NLMSG_DATA(hdr); - if (-err->error == ENODEV) + int nl_errno = -err->error; + + if (nl_errno == ENODEV) ; /* iface is not an nl80211 device */ - else if (-err->error == ENOENT) + else if (nl_errno == ENOENT) ; /* iface down? */ else - LOG_ERRNO_P(-err->error, "%s: nl80211 reply", m->iface); + LOG_ERRNO_P(nl_errno, "%s: nl80211 reply", m->iface); } else { From b27eff36f929ec9eb1e9d3b93cdf01d4b912199c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 27 Aug 2021 10:09:48 +0200 Subject: [PATCH 236/611] module/network: nl80211: join the MLME mcast group, ignore messages not for us MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This cleans up the nl80211 handling quite a bit, and adds initial support for nl80211 notifications. * We now join the nl80211 MLME multicast group (done by parsing the CTRL_ATTR_MCAST_GROUPS attribute in the reply to our CTRL_CMD_GETFAMILY request). This gives us CONNECT and DISCONNECT notifications, allowing us to request and reset SSID that way, instead of detecting the link’s OPER state. * Before parsing an nl80211 message, verify it’s for us, by looking for a NL80211_ATTR_IFINDEX attribute in the message (and comparing the contents with our previously detected ifindex). --- modules/network.c | 220 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 185 insertions(+), 35 deletions(-) diff --git a/modules/network.c b/modules/network.c index ba00fca..e4703c2 100644 --- a/modules/network.c +++ b/modules/network.c @@ -170,8 +170,8 @@ netlink_connect_rt(void) .nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR, }; - if (bind(sock, (const struct sockaddr *)&addr, sizeof(addr)) == -1) { - LOG_ERRNO("failed to bind netlink socket"); + if (bind(sock, (const struct sockaddr *)&addr, sizeof(addr)) < 0) { + LOG_ERRNO("failed to bind netlink RT socket"); close(sock); return -1; } @@ -188,19 +188,18 @@ netlink_connect_genl(void) return -1; } -#if 0 const struct sockaddr_nl addr = { .nl_family = AF_NETLINK, .nl_pid = nl_pid_value(), - .nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR, + /* no multicast notifications by default, will be added later */ }; - if (bind(sock, (const struct sockaddr *)&addr, sizeof(addr)) == -1) { + if (bind(sock, (const struct sockaddr *)&addr, sizeof(addr)) < 0) { LOG_ERRNO("failed to bind netlink socket"); close(sock); return -1; } -#endif + return sock; } @@ -299,7 +298,8 @@ send_ctrl_get_family_request(struct private *m) static bool send_nl80211_get_interface_request(struct private *m) { - assert(m->ifindex >= 0); + if (m->ifindex < 0) + return true; if (m->nl80211.seq_nr > 0) { LOG_DBG( @@ -373,8 +373,7 @@ find_my_ifindex(struct module *mod, const struct ifinfomsg *msg, size_t len) m->ifindex = msg->ifi_index; mtx_unlock(&mod->lock); - if (m->nl80211.family_id >= 0) - send_nl80211_get_interface_request(m); + send_nl80211_get_interface_request(m); return true; } @@ -424,18 +423,9 @@ handle_link(struct module *mod, uint16_t type, LOG_DBG("%s: IFLA_OPERSTATE: %hhu -> %hhu", m->iface, m->state, operstate); mtx_lock(&mod->lock); - { - m->state = operstate; - if (operstate != IF_OPER_UP) { - free(m->ssid); - m->ssid = NULL; - } - } + m->state = operstate; mtx_unlock(&mod->lock); update_bar = true; - - if (operstate == IF_OPER_UP) - send_nl80211_get_interface_request(m); break; } @@ -544,26 +534,113 @@ handle_address(struct module *mod, uint16_t type, mod->bar->refresh(mod->bar); } -static void +static bool foreach_nlattr(struct module *mod, const struct genlmsghdr *genl, size_t len, - void (*cb)(struct module *mod, uint16_t type, bool nested, + bool (*cb)(struct module *mod, uint16_t type, bool nested, const void *payload, size_t len)) { - const uint8_t *raw = (const uint8_t *)genl + NLA_HDRLEN; - const uint8_t *end = raw + len; + const uint8_t *raw = (const uint8_t *)genl + GENL_HDRLEN; + const uint8_t *end = (const uint8_t *)genl + len; for (const struct nlattr *attr = (const struct nlattr *)raw; raw < end; raw += NLA_ALIGN(attr->nla_len), attr = (const struct nlattr *)raw) { uint16_t type = attr->nla_type & NLA_TYPE_MASK; - bool nested = !!(attr->nla_type & NLA_F_NESTED); + bool nested = (attr->nla_type & NLA_F_NESTED) != 0;; + const void *payload = raw + NLA_HDRLEN; - cb(mod, type, nested, attr + 1, attr->nla_len - NLA_HDRLEN); + if (!cb(mod, type, nested, payload, attr->nla_len - NLA_HDRLEN)) + return false; } + + return true; } -static void +static bool +foreach_nlattr_nested(struct module *mod, const void *parent_payload, size_t len, + bool (*cb)(struct module *mod, uint16_t type, + bool nested, const void *payload, size_t len, + void *ctx), + void *ctx) +{ + const uint8_t *raw = parent_payload; + const uint8_t *end = parent_payload + len; + + for (const struct nlattr *attr = (const struct nlattr *)raw; + raw < end; + raw += NLA_ALIGN(attr->nla_len), attr = (const struct nlattr *)raw) + { + uint16_t type = attr->nla_type & NLA_TYPE_MASK; + bool nested = (attr->nla_type & NLA_F_NESTED) != 0; + const void *payload = raw + NLA_HDRLEN; + + if (!cb(mod, type, nested, payload, attr->nla_len - NLA_HDRLEN, ctx)) + return false; + } + + return true; +} + +struct mcast_group { + uint32_t id; + char *name; +}; + +static bool +parse_mcast_group(struct module *mod, uint16_t type, bool nested, + const void *payload, size_t len, void *_ctx) +{ + struct private *m = mod->private; + struct mcast_group *ctx = _ctx; + + switch (type) { + case CTRL_ATTR_MCAST_GRP_ID: { + ctx->id = *(uint32_t *)payload; + break; + } + + case CTRL_ATTR_MCAST_GRP_NAME: { + free(ctx->name); + ctx->name = strndup((const char *)payload, len); + break; + } + + default: + LOG_WARN("%s: unrecognized GENL MCAST GRP attribute: " + "%hu%s (size: %zu bytes)", m->iface, + type, nested ? " (nested)" : "", len); + break; + } + + return true; +} + +static bool +parse_mcast_groups(struct module *mod, uint16_t type, bool nested, + const void *payload, size_t len, void *_ctx) +{ + struct private *m = mod->private; + + struct mcast_group group = {0}; + foreach_nlattr_nested(mod, payload, len, &parse_mcast_group, &group); + + LOG_DBG("MCAST: %s -> %u", group.name, group.id); + + if (strcmp(group.name, NL80211_MULTICAST_GROUP_MLME) == 0) { + int r = setsockopt( + m->genl_sock, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, + &group.id, sizeof(int)); + + if (r < 0) + LOG_ERRNO("failed to joint the nl80211 MLME mcast group"); + } + + free(group.name); + return true; +} + +static bool handle_genl_ctrl(struct module *mod, uint16_t type, bool nested, const void *payload, size_t len) { @@ -572,8 +649,7 @@ handle_genl_ctrl(struct module *mod, uint16_t type, bool nested, switch (type) { case CTRL_ATTR_FAMILY_ID: { m->nl80211.family_id = *(const uint16_t *)payload; - if (m->ifindex >= 0) - send_nl80211_get_interface_request(m); + send_nl80211_get_interface_request(m); break; } @@ -581,24 +657,55 @@ handle_genl_ctrl(struct module *mod, uint16_t type, bool nested, //LOG_INFO("NAME: %.*s (%zu bytes)", (int)len, (const char *)payload, len); break; + case CTRL_ATTR_MCAST_GROUPS: + foreach_nlattr_nested(mod, payload, len, &parse_mcast_groups, NULL); + break; + default: LOG_DBG("%s: unrecognized GENL CTRL attribute: " - "%hu%s (size: %hu bytes)", m->iface, + "%hu%s (size: %zu bytes)", m->iface, type, nested ? " (nested)" : "", len); break; } + + return true; } -static void -handle_nl80211(struct module *mod, uint16_t type, bool nested, - const void *payload, size_t len) +static bool +check_for_nl80211_ifindex(struct module *mod, uint16_t type, bool nested, + const void *payload, size_t len) { struct private *m = mod->private; switch (type) { + case NL80211_ATTR_IFINDEX: + return *(uint32_t *)payload == m->ifindex; + } + + return true; +} + +static bool +nl80211_is_for_us(struct module *mod, const struct genlmsghdr *genl, + size_t msg_size) +{ + return foreach_nlattr(mod, genl, msg_size, &check_for_nl80211_ifindex); +} + +static bool +handle_nl80211_new_interface(struct module *mod, uint16_t type, bool nested, + const void *payload, size_t len) +{ + struct private *m = mod->private; + + switch (type) { + case NL80211_ATTR_IFINDEX: + assert(*(uint32_t *)payload == m->ifindex); + break; + case NL80211_ATTR_SSID: { const char *ssid = payload; - LOG_INFO("%s: SSID: %.*s", m->iface, (int)len, ssid); + LOG_INFO("%s: SSID: %.*s (type=%hhu)", m->iface, (int)len, ssid, type); mtx_lock(&mod->lock); free(m->ssid); @@ -611,10 +718,12 @@ handle_nl80211(struct module *mod, uint16_t type, bool nested, default: LOG_DBG("%s: unrecognized nl80211 attribute: " - "type=%hu%s, len=%hu", m->iface, + "type=%hu%s, len=%zu", m->iface, type, nested ? " (nested)" : "", len); break; } + + return true; } /* @@ -731,7 +840,48 @@ parse_genl_reply(struct module *mod, const struct nlmsghdr *hdr, size_t len) else if (hdr->nlmsg_type == m->nl80211.family_id) { const struct genlmsghdr *genl = NLMSG_DATA(hdr); const size_t msg_size = NLMSG_PAYLOAD(hdr, 0); - foreach_nlattr(mod, genl, msg_size, &handle_nl80211); + + switch (genl->cmd) { + case NL80211_CMD_NEW_INTERFACE: + if (nl80211_is_for_us(mod, genl, msg_size)) { + LOG_DBG("%s: got interface information", m->iface); + foreach_nlattr( + mod, genl, msg_size, &handle_nl80211_new_interface); + } + break; + + case NL80211_CMD_CONNECT: + /* + * Update SSID + * + * Unfortunately, the SSID doesn’t appear to be + * included in *any* of the notifications sent when + * associating, authenticating and connecting to a + * station. + * + * Thus, we need to explicitly request an update. + */ + if (nl80211_is_for_us(mod, genl, msg_size)) { + LOG_DBG("%s: connected, requesting interface information", + m->iface); + send_nl80211_get_interface_request(m); + } + break; + + case NL80211_CMD_DISCONNECT: + if (nl80211_is_for_us(mod, genl, msg_size)) { + LOG_DBG("%s: disconnected, resetting SSID etc", m->iface); + mtx_lock(&mod->lock); + free(m->ssid); + m->ssid = NULL; + mtx_unlock(&mod->lock); + } + break; + + default: + LOG_DBG("unrecognized nl80211 command: %hhu", genl->cmd); + break; + } } else if (hdr->nlmsg_type == NLMSG_ERROR) { From a685dadb7531bafc63bcdb5980e2fd5dfa126acf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 27 Aug 2021 11:59:43 +0200 Subject: [PATCH 237/611] module/network: expose signal strength and rx+tx bitrates --- modules/network.c | 249 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 215 insertions(+), 34 deletions(-) diff --git a/modules/network.c b/modules/network.c index e4703c2..1ca24d0 100644 --- a/modules/network.c +++ b/modules/network.c @@ -47,7 +47,8 @@ struct private { struct { uint16_t family_id; - uint32_t seq_nr; + uint32_t get_interface_seq_nr; + uint32_t get_station_seq_nr; } nl80211; bool get_addresses; @@ -62,6 +63,9 @@ struct private { /* WiFi extensions */ char *ssid; + int signal_strength_dbm; + uint32_t rx_bitrate; + uint32_t tx_bitrate; }; static void @@ -136,8 +140,11 @@ content(struct module *mod) tag_new_string(mod, "ipv4", ipv4_str), tag_new_string(mod, "ipv6", ipv6_str), tag_new_string(mod, "ssid", m->ssid), + tag_new_int(mod, "signal", m->signal_strength_dbm), + tag_new_int(mod, "rx-bitrate", m->rx_bitrate / 1000 / 1000), + tag_new_int(mod, "tx-bitrate", m->tx_bitrate / 1000 / 1000), }, - .count = 8, + .count = 11, }; mtx_unlock(&mod->lock); @@ -296,19 +303,10 @@ send_ctrl_get_family_request(struct private *m) } static bool -send_nl80211_get_interface_request(struct private *m) +send_nl80211_request(struct private *m, uint8_t cmd, uint16_t flags, uint32_t seq) { if (m->ifindex < 0) - return true; - - if (m->nl80211.seq_nr > 0) { - LOG_DBG( - "%s: nl80211 get-interface request already in progress", m->iface); - return true; - } - - m->nl80211.seq_nr = time(NULL); - LOG_DBG("%s: sending nl80211 get-interface request", m->iface); + return false; const struct { struct nlmsghdr hdr; @@ -323,14 +321,14 @@ send_nl80211_get_interface_request(struct private *m) .hdr = { .nlmsg_len = NLMSG_LENGTH(sizeof(req.msg)), .nlmsg_type = m->nl80211.family_id, - .nlmsg_flags = NLM_F_REQUEST, - .nlmsg_seq = m->nl80211.seq_nr, + .nlmsg_flags = flags, + .nlmsg_seq = seq, .nlmsg_pid = nl_pid_value(), }, .msg = { .genl = { - .cmd = NL80211_CMD_GET_INTERFACE, + .cmd = cmd, .version = 1, }, @@ -348,13 +346,52 @@ send_nl80211_get_interface_request(struct private *m) if (!send_nlmsg(m->genl_sock, &req, req.hdr.nlmsg_len)) { LOG_ERRNO("%s: failed to send netlink nl80211 get-inteface request", m->iface); - m->nl80211.seq_nr = 0; return false; } return true; } +static bool +send_nl80211_get_interface(struct private *m) +{ + if (m->nl80211.get_interface_seq_nr > 0) { + LOG_DBG( + "%s: nl80211 get-interface request already in progress", m->iface); + return true; + } + + LOG_DBG("%s: sending nl80211 get-interface request", m->iface); + + uint32_t seq = time(NULL); + if (send_nl80211_request(m, NL80211_CMD_GET_INTERFACE, NLM_F_REQUEST, seq)) { + m->nl80211.get_interface_seq_nr = seq; + return true; + } else + return false; +} + +static bool +send_nl80211_get_station(struct private *m) +{ + if (m->nl80211.get_station_seq_nr > 0) { + LOG_DBG( + "%s: nl80211 get-station request already in progress", m->iface); + return true; + } + + LOG_DBG("%s: sending nl80211 get-station request", m->iface); + + uint32_t seq = time(NULL); + if (send_nl80211_request( + m, NL80211_CMD_GET_STATION, NLM_F_REQUEST | NLM_F_DUMP, seq)) + { + m->nl80211.get_station_seq_nr = seq; + return true; + } else + return false; +} + static bool find_my_ifindex(struct module *mod, const struct ifinfomsg *msg, size_t len) { @@ -373,7 +410,8 @@ find_my_ifindex(struct module *mod, const struct ifinfomsg *msg, size_t len) m->ifindex = msg->ifi_index; mtx_unlock(&mod->lock); - send_nl80211_get_interface_request(m); + send_nl80211_get_interface(m); + send_nl80211_get_station(m); return true; } @@ -584,7 +622,7 @@ foreach_nlattr_nested(struct module *mod, const void *parent_payload, size_t len struct mcast_group { uint32_t id; - char *name; + const char *name; }; static bool @@ -601,15 +639,13 @@ parse_mcast_group(struct module *mod, uint16_t type, bool nested, } case CTRL_ATTR_MCAST_GRP_NAME: { - free(ctx->name); - ctx->name = strndup((const char *)payload, len); + ctx->name = (const char *)payload; break; } default: LOG_WARN("%s: unrecognized GENL MCAST GRP attribute: " - "%hu%s (size: %zu bytes)", m->iface, - type, nested ? " (nested)" : "", len); + "type=%hu, nested=%d, len=%zu", m->iface, type, nested, len); break; } @@ -628,6 +664,11 @@ parse_mcast_groups(struct module *mod, uint16_t type, bool nested, LOG_DBG("MCAST: %s -> %u", group.name, group.id); if (strcmp(group.name, NL80211_MULTICAST_GROUP_MLME) == 0) { + /* + * Join the nl80211 MLME multicast group - for + * CONNECT/DISCONNECT events. + */ + int r = setsockopt( m->genl_sock, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &group.id, sizeof(int)); @@ -636,7 +677,6 @@ parse_mcast_groups(struct module *mod, uint16_t type, bool nested, LOG_ERRNO("failed to joint the nl80211 MLME mcast group"); } - free(group.name); return true; } @@ -649,7 +689,7 @@ handle_genl_ctrl(struct module *mod, uint16_t type, bool nested, switch (type) { case CTRL_ATTR_FAMILY_ID: { m->nl80211.family_id = *(const uint16_t *)payload; - send_nl80211_get_interface_request(m); + send_nl80211_get_interface(m); break; } @@ -663,8 +703,7 @@ handle_genl_ctrl(struct module *mod, uint16_t type, bool nested, default: LOG_DBG("%s: unrecognized GENL CTRL attribute: " - "%hu%s (size: %zu bytes)", m->iface, - type, nested ? " (nested)" : "", len); + "type=%hu, nested=%d, len=%zu", m->iface, type, nested, len); break; } @@ -705,7 +744,6 @@ handle_nl80211_new_interface(struct module *mod, uint16_t type, bool nested, case NL80211_ATTR_SSID: { const char *ssid = payload; - LOG_INFO("%s: SSID: %.*s (type=%hhu)", m->iface, (int)len, ssid, type); mtx_lock(&mod->lock); free(m->ssid); @@ -718,8 +756,129 @@ handle_nl80211_new_interface(struct module *mod, uint16_t type, bool nested, default: LOG_DBG("%s: unrecognized nl80211 attribute: " - "type=%hu%s, len=%zu", m->iface, - type, nested ? " (nested)" : "", len); + "type=%hu, nested=%d, len=%zu", m->iface, type, nested, len); + break; + } + + return true; +} + +struct rate_info_ctx { + unsigned bitrate; +}; + +static bool +handle_nl80211_rate_info(struct module *mod, uint16_t type, bool nested, + const void *payload, size_t len, void *_ctx) +{ + struct private *m UNUSED = mod->private; + struct rate_info_ctx *ctx = _ctx; + + switch (type) { + case NL80211_RATE_INFO_BITRATE32: { + uint32_t bitrate_100kbit = *(uint32_t *)payload; + ctx->bitrate = bitrate_100kbit * 100 * 1000; + break; + } + + case NL80211_RATE_INFO_BITRATE: + if (ctx->bitrate == 0) { + uint16_t bitrate_100kbit = *(uint16_t *)payload; + ctx->bitrate = bitrate_100kbit * 100 * 1000; + } else { + /* Prefer the BITRATE32 attribute */ + } + break; + + default: + LOG_DBG("%s: unrecognized nl80211 rate info attribute: " + "type=%hu, nested=%d, len=%zu", m->iface, type, nested, len); + break; + } + + return true; +} + +struct station_info_ctx { + bool update_bar; +}; + +static bool +handle_nl80211_station_info(struct module *mod, uint16_t type, bool nested, + const void *payload, size_t len, void *_ctx) +{ + struct private *m = mod->private; + struct station_info_ctx *ctx = _ctx; + + switch (type) { + case NL80211_STA_INFO_SIGNAL: + LOG_DBG("signal strength (last): %hhd dBm", *(uint8_t *)payload); + break; + + case NL80211_STA_INFO_SIGNAL_AVG: { + LOG_DBG("signal strength (average): %hhd dBm", *(uint8_t *)payload); + mtx_lock(&mod->lock); + m->signal_strength_dbm = *(int8_t *)payload; + mtx_unlock(&mod->lock); + ctx->update_bar = true; + break; + } + + case NL80211_STA_INFO_TX_BITRATE: { + struct rate_info_ctx rctx = {0}; + foreach_nlattr_nested( + mod, payload, len, &handle_nl80211_rate_info, &rctx); + + LOG_DBG("TX bitrate: %.1f Mbit/s", rctx.bitrate / 1000. / 1000.); + mtx_lock(&mod->lock); + m->tx_bitrate = rctx.bitrate; + mtx_unlock(&mod->lock); + ctx->update_bar = true; + break; + } + + case NL80211_STA_INFO_RX_BITRATE: { + struct rate_info_ctx rctx = {0}; + foreach_nlattr_nested( + mod, payload, len, &handle_nl80211_rate_info, &rctx); + + LOG_DBG("RX bitrate: %.1f Mbit/s", rctx.bitrate / 1000. / 1000.); + mtx_lock(&mod->lock); + m->rx_bitrate = rctx.bitrate; + mtx_unlock(&mod->lock); + ctx->update_bar = true; + break; + } + + default: + LOG_DBG("%s: unrecognized nl80211 station info attribute: " + "type=%hu, nested=%d, len=%zu", m->iface, type, nested, len); + break; + } + + return true; +} + +static bool +handle_nl80211_new_station(struct module *mod, uint16_t type, bool nested, + const void *payload, size_t len) +{ + struct private *m UNUSED = mod->private; + + switch (type) { + case NL80211_ATTR_STA_INFO: { + struct station_info_ctx ctx = {0}; + foreach_nlattr_nested( + mod, payload, len, &handle_nl80211_station_info, &ctx); + + if (ctx.update_bar) + mod->bar->refresh(mod->bar); + break; + } + + default: + LOG_DBG("%s: unrecognized nl80211 attribute: " + "type=%hu, nested=%d, len=%zu", m->iface, type, nested, len); break; } @@ -826,12 +985,19 @@ parse_genl_reply(struct module *mod, const struct nlmsghdr *hdr, size_t len) struct private *m = mod->private; for (; NLMSG_OK(hdr, len); hdr = NLMSG_NEXT(hdr, len)) { - if (hdr->nlmsg_seq == m->nl80211.seq_nr) { + if (hdr->nlmsg_seq == m->nl80211.get_interface_seq_nr) { /* Current request is now considered complete */ - m->nl80211.seq_nr = 0; + m->nl80211.get_interface_seq_nr = 0; } - if (hdr->nlmsg_type == GENL_ID_CTRL) { + if (hdr->nlmsg_type == NLMSG_DONE) { + if (hdr->nlmsg_seq == m->nl80211.get_station_seq_nr) { + /* Current request is now considered complete */ + m->nl80211.get_station_seq_nr = 0; + } + } + + else if (hdr->nlmsg_type == GENL_ID_CTRL) { const struct genlmsghdr *genl = NLMSG_DATA(hdr); const size_t msg_size = NLMSG_PAYLOAD(hdr, 0); foreach_nlattr(mod, genl, msg_size, &handle_genl_ctrl); @@ -847,6 +1013,8 @@ parse_genl_reply(struct module *mod, const struct nlmsghdr *hdr, size_t len) LOG_DBG("%s: got interface information", m->iface); foreach_nlattr( mod, genl, msg_size, &handle_nl80211_new_interface); + + LOG_INFO("%s: SSID: %s", m->iface, m->ssid); } break; @@ -864,13 +1032,14 @@ parse_genl_reply(struct module *mod, const struct nlmsghdr *hdr, size_t len) if (nl80211_is_for_us(mod, genl, msg_size)) { LOG_DBG("%s: connected, requesting interface information", m->iface); - send_nl80211_get_interface_request(m); + send_nl80211_get_interface(m); } break; case NL80211_CMD_DISCONNECT: if (nl80211_is_for_us(mod, genl, msg_size)) { LOG_DBG("%s: disconnected, resetting SSID etc", m->iface); + mtx_lock(&mod->lock); free(m->ssid); m->ssid = NULL; @@ -878,6 +1047,18 @@ parse_genl_reply(struct module *mod, const struct nlmsghdr *hdr, size_t len) } break; + case NL80211_CMD_NEW_STATION: + if (nl80211_is_for_us(mod, genl, msg_size)) { + LOG_DBG("%s: got station information", m->iface); + foreach_nlattr(mod, genl, msg_size, &handle_nl80211_new_station); + } + + LOG_INFO("%s: signal: %d dBm, RX=%u Mbit/s, TX=%u Mbit/s", + m->iface, m->signal_strength_dbm, + m->rx_bitrate / 1000 / 1000, + m->tx_bitrate / 1000 / 1000); + break; + default: LOG_DBG("unrecognized nl80211 command: %hhu", genl->cmd); break; From 01ee028c4da33bff5988028c26a02590b12bfe18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 27 Aug 2021 12:07:41 +0200 Subject: [PATCH 238/611] doc: network: document ssid, signal, rx-bitrate and tx-bitrate --- doc/yambar-modules-network.5.scd | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/doc/yambar-modules-network.5.scd b/doc/yambar-modules-network.5.scd index c8a66d8..27ce739 100644 --- a/doc/yambar-modules-network.5.scd +++ b/doc/yambar-modules-network.5.scd @@ -39,6 +39,19 @@ address. | ipv6 : string : IPv6 address assigned to the interface, or *""* if none +| ssid +: string +: SSID the adapter is connected to (Wi-Fi only) +| signal +: int +: Signal strength, in dBm (Wi-Fi only) +| rx-bitrate +: int +: RX bitrate, in Mbit/s +| tx-bitrate +: int +: TX bitrate in Mbit/s + # CONFIGURATION From 25379b7e1f9d2150bb0918e97bf91ecac127a954 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 27 Aug 2021 12:10:04 +0200 Subject: [PATCH 239/611] changelog: new network tags (ssid, signal rx+tx bitrate) --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 501716a..638a965 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ * foreign-toplevel: Wayland module that provides information about currently opened windows. * alsa: support for capture devices. +* network: `ssid`, `signal`, `rx-bitrate` and `rx-bitrate` tags. ### Changed From 066427d77ac1745b366cedbb3dab0569499e1bf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 27 Aug 2021 12:13:20 +0200 Subject: [PATCH 240/611] examples: add ssid to wifi-network module --- examples/configurations/laptop.conf | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/examples/configurations/laptop.conf b/examples/configurations/laptop.conf index f079f2a..eab6c46 100644 --- a/examples/configurations/laptop.conf +++ b/examples/configurations/laptop.conf @@ -219,9 +219,13 @@ bar: up: map: tag: ipv4 - default: {string: {text: , font: *awesome}} - values: - "": {string: {text: , font: *awesome, foreground: ffffff66}} + default: + - string: {text: , font: *awesome} + - string: {text: "{ssid}"} + values: + "": + - string: {text: , font: *awesome, foreground: ffffff66} + - string: {text: "{ssid}", foreground: ffffff66} - alsa: card: hw:PCH mixer: Master From e8a2f8df9ab5dcdd407e493de29cc196ac92463d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 27 Aug 2021 15:38:48 +0200 Subject: [PATCH 241/611] module/network: reset signal strength and RX+TX bitrates on disconnect --- modules/network.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/network.c b/modules/network.c index 1ca24d0..cb3f670 100644 --- a/modules/network.c +++ b/modules/network.c @@ -1043,6 +1043,8 @@ parse_genl_reply(struct module *mod, const struct nlmsghdr *hdr, size_t len) mtx_lock(&mod->lock); free(m->ssid); m->ssid = NULL; + m->signal_strength_dbm = 0; + m->rx_bitrate = m->tx_bitrate = 0; mtx_unlock(&mod->lock); } break; From d450bf12a17b4eec7c82d61dbc3a2b8b05920d98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 27 Aug 2021 15:39:11 +0200 Subject: [PATCH 242/611] =?UTF-8?q?module/network:=20re-request=20station?= =?UTF-8?q?=20info=20when=20we=E2=80=99re=20re-connected?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/network.c | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/network.c b/modules/network.c index cb3f670..30cd35d 100644 --- a/modules/network.c +++ b/modules/network.c @@ -1033,6 +1033,7 @@ parse_genl_reply(struct module *mod, const struct nlmsghdr *hdr, size_t len) LOG_DBG("%s: connected, requesting interface information", m->iface); send_nl80211_get_interface(m); + send_nl80211_get_station(m); } break; From dabb2e140749cc1f4583541abfe692fc232a2af3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 27 Aug 2021 15:56:31 +0200 Subject: [PATCH 243/611] module/network: add support for periodically polling Wi-Fi stats MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds a new ‘poll-interval’ option to the network module. When set to a non-zero value, the following Wi-Fi stats will be updated: * Signal strength * RX+TX bitrate --- doc/yambar-modules-network.5.scd | 5 ++++ modules/network.c | 50 ++++++++++++++++++++++++++++++-- 2 files changed, 52 insertions(+), 3 deletions(-) diff --git a/doc/yambar-modules-network.5.scd b/doc/yambar-modules-network.5.scd index 27ce739..8ac46a1 100644 --- a/doc/yambar-modules-network.5.scd +++ b/doc/yambar-modules-network.5.scd @@ -63,6 +63,11 @@ address. : string : yes : Name of network interface to monitor +| poll-interval +: int +: no +: Periodically (in seconds) update the signal and rx+tx bitrate tags. + # EXAMPLES diff --git a/modules/network.c b/modules/network.c index 30cd35d..bf32c57 100644 --- a/modules/network.c +++ b/modules/network.c @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -41,6 +42,7 @@ struct af_addr { struct private { char *iface; struct particle *label; + int poll_interval; int genl_sock; int rt_sock; @@ -1097,6 +1099,25 @@ run(struct module *mod) int ret = 1; struct private *m = mod->private; + int timer_fd = -1; + if (m->poll_interval > 0) { + timer_fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC); + if (timer_fd < 0) { + LOG_ERRNO("%s: failed to create poll timer FD", m->iface); + goto out; + } + + struct itimerspec poll_time = { + .it_value = {.tv_sec = m->poll_interval}, + .it_interval = {.tv_sec = m->poll_interval}, + }; + + if (timerfd_settime(timer_fd, 0, &poll_time, NULL) < 0) { + LOG_ERRNO("%s: failed to arm poll timer", m->iface); + goto out; + } + } + m->rt_sock = netlink_connect_rt(); m->genl_sock = netlink_connect_genl(); @@ -1115,9 +1136,10 @@ run(struct module *mod) {.fd = mod->abort_fd, .events = POLLIN}, {.fd = m->rt_sock, .events = POLLIN}, {.fd = m->genl_sock, .events = POLLIN}, + {.fd = timer_fd, .events = POLLIN}, }; - poll(fds, 3, -1); + poll(fds, 3 + (timer_fd >= 0 ? 1 : 0), -1); if (fds[0].revents & (POLLIN | POLLHUP)) break; @@ -1129,6 +1151,11 @@ run(struct module *mod) break; } + if (fds[3].revents & POLLHUP) { + LOG_ERR("%s: disconnected from timer FD", m->iface); + break; + } + if (fds[1].revents & POLLIN) { /* Read one (or more) messages */ void *reply; @@ -1159,6 +1186,17 @@ run(struct module *mod) free(reply); } + + if (fds[3].revents & POLLIN) { + uint64_t count; + ssize_t amount = read(timer_fd, &count, sizeof(count)); + if (amount < 0) { + LOG_ERRNO("failed to read from timer FD"); + break; + } + + send_nl80211_get_station(m); + } } ret = 0; @@ -1168,16 +1206,19 @@ run(struct module *mod) close(m->rt_sock); if (m->genl_sock >= 0) close(m->genl_sock); + if (timer_fd >= 0) + close(timer_fd); m->rt_sock = m->genl_sock = -1; return ret; } static struct module * -network_new(const char *iface, struct particle *label) +network_new(const char *iface, struct particle *label, int poll_interval) { struct private *priv = calloc(1, sizeof(*priv)); priv->iface = strdup(iface); priv->label = label; + priv->poll_interval = poll_interval; priv->genl_sock = -1; priv->rt_sock = -1; @@ -1200,9 +1241,11 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) { const struct yml_node *name = yml_get_value(node, "name"); const struct yml_node *content = yml_get_value(node, "content"); + const struct yml_node *poll = yml_get_value(node, "poll-interval"); return network_new( - yml_value_as_string(name), conf_to_particle(content, inherited)); + yml_value_as_string(name), conf_to_particle(content, inherited), + poll != NULL ? yml_value_as_int(poll) : 0); } static bool @@ -1210,6 +1253,7 @@ verify_conf(keychain_t *chain, const struct yml_node *node) { static const struct attr_info attrs[] = { {"name", true, &conf_verify_string}, + {"poll-interval", false, &conf_verify_int}, MODULE_COMMON_ATTRS, }; From 4ab3263ebbbdd896d324fa8dd40eb36ca2696dc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 1 Sep 2021 19:11:14 +0200 Subject: [PATCH 244/611] changelog: network: poll-interval --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 638a965..0f587e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,8 @@ currently opened windows. * alsa: support for capture devices. * network: `ssid`, `signal`, `rx-bitrate` and `rx-bitrate` tags. +* network: `poll-interval` option (for the new `signal` and + `*-bitrate` tags). ### Changed From e201cc3d30693eb3a65c22b3bd5f69489d32100a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 27 Aug 2021 21:02:01 +0200 Subject: [PATCH 245/611] =?UTF-8?q?tag:=20add=20a=20=E2=80=98%=E2=80=99=20?= =?UTF-8?q?formatter?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Range tags can now be rendered as a percentage value, by using a ‘%’ formatter: {tag_name:%} --- CHANGELOG.md | 1 + doc/yambar-tags.5.scd | 51 ++++++++++++++++++++++++++++++++++++++----- tag.c | 28 ++++++++++++++++++++++-- 3 files changed, 73 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f587e1..57fe2e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ * network: `ssid`, `signal`, `rx-bitrate` and `rx-bitrate` tags. * network: `poll-interval` option (for the new `signal` and `*-bitrate` tags). +* tags: percentage formatter, for range tags: `{tag_name:%}`. ### Changed diff --git a/doc/yambar-tags.5.scd b/doc/yambar-tags.5.scd index 1e3c138..8d357cb 100644 --- a/doc/yambar-tags.5.scd +++ b/doc/yambar-tags.5.scd @@ -38,9 +38,50 @@ The available tag *types* are: # FORMATTING -As mentioned above, each tag type has a default representation that is -used when the tag is rendered by a string particle. +A tag may be followed by one or more formatters that alter the tags +rendition. + +Formatters are added by appending a ':' separated list of formatter +names: + + "{tag_name:max:hex}" + +In the table below, "kind" describes the type of action performed by +the formatter: + +- *format*: changes the representation of the tag's value +- *selector*: changes what to render + +In general, formatters of the same kind cannot be combined; if +multiple formatters of the same kind are specified, the last one will +be used. + +[[ *Formatter* +:[ *Kind* +:[ *Description* +:[ *Applies to*] +| hex +: format +: Renders a tag's value in hex +: All tag types +| oct +: format +: Renders a tag's value in octal +: All tag types +| % +: format +: Renders a range tag's value as a percentage value +: Range tags +| min +: selector +: Renders a range tag's mininum value +: Range tags +| max +: selector +: Renders a range tag's maximum value +: Range tags +| unit +: selector +: Renders a realtime tag's unit (e.g. "s", or "ms") +: Realtime tags -All integer, floating point and boolean tag types can be modified to -instead be rendered in hexadecimal or octal form, by appending either -the *:hex* or *:oct* suffixes. For example, _\"{tag_name:hex}\"_. \ No newline at end of file diff --git a/tag.c b/tag.c index 8ca7e52..0548b2e 100644 --- a/tag.c +++ b/tag.c @@ -459,7 +459,7 @@ tags_expand_template(const char *template, const struct tag_set *tags) sbuf_append_at_most(&formatted, template, begin - template); /* Parse arguments */ - enum { FMT_DEFAULT, FMT_HEX, FMT_OCT } format = FMT_DEFAULT; + enum { FMT_DEFAULT, FMT_HEX, FMT_OCT, FMT_PERCENT } format = FMT_DEFAULT; enum { VALUE_VALUE, VALUE_MIN, VALUE_MAX, VALUE_UNIT } kind = VALUE_VALUE; for (size_t i = 0; i < MAX_TAG_ARGS; i++) { @@ -469,12 +469,16 @@ tags_expand_template(const char *template, const struct tag_set *tags) format = FMT_HEX; else if (strcmp(tag_args[i], "oct") == 0) format = FMT_OCT; + else if (strcmp(tag_args[i], "%") == 0) + format = FMT_PERCENT; else if (strcmp(tag_args[i], "min") == 0) kind = VALUE_MIN; else if (strcmp(tag_args[i], "max") == 0) kind = VALUE_MAX; else if (strcmp(tag_args[i], "unit") == 0) kind = VALUE_UNIT; + else + LOG_WARN("invalid tag formatter: %s", tag_args[i]); } /* Copy tag value */ @@ -493,20 +497,40 @@ tags_expand_template(const char *template, const struct tag_set *tags) sbuf_append(&formatted, str); break; } + + case FMT_PERCENT: { + const long min = tag->min(tag); + const long max = tag->max(tag); + const long cur = tag->as_int(tag); + + char str[4]; + snprintf(str, sizeof(str), "%lu", (cur - min) * 100 / (max - min)); + sbuf_append(&formatted, str); + break; + } } break; case VALUE_MIN: case VALUE_MAX: { - const long value = kind == VALUE_MIN ? tag->min(tag) : tag->max(tag); + const long min = tag->min(tag); + const long max = tag->max(tag); + long value = kind == VALUE_MIN ? min : max; const char *fmt; switch (format) { case FMT_DEFAULT: fmt = "%ld"; break; case FMT_HEX: fmt = "%lx"; break; case FMT_OCT: fmt = "%lo"; break; + + case FMT_PERCENT: + value = (value - min) * 100 / (max - min); + fmt = "%lu"; + break; } + + char str[24]; snprintf(str, sizeof(str), fmt, value); sbuf_append(&formatted, str); From a6194c63e6d1dfe1c0346783f06016f16dfe7a28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 27 Aug 2021 21:15:19 +0200 Subject: [PATCH 246/611] tag: add kb/mb/gb formatters --- CHANGELOG.md | 2 ++ doc/yambar-tags.5.scd | 13 ++++++++++++ tag.c | 46 ++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 58 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 57fe2e7..365b4fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,8 @@ * network: `poll-interval` option (for the new `signal` and `*-bitrate` tags). * tags: percentage formatter, for range tags: `{tag_name:%}`. +* tags: percentage tag formatter, for range tags: `{tag_name:%}`. +* tags: kb/mb/gb tag formatters. ### Changed diff --git a/doc/yambar-tags.5.scd b/doc/yambar-tags.5.scd index 8d357cb..a237e30 100644 --- a/doc/yambar-tags.5.scd +++ b/doc/yambar-tags.5.scd @@ -72,6 +72,19 @@ be used. : format : Renders a range tag's value as a percentage value : Range tags +| kb +: format +: Renders a tag's value (in decimal) divided by 1024 (note that no + unit suffix is appended) +| mb +: format +: Renders a tag's value (in decimal) divided by 1024^2 (note that no + unit suffix is appended) +| gb +: format +: Renders a tag's value (in decimal) divided by 1024^3 (note that no + unit suffix is appended) +: All tag types | min : selector : Renders a range tag's mininum value diff --git a/tag.c b/tag.c index 0548b2e..fce5d38 100644 --- a/tag.c +++ b/tag.c @@ -459,7 +459,15 @@ tags_expand_template(const char *template, const struct tag_set *tags) sbuf_append_at_most(&formatted, template, begin - template); /* Parse arguments */ - enum { FMT_DEFAULT, FMT_HEX, FMT_OCT, FMT_PERCENT } format = FMT_DEFAULT; + enum { + FMT_DEFAULT, + FMT_HEX, + FMT_OCT, + FMT_PERCENT, + FMT_KBYTE, + FMT_MBYTE, + FMT_GBYTE, + } format = FMT_DEFAULT; enum { VALUE_VALUE, VALUE_MIN, VALUE_MAX, VALUE_UNIT } kind = VALUE_VALUE; for (size_t i = 0; i < MAX_TAG_ARGS; i++) { @@ -471,6 +479,12 @@ tags_expand_template(const char *template, const struct tag_set *tags) format = FMT_OCT; else if (strcmp(tag_args[i], "%") == 0) format = FMT_PERCENT; + else if (strcmp(tag_args[i], "kb") == 0) + format = FMT_KBYTE; + else if (strcmp(tag_args[i], "mb") == 0) + format = FMT_MBYTE; + else if (strcmp(tag_args[i], "gb") == 0) + format = FMT_GBYTE; else if (strcmp(tag_args[i], "min") == 0) kind = VALUE_MIN; else if (strcmp(tag_args[i], "max") == 0) @@ -508,6 +522,21 @@ tags_expand_template(const char *template, const struct tag_set *tags) sbuf_append(&formatted, str); break; } + + case FMT_KBYTE: + case FMT_MBYTE: + case FMT_GBYTE: { + const long divider = + format == FMT_KBYTE ? 1024 : + format == FMT_MBYTE ? 1024 * 1024 : + format == FMT_GBYTE ? 1024 * 1024 * 1024 : + 1; + + char str[24]; + snprintf(str, sizeof(str), "%lu", tag->as_int(tag) / divider); + sbuf_append(&formatted, str); + break; + } } break; @@ -527,9 +556,20 @@ tags_expand_template(const char *template, const struct tag_set *tags) value = (value - min) * 100 / (max - min); fmt = "%lu"; break; - } - + case FMT_KBYTE: + case FMT_MBYTE: + case FMT_GBYTE: { + const long divider = + format == FMT_KBYTE ? 1024 : + format == FMT_MBYTE ? 1024 * 1024 : + format == FMT_GBYTE ? 1024 * 1024 * 1024 : + 1; + value /= divider; + fmt = "%lu"; + break; + } + } char str[24]; snprintf(str, sizeof(str), fmt, value); From f0b16033fe28986dadbb6609280c3cad29ff5c17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 27 Aug 2021 21:18:01 +0200 Subject: [PATCH 247/611] doc: yambar-tags: codespell: mininum -> minimum --- doc/yambar-tags.5.scd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/yambar-tags.5.scd b/doc/yambar-tags.5.scd index a237e30..cdb0b4b 100644 --- a/doc/yambar-tags.5.scd +++ b/doc/yambar-tags.5.scd @@ -87,7 +87,7 @@ be used. : All tag types | min : selector -: Renders a range tag's mininum value +: Renders a range tag's minimum value : Range tags | max : selector From 8c2e5d8bdea8bcccbc56c8a60774ff75f5f2b4ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 27 Aug 2021 21:19:20 +0200 Subject: [PATCH 248/611] doc: yambar-tags: add missing last column to kb/mb formatters --- doc/yambar-tags.5.scd | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/yambar-tags.5.scd b/doc/yambar-tags.5.scd index cdb0b4b..38925a9 100644 --- a/doc/yambar-tags.5.scd +++ b/doc/yambar-tags.5.scd @@ -76,10 +76,12 @@ be used. : format : Renders a tag's value (in decimal) divided by 1024 (note that no unit suffix is appended) +: All tag types | mb : format : Renders a tag's value (in decimal) divided by 1024^2 (note that no unit suffix is appended) +: All tag types | gb : format : Renders a tag's value (in decimal) divided by 1024^3 (note that no From eff890ab9d5ad74aa9da4d5b8f7ee5f45adbe7e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 27 Aug 2021 21:57:34 +0200 Subject: [PATCH 249/611] tag: add kib/mib/gib formatters --- CHANGELOG.md | 2 +- doc/yambar-tags.5.scd | 16 +++++----------- tag.c | 33 ++++++++++++++++++++++++++++++--- 3 files changed, 36 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 365b4fd..c48f9d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,7 +35,7 @@ `*-bitrate` tags). * tags: percentage formatter, for range tags: `{tag_name:%}`. * tags: percentage tag formatter, for range tags: `{tag_name:%}`. -* tags: kb/mb/gb tag formatters. +* tags: kb/mb/gb, and kib/mib/gib tag formatters. ### Changed diff --git a/doc/yambar-tags.5.scd b/doc/yambar-tags.5.scd index 38925a9..49a08ab 100644 --- a/doc/yambar-tags.5.scd +++ b/doc/yambar-tags.5.scd @@ -72,20 +72,14 @@ be used. : format : Renders a range tag's value as a percentage value : Range tags -| kb +| kb, mb, gb : format -: Renders a tag's value (in decimal) divided by 1024 (note that no - unit suffix is appended) +: Renders a tag's value (in decimal) divided by 1024, 1024^2 or + 1024^3. Note: no unit suffix is appended) : All tag types -| mb +| kib, mib, gib : format -: Renders a tag's value (in decimal) divided by 1024^2 (note that no - unit suffix is appended) -: All tag types -| gb -: format -: Renders a tag's value (in decimal) divided by 1024^3 (note that no - unit suffix is appended) +: Same as *kb*, *mb* and *gb*, but divide by 1000^n instead of 1024^n. : All tag types | min : selector diff --git a/tag.c b/tag.c index fce5d38..2943423 100644 --- a/tag.c +++ b/tag.c @@ -467,8 +467,17 @@ tags_expand_template(const char *template, const struct tag_set *tags) FMT_KBYTE, FMT_MBYTE, FMT_GBYTE, + FMT_KIBYTE, + FMT_MIBYTE, + FMT_GIBYTE, } format = FMT_DEFAULT; - enum { VALUE_VALUE, VALUE_MIN, VALUE_MAX, VALUE_UNIT } kind = VALUE_VALUE; + + enum { + VALUE_VALUE, + VALUE_MIN, + VALUE_MAX, + VALUE_UNIT, + } kind = VALUE_VALUE; for (size_t i = 0; i < MAX_TAG_ARGS; i++) { if (tag_args[i] == NULL) @@ -485,6 +494,12 @@ tags_expand_template(const char *template, const struct tag_set *tags) format = FMT_MBYTE; else if (strcmp(tag_args[i], "gb") == 0) format = FMT_GBYTE; + else if (strcmp(tag_args[i], "kib") == 0) + format = FMT_KIBYTE; + else if (strcmp(tag_args[i], "mib") == 0) + format = FMT_MIBYTE; + else if (strcmp(tag_args[i], "gib") == 0) + format = FMT_GIBYTE; else if (strcmp(tag_args[i], "min") == 0) kind = VALUE_MIN; else if (strcmp(tag_args[i], "max") == 0) @@ -525,11 +540,17 @@ tags_expand_template(const char *template, const struct tag_set *tags) case FMT_KBYTE: case FMT_MBYTE: - case FMT_GBYTE: { + case FMT_GBYTE: + case FMT_KIBYTE: + case FMT_MIBYTE: + case FMT_GIBYTE: { const long divider = format == FMT_KBYTE ? 1024 : format == FMT_MBYTE ? 1024 * 1024 : format == FMT_GBYTE ? 1024 * 1024 * 1024 : + format == FMT_KIBYTE ? 1000 : + format == FMT_MIBYTE ? 1000 * 1000 : + format == FMT_GIBYTE ? 1000 * 1000 * 1000 : 1; char str[24]; @@ -559,11 +580,17 @@ tags_expand_template(const char *template, const struct tag_set *tags) case FMT_KBYTE: case FMT_MBYTE: - case FMT_GBYTE: { + case FMT_GBYTE: + case FMT_KIBYTE: + case FMT_MIBYTE: + case FMT_GIBYTE: { const long divider = format == FMT_KBYTE ? 1024 : format == FMT_MBYTE ? 1024 * 1024 : format == FMT_GBYTE ? 1024 * 1024 * 1024 : + format == FMT_KIBYTE ? 1000 : + format == FMT_MIBYTE ? 1000 * 1000 : + format == FMT_GIBYTE ? 1000 * 1000 * 1000 : 1; value /= divider; fmt = "%lu"; From 6fa118a5e5c75bc3691b271641ac082f20ea8e3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 3 Sep 2021 20:38:26 +0200 Subject: [PATCH 250/611] changelog: prepare for 1.7.0 --- CHANGELOG.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c48f9d7..ab2e3fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,13 +1,14 @@ # Changelog -* [Unreleased](#unreleased) +* [1.7.0](#1-7-0) * [1.6.2](#1-6-2) * [1.6.1](#1-6-1) * [1.6.0](#1-6-0) * [1.5.0](#1-5-0) -## Unreleased +## 1.7.0 + ### Added * i3: `persistent` attribute, allowing persistent workspaces @@ -46,8 +47,6 @@ (https://codeberg.org/dnkl/yambar/issues/86). -### Deprecated -### Removed ### Fixed * `yambar --backend=wayland` always erroring out with _”yambar was @@ -58,7 +57,6 @@ disconnected (https://codeberg.org/dnkl/yambar/issues/61). -### Security ### Contributors * [paemuri](https://codeberg.org/paemuri) From 42d944d020100991294e6b3a6dca45c7926bc5bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 3 Sep 2021 20:39:01 +0200 Subject: [PATCH 251/611] meson/pkgbuild: bump version to 1.7.0 --- PKGBUILD | 2 +- PKGBUILD.wayland-only | 2 +- meson.build | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/PKGBUILD b/PKGBUILD index 17a867c..e4b343b 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -1,5 +1,5 @@ pkgname=yambar -pkgver=1.6.2 +pkgver=1.7.0 pkgrel=1 pkgdesc="Simplistic and highly configurable status panel for X and Wayland" arch=('x86_64' 'aarch64') diff --git a/PKGBUILD.wayland-only b/PKGBUILD.wayland-only index a3f8207..d0963e4 100644 --- a/PKGBUILD.wayland-only +++ b/PKGBUILD.wayland-only @@ -1,5 +1,5 @@ pkgname=yambar-wayland -pkgver=1.6.2 +pkgver=1.7.0 pkgrel=1 pkgdesc="Simplistic and highly configurable status panel for Wayland" arch=('x86_64' 'aarch64') diff --git a/meson.build b/meson.build index f621b43..b7065e6 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('yambar', 'c', - version: '1.6.2', + version: '1.7.0', license: 'MIT', meson_version: '>=0.53.0', default_options: ['c_std=c18', From ab0323e35e9aa2626355409055c60fff5270a93d Mon Sep 17 00:00:00 2001 From: nogerine Date: Sat, 11 Sep 2021 00:15:14 +0000 Subject: [PATCH 252/611] config: fix incorrect attribute name in validation --- config-verify.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config-verify.c b/config-verify.c index 67b21e1..63efef3 100644 --- a/config-verify.c +++ b/config-verify.c @@ -434,8 +434,8 @@ conf_verify_bar(const struct yml_node *bar) {"right-spacing", false, &conf_verify_int}, {"margin", false, &conf_verify_int}, - {"left_margin", false, &conf_verify_int}, - {"right_margin", false, &conf_verify_int}, + {"left-margin", false, &conf_verify_int}, + {"right-margin", false, &conf_verify_int}, {"border", false, &verify_bar_border}, {"font", false, &conf_verify_font}, From 3ff9fcab48188d1ac24931779cffcebac7ee01c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 12 Sep 2021 09:56:01 +0200 Subject: [PATCH 253/611] =?UTF-8?q?changelog:=20add=20new=20=E2=80=98unrel?= =?UTF-8?q?eased=E2=80=99=20section?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ab2e3fc..5954fa6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # Changelog +* [Unreleased](#unreleased) * [1.7.0](#1-7-0) * [1.6.2](#1-6-2) * [1.6.1](#1-6-1) @@ -7,6 +8,16 @@ * [1.5.0](#1-5-0) +## Unreleased +### Added +### Changed +### Deprecated +### Removed +### Fixed +### Security +### Contributors + + ## 1.7.0 ### Added From 60904bb18f262a562ba819db513e80945f242c17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 12 Sep 2021 10:30:36 +0200 Subject: [PATCH 254/611] changelog: left- and right-margin options being rejected as invalid --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5954fa6..00542df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,11 @@ ### Deprecated ### Removed ### Fixed + +* `left-margin` and `right-margin` from being rejected as invalid + options. + + ### Security ### Contributors From ba5b28f4379c39d945fb2077f1625f7d83c12751 Mon Sep 17 00:00:00 2001 From: anb Date: Sat, 11 Sep 2021 13:40:15 -0700 Subject: [PATCH 255/611] clock: add a config option to show UTC time --- CHANGELOG.md | 2 +- doc/yambar-modules-clock.5.scd | 4 ++++ modules/clock.c | 11 ++++++++--- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 00542df..e46d99f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -53,7 +53,7 @@ * tags: percentage formatter, for range tags: `{tag_name:%}`. * tags: percentage tag formatter, for range tags: `{tag_name:%}`. * tags: kb/mb/gb, and kib/mib/gib tag formatters. - +* clock: add a config option to show UTC time. ### Changed diff --git a/doc/yambar-modules-clock.5.scd b/doc/yambar-modules-clock.5.scd index 05e18fc..e8dbadf 100644 --- a/doc/yambar-modules-clock.5.scd +++ b/doc/yambar-modules-clock.5.scd @@ -29,6 +29,10 @@ clock - This module provides the current date and time : string : no : *strftime* formatter for the _date_ date (default=*%x*) +| utc +: bool +: no +: Use GMT instead of local timezone (default=false) # EXAMPLES diff --git a/modules/clock.c b/modules/clock.c index b0db44e..2956a40 100644 --- a/modules/clock.c +++ b/modules/clock.c @@ -22,6 +22,7 @@ struct private { } update_granularity; char *date_format; char *time_format; + bool utc; }; static void @@ -46,7 +47,7 @@ content(struct module *mod) { const struct private *m = mod->private; time_t t = time(NULL); - struct tm *tm = localtime(&t); + struct tm *tm = m->utc ? gmtime(&t) : localtime(&t); char date_str[1024]; strftime(date_str, sizeof(date_str), m->date_format, tm); @@ -131,12 +132,13 @@ run(struct module *mod) } static struct module * -clock_new(struct particle *label, const char *date_format, const char *time_format) +clock_new(struct particle *label, const char *date_format, const char *time_format, bool utc) { struct private *m = calloc(1, sizeof(*m)); m->label = label; m->date_format = strdup(date_format); m->time_format = strdup(time_format); + m->utc = utc; static const char *const seconds_formatters[] = { "%c", @@ -178,11 +180,13 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) const struct yml_node *c = yml_get_value(node, "content"); const struct yml_node *date_format = yml_get_value(node, "date-format"); const struct yml_node *time_format = yml_get_value(node, "time-format"); + const struct yml_node *utc = yml_get_value(node, "utc"); return clock_new( conf_to_particle(c, inherited), date_format != NULL ? yml_value_as_string(date_format) : "%x", - time_format != NULL ? yml_value_as_string(time_format) : "%H:%M"); + time_format != NULL ? yml_value_as_string(time_format) : "%H:%M", + utc != NULL ? yml_value_as_bool(utc) : false); } static bool @@ -191,6 +195,7 @@ verify_conf(keychain_t *chain, const struct yml_node *node) static const struct attr_info attrs[] = { {"date-format", false, &conf_verify_string}, {"time-format", false, &conf_verify_string}, + {"utc", false, &conf_verify_bool}, MODULE_COMMON_ATTRS, }; From e723b039ad50485dc70fecb7e59553633c28a2c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stanislav=20Ochotnick=C3=BD?= Date: Thu, 16 Sep 2021 10:24:53 +0200 Subject: [PATCH 256/611] Make libmpdclient an optional dependency Without this change yambar can't be installed/used without libmpdclient even for people who do not use MPD. Let's make this optional. We could put the optional module summary in the module meson.build but we'd have to move summary() in main meson.build so that they appear in proper order. --- CHANGELOG.md | 4 ++++ meson.build | 7 +++++++ meson_options.txt | 3 +++ modules/meson.build | 10 ++++++++-- plugin.c | 4 ++++ 5 files changed, 26 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e46d99f..8f4100b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,9 @@ ## Unreleased ### Added ### Changed + +* Made `libmpdclient` an optional dependency + ### Deprecated ### Removed ### Fixed @@ -22,6 +25,7 @@ ### Security ### Contributors +* [sochotnicky](https://codeberg.org/sochotnicky) ## 1.7.0 diff --git a/meson.build b/meson.build index b7065e6..08d21bc 100644 --- a/meson.build +++ b/meson.build @@ -119,6 +119,7 @@ yambar = executable( version, dependencies: [bar, pixman, yaml, threads, dl, tllist, fcft] + decorations + particles + modules, + c_args: [plugin_mpd_enabled? '-DPLUGIN_ENABLED_MPD':[]], build_rpath: '$ORIGIN/modules:$ORIGIN/decorations:$ORIGIN/particles', export_dynamic: true, install: true, @@ -153,3 +154,9 @@ summary( }, bool_yn: true ) + +summary( + {'Music Player Daemon (MPD)': plugin_mpd_enabled}, + section: 'Optional modules', + bool_yn: true +) diff --git a/meson_options.txt b/meson_options.txt index f293c6f..15dc3e9 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -5,3 +5,6 @@ option( option( 'core-plugins-as-shared-libraries', type: 'boolean', value: false, description: 'Compiles modules, particles and decorations as shared libraries, which are loaded on-demand') +option( + 'plugin-mpd', type: 'feature', value: 'auto', + description: 'Music Player Daemon (MPD) support') diff --git a/modules/meson.build b/modules/meson.build index dbaa7a7..6b64958 100644 --- a/modules/meson.build +++ b/modules/meson.build @@ -5,9 +5,12 @@ modules = [] alsa = dependency('alsa') udev = dependency('libudev') json = dependency('json-c') -mpd = dependency('libmpdclient') xcb_xkb = dependency('xcb-xkb', required: get_option('backend-x11')) +# Optional deps +mpd = dependency('libmpdclient', required: get_option('plugin-mpd')) +plugin_mpd_enabled = mpd.found() + # Module name -> (source-list, dep-list) mod_data = { 'alsa': [[], [m, alsa]], @@ -16,13 +19,16 @@ mod_data = { 'clock': [[], []], 'i3': [['i3-common.c', 'i3-common.h'], [dynlist, json]], 'label': [[], []], - 'mpd': [[], [mpd]], 'network': [[], []], 'removables': [[], [dynlist, udev]], 'script': [[], []], 'sway-xkb': [['i3-common.c', 'i3-common.h'], [dynlist, json]], } +if plugin_mpd_enabled + mod_data += {'mpd': [[], [mpd]]} +endif + if backend_x11 mod_data += { 'xkb': [[], [xcb_stuff, xcb_xkb]], diff --git a/plugin.c b/plugin.c index 5e0e4bc..00b0061 100644 --- a/plugin.c +++ b/plugin.c @@ -39,7 +39,9 @@ EXTERN_MODULE(clock); EXTERN_MODULE(foreign_toplevel); EXTERN_MODULE(i3); EXTERN_MODULE(label); +#if defined(PLUGIN_ENABLED_MPD) EXTERN_MODULE(mpd); +#endif EXTERN_MODULE(network); EXTERN_MODULE(removables); EXTERN_MODULE(river); @@ -117,7 +119,9 @@ init(void) #endif REGISTER_CORE_MODULE(i3, i3); REGISTER_CORE_MODULE(label, label); +#if defined(PLUGIN_ENABLED_MPD) REGISTER_CORE_MODULE(mpd, mpd); +#endif REGISTER_CORE_MODULE(network, network); REGISTER_CORE_MODULE(removables, removables); #if defined(HAVE_PLUGIN_river) From 13b5934e657505aa3800f6b81bbe756a06d3fcf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stanislav=20Ochotnick=C3=BD?= Date: Thu, 16 Sep 2021 19:22:00 +0200 Subject: [PATCH 257/611] Add MPRIS example in script module documentation --- doc/yambar-modules-script.5.scd | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/doc/yambar-modules-script.5.scd b/doc/yambar-modules-script.5.scd index 26b210f..7ba46d8 100644 --- a/doc/yambar-modules-script.5.scd +++ b/doc/yambar-modules-script.5.scd @@ -113,6 +113,37 @@ bar: content: {string: {text: "{test}"}} ``` +Another example use case of this module could be to display currently playing +song or other media from players that support MPRIS (Media Player Remote +Interfacing Specification): + +``` +bar: + center: + - script: + path: /usr/bin/playerctl + args: + - "--follow" + - "metadata" + - "-f" + - | + status|string|{{status}} + artist|string|{{artist}} + title|string|{{title}} + content: + map: + tag: status + values: + Paused: {empty: {}} + Playing: + content: {string: {text: "{artist} - {title}"}} +``` + +The above snippet runs a _playerctl_ utility in _--follow_ mode, reacting to +media updates on DBUS and outputting status, artist and title of media being +played in a format that is recognized by yambar. See _playerctl_ documentation +for more available metadata fields and control over which players get used. + # SEE ALSO *yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5) From 8709e8da3835f5954f5d8d577035f76e68f82353 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stanislav=20Ochotnick=C3=BD?= Date: Mon, 20 Sep 2021 08:36:53 +0200 Subject: [PATCH 258/611] Make default battery state as unknown When a given battery is not found or otherwise state cannot be read - we default to "unknown" state instead of discharging with incomplete data --- modules/battery.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/modules/battery.c b/modules/battery.c index 4a55141..215fa32 100644 --- a/modules/battery.c +++ b/modules/battery.c @@ -20,7 +20,7 @@ #include "../config-verify.h" #include "../plugin.h" -enum state { STATE_FULL, STATE_NOTCHARGING, STATE_CHARGING, STATE_DISCHARGING }; +enum state { STATE_FULL, STATE_NOTCHARGING, STATE_CHARGING, STATE_DISCHARGING, STATE_UNKNOWN }; struct private { struct particle *label; @@ -76,7 +76,8 @@ content(struct module *mod) assert(m->state == STATE_FULL || m->state == STATE_NOTCHARGING || m->state == STATE_CHARGING || - m->state == STATE_DISCHARGING); + m->state == STATE_DISCHARGING || + m->state == STATE_UNKNOWN); unsigned long hours; unsigned long minutes; @@ -357,7 +358,7 @@ update_status(struct module *mod) if (status == NULL) { LOG_WARN("failed to read battery state"); - state = STATE_DISCHARGING; + state = STATE_UNKNOWN; } else if (strcmp(status, "Full") == 0) state = STATE_FULL; else if (strcmp(status, "Not charging") == 0) @@ -367,10 +368,10 @@ update_status(struct module *mod) else if (strcmp(status, "Discharging") == 0) state = STATE_DISCHARGING; else if (strcmp(status, "Unknown") == 0) - state = STATE_DISCHARGING; + state = STATE_UNKNOWN; else { LOG_ERR("unrecognized battery state: %s", status); - state = STATE_DISCHARGING; + state = STATE_UNKNOWN; } LOG_DBG("capacity: %ld, energy: %ld, power: %ld, charge=%ld, current=%ld, " @@ -464,7 +465,7 @@ battery_new(const char *battery, struct particle *label, int poll_interval_secs) m->label = label; m->poll_interval = poll_interval_secs; m->battery = strdup(battery); - m->state = STATE_DISCHARGING; + m->state = STATE_UNKNOWN; struct module *mod = module_common_new(); mod->private = m; From 535d49e9c336d4443c7cf8836f87ecb32cd2b6ae Mon Sep 17 00:00:00 2001 From: Vincent Fischer Date: Sun, 19 Sep 2021 15:53:52 +0200 Subject: [PATCH 259/611] allow ramp particles to overwrite min and max --- CHANGELOG.md | 2 ++ doc/yambar-particles.5.scd | 10 ++++++++++ particles/ramp.c | 25 +++++++++++++++++++++++-- 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f4100b..3b18c5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ ## Unreleased ### Added +* ramp: can now have custom min and max values + (https://codeberg.org/dnkl/yambar/issues/103). ### Changed * Made `libmpdclient` an optional dependency diff --git a/doc/yambar-particles.5.scd b/doc/yambar-particles.5.scd index 4ded3c1..be86b56 100644 --- a/doc/yambar-particles.5.scd +++ b/doc/yambar-particles.5.scd @@ -226,6 +226,16 @@ indicator. : List of particles. Note that the tag value is *not* used as-is; its minimum and maximum values are used to map the tag's range to the particle list's range. +| min +: int +: no +: If present this will be used as a lower bound instead of the tags minimum value. + Tag values falling outside the defined range will get clamped to min/max. +| max +: int +: no +: If present this will be used as an upper bound instead of the tags maximum value. + Tag values falling outside the defined range will get clamped to min/max. ## EXAMPLES diff --git a/particles/ramp.c b/particles/ramp.c index 1d4cb59..7815d99 100644 --- a/particles/ramp.c +++ b/particles/ramp.c @@ -14,6 +14,10 @@ struct private { char *tag; + bool use_custom_min; + long min; + bool use_custom_max; + long max; struct particle **particles; size_t count; }; @@ -110,6 +114,9 @@ instantiate(const struct particle *particle, const struct tag_set *tags) long min = tag != NULL ? tag->min(tag) : 0; long max = tag != NULL ? tag->max(tag) : 0; + min = p->use_custom_min ? p->min : min; + max = p->use_custom_max ? p->max : max; + if (min > max) { LOG_WARN( "tag's minimum value is greater than its maximum: " @@ -162,13 +169,19 @@ instantiate(const struct particle *particle, const struct tag_set *tags) static struct particle * ramp_new(struct particle *common, const char *tag, - struct particle *particles[], size_t count) + struct particle *particles[], size_t count, + bool use_custom_min, long min, + bool use_custom_max, long max) { struct private *priv = calloc(1, sizeof(*priv)); priv->tag = strdup(tag); priv->particles = malloc(count * sizeof(priv->particles[0])); priv->count = count; + priv->use_custom_max = use_custom_max; + priv->max = max; + priv->use_custom_min = use_custom_min; + priv->min = min; for (size_t i = 0; i < count; i++) priv->particles[i] = particles[i]; @@ -184,6 +197,8 @@ from_conf(const struct yml_node *node, struct particle *common) { const struct yml_node *tag = yml_get_value(node, "tag"); const struct yml_node *items = yml_get_value(node, "items"); + const struct yml_node *min = yml_get_value(node, "min"); + const struct yml_node *max = yml_get_value(node, "max"); size_t count = yml_list_length(items); struct particle *parts[count]; @@ -197,7 +212,11 @@ from_conf(const struct yml_node *node, struct particle *common) it.node, (struct conf_inherit){common->font, common->foreground}); } - return ramp_new(common, yml_value_as_string(tag), parts, count); + long min_v = min != NULL ? yml_value_as_int(min) : 0; + long max_v = max != NULL ? yml_value_as_int(max) : 0; + + return ramp_new(common, yml_value_as_string(tag), parts, count, min != NULL, + min_v, max != NULL, max_v); } static bool @@ -206,6 +225,8 @@ verify_conf(keychain_t *chain, const struct yml_node *node) static const struct attr_info attrs[] = { {"tag", true, &conf_verify_string}, {"items", true, &conf_verify_particle_list_items}, + {"min", false, &conf_verify_int}, + {"max", false, &conf_verify_int}, PARTICLE_COMMON_ATTRS, }; From 76225a8366fa15b2fd45611a0e0961e72c7e7120 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 20 Sep 2021 19:30:37 +0200 Subject: [PATCH 260/611] doc: particles: line wrap --- doc/yambar-particles.5.scd | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/doc/yambar-particles.5.scd b/doc/yambar-particles.5.scd index be86b56..ab689a0 100644 --- a/doc/yambar-particles.5.scd +++ b/doc/yambar-particles.5.scd @@ -229,13 +229,15 @@ indicator. | min : int : no -: If present this will be used as a lower bound instead of the tags minimum value. - Tag values falling outside the defined range will get clamped to min/max. +: If present this will be used as a lower bound instead of the tags + minimum value. Tag values falling outside the defined range will + get clamped to min/max. | max : int : no -: If present this will be used as an upper bound instead of the tags maximum value. - Tag values falling outside the defined range will get clamped to min/max. +: If present this will be used as an upper bound instead of the tags + maximum value. Tag values falling outside the defined range will + get clamped to min/max. ## EXAMPLES From 0f389435ca753c59c74275a126d395aeb8cc416d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 30 Sep 2021 10:00:40 +0200 Subject: [PATCH 261/611] changelog: battery: unknown states map to unknown, not discharging --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b18c5e..9f06a69 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,9 @@ ### Changed * Made `libmpdclient` an optional dependency +* battery: unknown battery states are now mapped to ‘unknown’, instead + of ‘discharging’. + ### Deprecated ### Removed From 1bcf116859fd2a0f04c18bdc8ff9c2038f5f0e40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 30 Sep 2021 09:52:25 +0200 Subject: [PATCH 262/611] modules: handle udev_monitor_receive_device() returning NULL Closes #109 --- CHANGELOG.md | 3 +++ modules/backlight.c | 6 ++++-- modules/battery.c | 4 +++- modules/removables.c | 3 +++ 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f06a69..938c91e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,9 @@ * `left-margin` and `right-margin` from being rejected as invalid options. +* Crash when `udev_monitor_receive_device()` returned `NULL`. This + affected the “backlight”, “battery” and “removables” modules + (https://codeberg.org/dnkl/yambar/issues/109). ### Security diff --git a/modules/backlight.c b/modules/backlight.c index e7bca2e..292b067 100644 --- a/modules/backlight.c +++ b/modules/backlight.c @@ -189,9 +189,11 @@ run(struct module *mod) break; struct udev_device *dev = udev_monitor_receive_device(mon); - const char *sysname = udev_device_get_sysname(dev); + if (dev == NULL) + continue; - bool is_us = strcmp(sysname, m->device) == 0; + const char *sysname = udev_device_get_sysname(dev); + bool is_us = sysname != NULL && strcmp(sysname, m->device) == 0; udev_device_unref(dev); if (!is_us) diff --git a/modules/battery.c b/modules/battery.c index 215fa32..4f17de4 100644 --- a/modules/battery.c +++ b/modules/battery.c @@ -437,8 +437,10 @@ run(struct module *mod) if (fds[1].revents & POLLIN) { struct udev_device *dev = udev_monitor_receive_device(mon); - const char *sysname = udev_device_get_sysname(dev); + if (dev == NULL) + continue; + const char *sysname = udev_device_get_sysname(dev); bool is_us = sysname != NULL && strcmp(sysname, m->battery) == 0; udev_device_unref(dev); diff --git a/modules/removables.c b/modules/removables.c index a676ceb..04d2695 100644 --- a/modules/removables.c +++ b/modules/removables.c @@ -571,6 +571,9 @@ run(struct module *mod) if (fds[1].revents & POLLIN) { struct udev_device *dev = udev_monitor_receive_device(dev_mon); + if (dev == NULL) + continue; + if (handle_udev_event(mod, dev)) update = true; udev_device_unref(dev); From 26892c66b267499370afb92b4738e9488770c346 Mon Sep 17 00:00:00 2001 From: Baptiste Daroussin Date: Thu, 30 Sep 2021 17:29:46 +0200 Subject: [PATCH 263/611] headers: basename is defined under libgen.h --- modules/script.c | 1 + modules/xwindow.c | 1 + 2 files changed, 2 insertions(+) diff --git a/modules/script.c b/modules/script.c index f323635..2ddd722 100644 --- a/modules/script.c +++ b/modules/script.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include diff --git a/modules/xwindow.c b/modules/xwindow.c index 3c8655e..3925f4f 100644 --- a/modules/xwindow.c +++ b/modules/xwindow.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include From 2e6847077ea2c6b8ebad5751ed1d9398cba47bb5 Mon Sep 17 00:00:00 2001 From: Baptiste Daroussin Date: Thu, 30 Sep 2021 17:30:32 +0200 Subject: [PATCH 264/611] signal: add the signal header needed for the signal functions --- particle.c | 1 + 1 file changed, 1 insertion(+) diff --git a/particle.c b/particle.c index 468bd28..1b06f9a 100644 --- a/particle.c +++ b/particle.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include From 6a9f66b837d0220e7c005d90f0c3fafa255aa3b1 Mon Sep 17 00:00:00 2001 From: Baptiste Daroussin Date: Thu, 30 Sep 2021 17:31:12 +0200 Subject: [PATCH 265/611] meson: add the library needed for the FreeBSD support --- meson.build | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/meson.build b/meson.build index 08d21bc..1970ec4 100644 --- a/meson.build +++ b/meson.build @@ -43,7 +43,9 @@ endif # Common dependencies dl = cc.find_library('dl') m = cc.find_library('m') -threads = dependency('threads') +threads = [dependency('threads'), cc.find_library('stdthreads', required: false)] +libepoll = dependency('epoll-shim', required: false) +libinotify = dependency('libinotify', required: false) pixman = dependency('pixman-1') yaml = dependency('yaml-0.1') @@ -117,7 +119,7 @@ yambar = executable( 'tag.c', 'tag.h', 'yml.c', 'yml.h', version, - dependencies: [bar, pixman, yaml, threads, dl, tllist, fcft] + + dependencies: [bar, libepoll, libinotify, pixman, yaml, threads, dl, tllist, fcft] + decorations + particles + modules, c_args: [plugin_mpd_enabled? '-DPLUGIN_ENABLED_MPD':[]], build_rpath: '$ORIGIN/modules:$ORIGIN/decorations:$ORIGIN/particles', From bfff39b7c923fd7eaaec86fe31f621f6c6380879 Mon Sep 17 00:00:00 2001 From: Baptiste Daroussin Date: Thu, 30 Sep 2021 17:28:36 +0200 Subject: [PATCH 266/611] main: cast sig_atomic_t to long, to fix the printf portability --- main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.c b/main.c index e92e2e5..49d3985 100644 --- a/main.c +++ b/main.c @@ -374,7 +374,7 @@ main(int argc, char *const *argv) } if (aborted) - LOG_INFO("aborted: %s (%d)", strsignal(aborted), aborted); + LOG_INFO("aborted: %s (%ld)", strsignal(aborted), (long)aborted); done: /* Signal abort to other threads */ From 82c92185eabae2d1e8fc8ed1f4f1d42aa12cc427 Mon Sep 17 00:00:00 2001 From: Baptiste Daroussin Date: Thu, 30 Sep 2021 17:26:56 +0200 Subject: [PATCH 267/611] memfd: linux/memfd does not exist on FreeBSD memfd_create is defined in sys/mman.h on FreeBSD --- bar/wayland.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bar/wayland.c b/bar/wayland.c index 270b4ff..16af9fe 100644 --- a/bar/wayland.c +++ b/bar/wayland.c @@ -9,7 +9,9 @@ #include #include +#ifdef __linux__ #include +#endif #include #include From ec465ba3b3edc9e6140e75f5ef53591b0446633e Mon Sep 17 00:00:00 2001 From: Jan Beich Date: Sun, 27 Dec 2020 23:38:36 +0000 Subject: [PATCH 268/611] wayland: unbreak build without memfd_create New FreeBSD versions have memfd_create but other BSDs don't. bar/wayland.c:774:15: error: implicit declaration of function 'memfd_create' is invalid in C99 [-Werror,-Wimplicit-function-declaration] pool_fd = memfd_create("yambar-wayland-shm-buffer-pool", MFD_CLOEXEC); ^ bar/wayland.c:774:62: error: use of undeclared identifier 'MFD_CLOEXEC' pool_fd = memfd_create("yambar-wayland-shm-buffer-pool", MFD_CLOEXEC); ^ --- bar/wayland.c | 13 ++++++++++--- meson.build | 4 ++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/bar/wayland.c b/bar/wayland.c index 16af9fe..696f598 100644 --- a/bar/wayland.c +++ b/bar/wayland.c @@ -9,9 +9,7 @@ #include #include -#ifdef __linux__ -#include -#endif +#include #include #include @@ -771,7 +769,16 @@ get_buffer(struct wayland_backend *backend) pixman_image_t *pix = NULL; /* Backing memory for SHM */ +#if defined(MEMFD_CREATE) pool_fd = memfd_create("yambar-wayland-shm-buffer-pool", MFD_CLOEXEC); +#elif defined(__FreeBSD__) + // memfd_create on FreeBSD 13 is SHM_ANON without sealing support + pool_fd = shm_open(SHM_ANON, O_RDWR | O_CLOEXEC, 0600); +#else + char name[] = "/tmp/yambar-wayland-shm-buffer-pool-XXXXXX"; + pool_fd = mkostemp(name, O_CLOEXEC); + unlink(name); +#endif if (pool_fd == -1) { LOG_ERRNO("failed to create SHM backing memory file"); goto err; diff --git a/meson.build b/meson.build index 1970ec4..ed4d0d1 100644 --- a/meson.build +++ b/meson.build @@ -12,6 +12,10 @@ plugs_as_libs = get_option('core-plugins-as-shared-libraries') cc = meson.get_compiler('c') +if cc.has_function('memfd_create') + add_project_arguments('-DMEMFD_CREATE', language: 'c') +endif + # Compute the relative path used by compiler invocations. source_root = meson.current_source_dir().split('/') build_root = meson.build_root().split('/') From 03476e93605c5f9ef91e2bb58375bbae81c3cd09 Mon Sep 17 00:00:00 2001 From: Jan Beich Date: Sun, 3 Oct 2021 23:03:14 +0000 Subject: [PATCH 269/611] yml: silence unused variable warning with Clang 13 yml.c:383:9: error: variable 'indent' set but not used [-Werror,-Wunused-but-set-variable] int indent = 0; ^ --- yml.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/yml.c b/yml.c index 52370ef..6afa8d6 100644 --- a/yml.c +++ b/yml.c @@ -9,6 +9,8 @@ #include #include +#define UNUSED __attribute__((unused)) + enum yml_error { YML_ERR_NONE, YML_ERR_DUPLICATE_KEY, @@ -380,7 +382,7 @@ yml_load(FILE *yml, char **error) yaml_parser_set_input_file(&yaml, yml); bool done = false; - int indent = 0; + int indent UNUSED = 0; struct yml_node *root = malloc(sizeof(*root)); root->type = ROOT; From 77303e817301aa527ffb09e37a5bd0217b1a6519 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 10 Oct 2021 18:38:58 +0200 Subject: [PATCH 270/611] ci: codeberg CI --- .woodpecker.yml | 108 ++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 2 + 2 files changed, 110 insertions(+) create mode 100644 .woodpecker.yml diff --git a/.woodpecker.yml b/.woodpecker.yml new file mode 100644 index 0000000..e8adf8b --- /dev/null +++ b/.woodpecker.yml @@ -0,0 +1,108 @@ +clone: + git: + image: plugins/git + tags: true + +pipeline: + codespell: + image: alpine:latest + commands: + - apk add python3 + - apk add py3-pip + - pip install codespell + - codespell README.md CHANGELOG.md *.c *.h doc/*.scd + + subprojects: + image: alpine:latest + commands: + - apk add git + - mkdir -p subprojects && cd subprojects + - git clone https://codeberg.org/dnkl/tllist.git + - git clone https://codeberg.org/dnkl/fcft.git + - cd .. + + x64: + group: build + image: alpine:latest + commands: + - apk update + - apk add musl-dev eudev-libs eudev-dev linux-headers meson ninja gcc scdoc + - apk add pixman-dev freetype-dev fontconfig-dev + - apk add libxcb-dev xcb-util-wm-dev xcb-util-cursor-dev yaml-dev + - apk add wayland-dev wayland-protocols wlroots-dev + - apk add json-c-dev libmpdclient-dev alsa-lib-dev + - apk add ttf-dejavu + - apk add git + + # Debug + - apk add gcovr + - mkdir -p bld/debug-x64 + - cd bld/debug-x64 + - meson --buildtype=debug -Db_coverage=true ../.. + - ninja -k0 + - meson test --print-errorlogs + - ninja coverage-html + - mv meson-logs/coveragereport ../../coverage + - ninja coverage-text + - tail -2 meson-logs/coverage.txt + - cd ../.. + + # Release + - mkdir -p bld/release-x64 + - cd bld/release-x64 + - meson --buildtype=minsize ../../ + - ninja -k0 + - meson test --print-errorlogs + - cd ../.. + + # X11 only + - mkdir -p bld/x11-only + - cd bld/x11-only + - meson --buildtype=debug -Dbackend-x11=enabled -Dbackend-wayland=disabled ../../ + - ninja -k0 + - meson test --print-errorlogs + - cd ../.. + + # Wayland only + - mkdir -p bld/wayland-only + - cd bld/wayland-only + - meson --buildtype=debug -Dbackend-x11=disabled -Dbackend-wayland=enabled ../../ + - ninja -k0 + - meson test --print-errorlogs + - cd ../.. + + x86: + group: build + image: i386/alpine:latest + commands: + - apk add musl-dev eudev-libs eudev-dev linux-headers meson ninja gcc scdoc + - apk add pixman-dev freetype-dev fontconfig-dev + - apk add libxcb-dev xcb-util-wm-dev xcb-util-cursor-dev yaml-dev + - apk add wayland-dev wayland-protocols wlroots-dev + - apk add json-c-dev libmpdclient-dev alsa-lib-dev + - apk add ttf-dejavu + - apk add git + + # Debug + - mkdir -p bld/debug-x86 + - cd bld/debug-x86 + - meson --buildtype=debug ../../ + - ninja -k0 + - meson test --print-errorlogs + - cd ../.. + + # Release + - mkdir -p bld/release-x86 + - cd bld/release-x86 + - meson --buildtype=minsize ../../ + - ninja -k0 + - meson test --print-errorlogs + - cd ../.. + + # Plugins as shared modules + - mkdir -p bld/shared-modules + - cd bld/shared-modules + - meson --buildtype=debug -Dcore-plugins-as-shared-libraries=true ../../ + - ninja -k0 + - meson test --print-errorlogs + - cd ../.. diff --git a/README.md b/README.md index f61d195..5251119 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +[![CI status](https://ci.codeberg.org/api/badges/dnkl/yambar/status.svg)](https://ci.codeberg.org/dnkl/yambar) + # Yambar [![Packaging status](https://repology.org/badge/vertical-allrepos/yambar.svg)](https://repology.org/project/yambar/versions) From 6250360c584a9d23be259383d1f1ac907985c8bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 10 Oct 2021 19:05:47 +0200 Subject: [PATCH 271/611] ci: limit builds to the master branch (and pulls targeting it) --- .woodpecker.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.woodpecker.yml b/.woodpecker.yml index e8adf8b..5f95979 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -5,6 +5,7 @@ clone: pipeline: codespell: + when: { branch: master } image: alpine:latest commands: - apk add python3 @@ -13,6 +14,7 @@ pipeline: - codespell README.md CHANGELOG.md *.c *.h doc/*.scd subprojects: + when: { branch: master } image: alpine:latest commands: - apk add git @@ -22,6 +24,7 @@ pipeline: - cd .. x64: + when: { branch: master } group: build image: alpine:latest commands: @@ -72,6 +75,7 @@ pipeline: - cd ../.. x86: + when: { branch: master } group: build image: i386/alpine:latest commands: From 9a1371b96a4bb7f5bddb65d48d9d4db8063e3eb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 11 Oct 2021 20:21:00 +0200 Subject: [PATCH 272/611] generate-version: handle git repo not having any tags --- .woodpecker.yml | 5 ----- generate-version.sh | 9 ++++++++- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/.woodpecker.yml b/.woodpecker.yml index 5f95979..294bee2 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -1,8 +1,3 @@ -clone: - git: - image: plugins/git - tags: true - pipeline: codespell: when: { branch: master } diff --git a/generate-version.sh b/generate-version.sh index 458f4a1..8ac3b03 100755 --- a/generate-version.sh +++ b/generate-version.sh @@ -13,7 +13,14 @@ out_file=${3} if [ -d "${src_dir}/.git" ] && command -v git > /dev/null; then workdir=$(pwd) cd "${src_dir}" - git_version=$(git describe --always --tags) + + if git describe --tags > /dev/null 2>&1; then + git_version=$(git describe --always --tags) + else + # No tags available, happens in e.g. CI builds + git_version="${default_version}" + fi + git_branch=$(git rev-parse --abbrev-ref HEAD) cd "${workdir}" From 9fe8ef2574318773562417bf23b15889be32e857 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 11 Oct 2021 20:21:10 +0200 Subject: [PATCH 273/611] =?UTF-8?q?ci:=20run=20=E2=80=9Cyambar=20--version?= =?UTF-8?q?=E2=80=9D=20after=20each=20build?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .woodpecker.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.woodpecker.yml b/.woodpecker.yml index 294bee2..875213a 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -43,6 +43,7 @@ pipeline: - mv meson-logs/coveragereport ../../coverage - ninja coverage-text - tail -2 meson-logs/coverage.txt + - ./yambar --version - cd ../.. # Release @@ -51,6 +52,7 @@ pipeline: - meson --buildtype=minsize ../../ - ninja -k0 - meson test --print-errorlogs + - ./yambar --version - cd ../.. # X11 only @@ -59,6 +61,7 @@ pipeline: - meson --buildtype=debug -Dbackend-x11=enabled -Dbackend-wayland=disabled ../../ - ninja -k0 - meson test --print-errorlogs + - ./yambar --version - cd ../.. # Wayland only @@ -67,6 +70,7 @@ pipeline: - meson --buildtype=debug -Dbackend-x11=disabled -Dbackend-wayland=enabled ../../ - ninja -k0 - meson test --print-errorlogs + - ./yambar --version - cd ../.. x86: @@ -88,6 +92,7 @@ pipeline: - meson --buildtype=debug ../../ - ninja -k0 - meson test --print-errorlogs + - ./yambar --version - cd ../.. # Release @@ -96,6 +101,7 @@ pipeline: - meson --buildtype=minsize ../../ - ninja -k0 - meson test --print-errorlogs + - ./yambar --version - cd ../.. # Plugins as shared modules @@ -104,4 +110,5 @@ pipeline: - meson --buildtype=debug -Dcore-plugins-as-shared-libraries=true ../../ - ninja -k0 - meson test --print-errorlogs + - ./yambar --version - cd ../.. From 515b36da0d686d6845b315268622af578d49b091 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 17 Oct 2021 09:52:28 +0200 Subject: [PATCH 274/611] foreign-toplevel: refresh the bar when a top-level is closed Fixes an issue where the last active window is still being displayed in the bar after the corresponding top-level has been closed. Reported here: https://github.com/johanmalm/labwc/issues/73#issuecomment-945007028 --- CHANGELOG.md | 1 + modules/foreign-toplevel.c | 3 +++ 2 files changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 938c91e..2ea91e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ * Crash when `udev_monitor_receive_device()` returned `NULL`. This affected the “backlight”, “battery” and “removables” modules (https://codeberg.org/dnkl/yambar/issues/109). +* foreign-toplevel: update bar when a top-level is closed. ### Security diff --git a/modules/foreign-toplevel.c b/modules/foreign-toplevel.c index ef45b3b..2997042 100644 --- a/modules/foreign-toplevel.c +++ b/modules/foreign-toplevel.c @@ -372,6 +372,9 @@ closed(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle) } } mtx_unlock(&mod->lock); + + const struct bar *bar = mod->bar; + bar->refresh(bar); } static void From 94a0154c2315fa22985ddbff5aebef3d43e260ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 17 Oct 2021 16:48:09 +0200 Subject: [PATCH 275/611] bar: refresh before starting the main loop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This ensures the bar surface gets mapped, and a background + border rendered, even though no module “refreshes” it during initialization. Closes #116 --- CHANGELOG.md | 4 ++++ bar/bar.c | 1 + 2 files changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ea91e2..6990b6b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,8 @@ ### Added * ramp: can now have custom min and max values (https://codeberg.org/dnkl/yambar/issues/103). + + ### Changed * Made `libmpdclient` an optional dependency @@ -29,6 +31,8 @@ affected the “backlight”, “battery” and “removables” modules (https://codeberg.org/dnkl/yambar/issues/109). * foreign-toplevel: update bar when a top-level is closed. +* Bar not being mapped on an output before at least one module has + “refreshed” it (https://codeberg.org/dnkl/yambar/issues/116). ### Security diff --git a/bar/bar.c b/bar/bar.c index 2399368..8b19a43 100644 --- a/bar/bar.c +++ b/bar/bar.c @@ -323,6 +323,7 @@ run(struct bar *_bar) LOG_DBG("all modules started"); + refresh(_bar); bar->backend.iface->loop(_bar, &expose, &on_mouse); LOG_DBG("shutting down"); From 2b670ea612a6d038d262a6aa3ba55f5cb2e84b7c Mon Sep 17 00:00:00 2001 From: mz Date: Tue, 19 Oct 2021 10:11:46 +0200 Subject: [PATCH 276/611] remove an extra space Remove the extra space due to `did not find expected key while parsing a block mapping` issue --- examples/configurations/laptop.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/configurations/laptop.conf b/examples/configurations/laptop.conf index eab6c46..0c31b20 100644 --- a/examples/configurations/laptop.conf +++ b/examples/configurations/laptop.conf @@ -222,7 +222,7 @@ bar: default: - string: {text: , font: *awesome} - string: {text: "{ssid}"} - values: + values: "": - string: {text: , font: *awesome, foreground: ffffff66} - string: {text: "{ssid}", foreground: ffffff66} From 4f3aa8c6b07381ea459a5d6c12dcf2774aa002eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 22 Oct 2021 18:05:20 +0200 Subject: [PATCH 277/611] =?UTF-8?q?bar:=20do=20a=20synchronous=20=E2=80=9C?= =?UTF-8?q?refresh=E2=80=9D=20*before*=20starting=20the=20modules?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This ensures the surface has been mapped, and our “current” output is known. This fixes an issue where modules depending on the current output being known failed to update correctly during startup. --- bar/bar.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bar/bar.c b/bar/bar.c index 8b19a43..4f1895f 100644 --- a/bar/bar.c +++ b/bar/bar.c @@ -293,6 +293,7 @@ run(struct bar *_bar) } set_cursor(_bar, "left_ptr"); + expose(_bar); /* Start modules */ thrd_t thrd_left[max(bar->left.count, 1)]; @@ -323,7 +324,6 @@ run(struct bar *_bar) LOG_DBG("all modules started"); - refresh(_bar); bar->backend.iface->loop(_bar, &expose, &on_mouse); LOG_DBG("shutting down"); From fee0b91174e4a9117b1955a51bbaaf785348bcd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 22 Oct 2021 18:06:19 +0200 Subject: [PATCH 278/611] =?UTF-8?q?bar/wayland:=20remove=20unused=20member?= =?UTF-8?q?=20=E2=80=98bar=5Fexpose=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bar/wayland.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/bar/wayland.c b/bar/wayland.c index 696f598..4f69135 100644 --- a/bar/wayland.c +++ b/bar/wayland.c @@ -116,7 +116,6 @@ struct wayland_backend { double aggregated_scroll; bool have_discrete; - void (*bar_expose)(const struct bar *bar); void (*bar_on_mouse)(struct bar *bar, enum mouse_event event, enum mouse_button btn, int x, int y); }; @@ -1111,7 +1110,6 @@ loop(struct bar *_bar, pthread_setname_np(pthread_self(), "bar(wayland)"); - backend->bar_expose = expose; backend->bar_on_mouse = on_mouse; while (wl_display_prepare_read(backend->display) != 0) From 939b81a4ea72896a1b9f4f21653bbdc26be64e07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 22 Oct 2021 18:06:43 +0200 Subject: [PATCH 279/611] bar/wayland: create comm pipe with CLOEXEC | NONBLOCK --- bar/wayland.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bar/wayland.c b/bar/wayland.c index 4f69135..02e9ad6 100644 --- a/bar/wayland.c +++ b/bar/wayland.c @@ -1029,7 +1029,7 @@ setup(struct bar *_bar) assert(backend->monitor == NULL || backend->width / backend->monitor->scale <= backend->monitor->width_px); - if (pipe(backend->pipe_fds) == -1) { + if (pipe2(backend->pipe_fds, O_CLOEXEC | O_NONBLOCK) == -1) { LOG_ERRNO("failed to create pipe"); return false; } From bd44e82ecaefbd2056eeb58bba48209b94b28c75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 22 Oct 2021 18:07:14 +0200 Subject: [PATCH 280/611] =?UTF-8?q?bar/wayland:=20coalesce=20=E2=80=9Crefr?= =?UTF-8?q?esh=E2=80=9D=20commands?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reduces the number of repaints primarily during startup, but also when e.g. switching workspace. --- bar/wayland.c | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/bar/wayland.c b/bar/wayland.c index 02e9ad6..90d1103 100644 --- a/bar/wayland.c +++ b/bar/wayland.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -1139,16 +1140,30 @@ loop(struct bar *_bar, } if (fds[2].revents & POLLIN) { - uint8_t command; - if (read(backend->pipe_fds[0], &command, sizeof(command)) - != sizeof(command)) - { - LOG_ERRNO("failed to read from command pipe"); - break; + bool do_expose = false; + + /* Coalesce “refresh” commands */ + size_t count = 0; + while (true) { + uint8_t command; + ssize_t r = read(backend->pipe_fds[0], &command, sizeof(command)); + if (r < 0 && errno == EAGAIN) + break; + if (r != sizeof(command)) { + LOG_ERRNO("failed to read from command pipe"); + break; + } + + assert(command == 1); + if (command == 1) { + count++; + do_expose = true; + } } - assert(command == 1); - expose(_bar); + LOG_DBG("coalesced %zu expose commands", count); + if (do_expose) + expose(_bar); } if (fds[1].revents & POLLIN) { From cad9dd8efdc67de7c35e0109f41726f8d38d2a6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 23 Oct 2021 16:03:21 +0200 Subject: [PATCH 281/611] ci: also build release branches --- .woodpecker.yml | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/.woodpecker.yml b/.woodpecker.yml index 875213a..4ea84f1 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -1,6 +1,9 @@ pipeline: codespell: - when: { branch: master } + when: + branch: + - master + - releases/* image: alpine:latest commands: - apk add python3 @@ -9,7 +12,10 @@ pipeline: - codespell README.md CHANGELOG.md *.c *.h doc/*.scd subprojects: - when: { branch: master } + when: + branch: + - master + - releases/* image: alpine:latest commands: - apk add git @@ -19,7 +25,10 @@ pipeline: - cd .. x64: - when: { branch: master } + when: + branch: + - master + - releases/* group: build image: alpine:latest commands: @@ -74,7 +83,10 @@ pipeline: - cd ../.. x86: - when: { branch: master } + when: + branch: + - master + - releases/* group: build image: i386/alpine:latest commands: From 7d715d81a8e89bfd271c710cd89bb0181dc54d18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 24 Oct 2021 17:24:15 +0200 Subject: [PATCH 282/611] =?UTF-8?q?example:=20conf:=20use=20foreign-toplev?= =?UTF-8?q?el=20instead=20of=20i3=E2=80=99s=20=E2=80=98current=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/configurations/laptop.conf | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/examples/configurations/laptop.conf b/examples/configurations/laptop.conf index 0c31b20..65143cc 100644 --- a/examples/configurations/laptop.conf +++ b/examples/configurations/laptop.conf @@ -105,23 +105,17 @@ bar: unfocused: {string: {<<: *music}} invisible: {string: {<<: [*music, *invisible]}} urgent: {string: {<<: [*music, *urgent]}} - current: - map: - left-margin: 7 - tag: application - values: - "": - - map: {<<: *i3_mode} - - string: {text: "{title}"} - default: - list: - spacing: 0 - items: - - map: {<<: *i3_mode} - - string: {text: "{application}", max: 10, foreground: ffa0a0ff} - - string: {text: ": "} - - string: {text: "{title}", max: 35} + - foreign-toplevel: + content: + map: + tag: activated + values: + false: {empty: {}} + true: + - string: {text: "{app-id}", foreground: ffa0a0ff} + - string: {text: ": "} + - string: {text: "{title}"} center: - mpd: host: /run/mpd/socket From 4e96dbd7f7fefed3a7a79e7cabc0c3c08bd5623a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 24 Oct 2021 17:35:15 +0200 Subject: [PATCH 283/611] =?UTF-8?q?network:=20don=E2=80=99t=20send=20nl802?= =?UTF-8?q?11=20request=20if=20we=20don=E2=80=99t=20have=20a=20family-id?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes an issue where we sometimes failed to retrieve the SSID; we sent one initial request before the family ID had been set. Then, we received the family ID and tried to send another request. However, if the first request was still in progress, the second request was never made. Since the first request lacked the family ID, that response didn’t contain anything useful. --- modules/network.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/network.c b/modules/network.c index bf32c57..117f687 100644 --- a/modules/network.c +++ b/modules/network.c @@ -310,6 +310,9 @@ send_nl80211_request(struct private *m, uint8_t cmd, uint16_t flags, uint32_t se if (m->ifindex < 0) return false; + if (m->nl80211.family_id == (uint16_t)-1) + return false; + const struct { struct nlmsghdr hdr; struct { From 9ffd305b594cf4767da3af8d33da9e02efb67b7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 24 Oct 2021 17:47:54 +0200 Subject: [PATCH 284/611] network: must re-send station request when we receive family ID The get station request also fails if the family ID is invalid. --- modules/network.c | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/network.c b/modules/network.c index 117f687..06ada9b 100644 --- a/modules/network.c +++ b/modules/network.c @@ -695,6 +695,7 @@ handle_genl_ctrl(struct module *mod, uint16_t type, bool nested, case CTRL_ATTR_FAMILY_ID: { m->nl80211.family_id = *(const uint16_t *)payload; send_nl80211_get_interface(m); + send_nl80211_get_station(m); break; } From 31f160141e0250e3b4c090e6a33d5a53cd85c5da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 24 Oct 2021 17:50:10 +0200 Subject: [PATCH 285/611] changelog: network: failure to retrieve wireless attributes --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6990b6b..84f80e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,8 @@ * foreign-toplevel: update bar when a top-level is closed. * Bar not being mapped on an output before at least one module has “refreshed” it (https://codeberg.org/dnkl/yambar/issues/116). +* network: failure to retrieve wireless attributes (SSID, RX/TX + bitrate, signal strength etc). ### Security From f0782d5124f9f6c72a5e07c2dfdb6d3d98cc9e45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 22 Oct 2021 18:08:39 +0200 Subject: [PATCH 286/611] =?UTF-8?q?deco:=20new=20decoration,=20=E2=80=98bo?= =?UTF-8?q?rder=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Kind of like “underline”, but draws a border around the entire particle. --- CHANGELOG.md | 1 + decorations/border.c | 93 +++++++++++++++++++++++++++++++++++++++++ decorations/meson.build | 2 +- plugin.c | 2 + 4 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 decorations/border.c diff --git a/CHANGELOG.md b/CHANGELOG.md index 84f80e3..d102c7d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ ### Added * ramp: can now have custom min and max values (https://codeberg.org/dnkl/yambar/issues/103). +* border: new decoration. ### Changed diff --git a/decorations/border.c b/decorations/border.c new file mode 100644 index 0000000..ac50c33 --- /dev/null +++ b/decorations/border.c @@ -0,0 +1,93 @@ +#include + +#include "../config.h" +#include "../config-verify.h" +#include "../decoration.h" +#include "../plugin.h" + +#define LOG_MODULE "border" +#define LOG_ENABLE_DBG 0 +#include "../log.h" + +#define min(x, y) ((x) < (y) ? (x) : (y)) +#define max(x, y) ((x) > (y) ? (x) : (y)) + +struct private { + pixman_color_t color; + int size; +}; + +static void +destroy(struct deco *deco) +{ + struct private *d = deco->private; + free(d); + free(deco); +} + +static void +expose(const struct deco *deco, pixman_image_t *pix, int x, int y, int width, int height) +{ + const struct private *d = deco->private; + pixman_image_fill_rectangles( + PIXMAN_OP_OVER, pix, &d->color, 4, + (pixman_rectangle16_t []){ + /* Top */ + {x, y, width, min(d->size, height)}, + + /* Bottom */ + {x, max(y + height - d->size, y), width, min(d->size, height)}, + + /* Left */ + {x, y, min(d->size, width), height}, + + /* Right */ + {max(x + width - d->size, x), y, min(d->size, width), height}, + }); +} + +static struct deco * +border_new(pixman_color_t color, int size) +{ + struct private *priv = calloc(1, sizeof(*priv)); + priv->color = color; + priv->size = size; + + struct deco *deco = calloc(1, sizeof(*deco)); + deco->private = priv; + deco->expose = &expose; + deco->destroy = &destroy; + + return deco; +} + +static struct deco * +from_conf(const struct yml_node *node) +{ + const struct yml_node *color = yml_get_value(node, "color"); + const struct yml_node *size = yml_get_value(node, "size"); + return border_new( + conf_to_color(color), + size != NULL ? yml_value_as_int(size) : 1); +} + +static bool +verify_conf(keychain_t *chain, const struct yml_node *node) +{ + static const struct attr_info attrs[] = { + {"color", true, &conf_verify_color}, + {"size", false, &conf_verify_int}, + DECORATION_COMMON_ATTRS, + }; + + return conf_verify_dict(chain, node, attrs); +} + +const struct deco_iface deco_border_iface = { + .verify_conf = &verify_conf, + .from_conf = &from_conf, +}; + +#if defined(CORE_PLUGINS_AS_SHARED_LIBRARIES) +extern const struct deco_iface iface __attribute__((weak, alias("deco_border_iface"))); +#endif diff --git a/decorations/meson.build b/decorations/meson.build index 708267e..e8b289c 100644 --- a/decorations/meson.build +++ b/decorations/meson.build @@ -1,7 +1,7 @@ deco_sdk = declare_dependency(dependencies: [pixman, tllist, fcft]) decorations = [] -foreach deco : ['background', 'stack', 'underline'] +foreach deco : ['background', 'border', 'stack', 'underline'] if plugs_as_libs shared_module('@0@'.format(deco), '@0@.c'.format(deco), dependencies: deco_sdk, diff --git a/plugin.c b/plugin.c index 00b0061..5f31b2e 100644 --- a/plugin.c +++ b/plugin.c @@ -58,6 +58,7 @@ EXTERN_PARTICLE(ramp); EXTERN_PARTICLE(string); EXTERN_DECORATION(background); +EXTERN_DECORATION(border); EXTERN_DECORATION(stack); EXTERN_DECORATION(underline); @@ -144,6 +145,7 @@ init(void) REGISTER_CORE_PARTICLE(string, string); REGISTER_CORE_DECORATION(background, background); + REGISTER_CORE_DECORATION(border, border); REGISTER_CORE_DECORATION(stack, stack); REGISTER_CORE_DECORATION(underline, underline); From 134141b7c58844494da2ae488e394f7562363062 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 23 Oct 2021 12:17:00 +0200 Subject: [PATCH 287/611] =?UTF-8?q?doc:=20decorations:=20document=20the=20?= =?UTF-8?q?new=20=E2=80=98border=E2=80=99=20decoration?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/yambar-decorations.5.scd | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/doc/yambar-decorations.5.scd b/doc/yambar-decorations.5.scd index 48dcc87..8c972f2 100644 --- a/doc/yambar-decorations.5.scd +++ b/doc/yambar-decorations.5.scd @@ -70,6 +70,39 @@ content: color: ff0000ff ``` + +# BORDER + +This decoration renders a border of configurable size (i.e border +width) around the particle. + +## CONFIGURATION + +[[ *Name* +:[ *Type* +:[ *Req* +:[ *Description* +| color +: color +: yes +: The color of the line. See *yambar*(5) for format. +| size +: int +: no +: Border width, in pixels. Defaults to 1px. + +## EXAMPLES + +``` +content: + string: + deco: + border: + size: 2 + color: ff0000ff +``` + + # STACK This particles combines multiple decorations. From 66ec7a85a19b018b95f310a17d1baf5c864ce3b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 24 Oct 2021 18:34:43 +0200 Subject: [PATCH 288/611] =?UTF-8?q?example:=20conf:=20foreign-toplevel:=20?= =?UTF-8?q?merge=20=E2=80=98:=20=E2=80=99=20and=20=E2=80=98{title}?= =?UTF-8?q?=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/configurations/laptop.conf | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/configurations/laptop.conf b/examples/configurations/laptop.conf index 65143cc..19d0bb4 100644 --- a/examples/configurations/laptop.conf +++ b/examples/configurations/laptop.conf @@ -114,8 +114,7 @@ bar: false: {empty: {}} true: - string: {text: "{app-id}", foreground: ffa0a0ff} - - string: {text: ": "} - - string: {text: "{title}"} + - string: {text: ": {title}"} center: - mpd: host: /run/mpd/socket From 832411250433fe73acdec0b09d9654c8725b729a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 30 Oct 2021 18:45:43 +0200 Subject: [PATCH 289/611] =?UTF-8?q?example:=20battery:=20map=20=E2=80=98un?= =?UTF-8?q?known=E2=80=99=20to=20=E2=80=98discharging=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Related to #123 --- examples/configurations/laptop.conf | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/examples/configurations/laptop.conf b/examples/configurations/laptop.conf index 19d0bb4..3a6c439 100644 --- a/examples/configurations/laptop.conf +++ b/examples/configurations/laptop.conf @@ -248,11 +248,10 @@ bar: - battery: name: BAT0 poll-interval: 30 - content: - map: - tag: state - values: - discharging: + anchors: + discharging: &discharging + list: + items: - ramp: tag: capacity items: @@ -267,6 +266,14 @@ bar: - string: {text: , font: *awesome} - string: {text: , foreground: 00ff00ff, font: *awesome} - string: {text: "{capacity}% {estimate}"} + content: + map: + tag: state + values: + unknown: + <<: *discharging + discharging: + <<: *discharging charging: - string: {text: , foreground: 00ff00ff, font: *awesome} - string: {text: "{capacity}% {estimate}"} From 58038a42361b60ee9c9d72612c1ab96c5a9baf4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 31 Oct 2021 21:07:09 +0100 Subject: [PATCH 290/611] doc: battery: some batteries enter "unknown" under normal operation --- doc/yambar-modules-battery.5.scd | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/doc/yambar-modules-battery.5.scd b/doc/yambar-modules-battery.5.scd index 42b5e00..3d55ed9 100644 --- a/doc/yambar-modules-battery.5.scd +++ b/doc/yambar-modules-battery.5.scd @@ -8,6 +8,17 @@ battery - This module reads battery status This module reads battery status from _/sys/class/power_supply_ and uses *udev* to monitor for changes. +Note that it is common (and "normal") for batteries to be in the state +*unknown* under certain conditions. + +For example, some have been seen to enter the *unknown* state when +charging and the capacity reaches ~90%. The battery then stays in +*unknown*, rather than *charging*, until it has been fully charged and +enters the state *full*. + +This does not happen with all batteries, and other batteries may enter +the state *unknown* under other conditions. + # TAGS [[ *Name* From d40220e51196e517c97602ec7a236236c4701481 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 15 Nov 2021 18:06:10 +0100 Subject: [PATCH 291/611] bar/wayland: handle failure to set initial size in setup() --- bar/wayland.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bar/wayland.c b/bar/wayland.c index 90d1103..a5a6731 100644 --- a/bar/wayland.c +++ b/bar/wayland.c @@ -1025,7 +1025,8 @@ setup(struct bar *_bar) ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | top_or_bottom); - update_size(backend); + if (!update_size(backend)) + return false; assert(backend->monitor == NULL || backend->width / backend->monitor->scale <= backend->monitor->width_px); From 11bb45aa878b4cc7a5ddc5ed0640e27403fddc30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 15 Nov 2021 18:15:52 +0100 Subject: [PATCH 292/611] doc: script: add missing column in options table --- doc/yambar-modules-script.5.scd | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/yambar-modules-script.5.scd b/doc/yambar-modules-script.5.scd index 7ba46d8..c1f31c6 100644 --- a/doc/yambar-modules-script.5.scd +++ b/doc/yambar-modules-script.5.scd @@ -77,6 +77,7 @@ User defined. : Arguments to pass to the script/binary. | poll-interval : integer +: no : Number of seconds between each script run. If unset, continuous mode is used. From 72056c50cfec93e1ac41ded7ec4f177f7af5a0b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 15 Nov 2021 18:16:15 +0100 Subject: [PATCH 293/611] config-verify: add conf_verify_unsigned() Like conf_verify_int(), but also requires the integer to be >= 0 --- config-verify.c | 11 +++++++++++ config-verify.h | 1 + 2 files changed, 12 insertions(+) diff --git a/config-verify.c b/config-verify.c index 63efef3..eef8059 100644 --- a/config-verify.c +++ b/config-verify.c @@ -50,6 +50,17 @@ conf_verify_int(keychain_t *chain, const struct yml_node *node) return false; } +bool +conf_verify_unsigned(keychain_t *chain, const struct yml_node *node) +{ + if (yml_value_is_int(node) && yml_value_as_int(node) >= 0) + return true; + + LOG_ERR("%s: value is not a positive integer: '%s'", + conf_err_prefix(chain, node), yml_value_as_string(node)); + return false; +} + bool conf_verify_bool(keychain_t *chain, const struct yml_node *node) { diff --git a/config-verify.h b/config-verify.h index 5afe3f6..8e5ab29 100644 --- a/config-verify.h +++ b/config-verify.h @@ -32,6 +32,7 @@ const char *conf_err_prefix( bool conf_verify_string(keychain_t *chain, const struct yml_node *node); bool conf_verify_int(keychain_t *chain, const struct yml_node *node); +bool conf_verify_unsigned(keychain_t *chain, const struct yml_node *node); bool conf_verify_bool(keychain_t *chain, const struct yml_node *node); bool conf_verify_enum(keychain_t *chain, const struct yml_node *node, From 23f12a65b2ef168d58ddf97a413220b9f4a811e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 15 Nov 2021 18:16:37 +0100 Subject: [PATCH 294/611] conf: bar/border: verify: all integer options are supposed to be unsigned --- config-verify.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/config-verify.c b/config-verify.c index eef8059..b92fb71 100644 --- a/config-verify.c +++ b/config-verify.c @@ -392,17 +392,17 @@ static bool verify_bar_border(keychain_t *chain, const struct yml_node *node) { static const struct attr_info attrs[] = { - {"width", false, &conf_verify_int}, - {"left-width", false, &conf_verify_int}, - {"right-width", false, &conf_verify_int}, - {"top-width", false, &conf_verify_int}, - {"bottom-width", false, &conf_verify_int}, + {"width", false, &conf_verify_unsigned}, + {"left-width", false, &conf_verify_unsigned}, + {"right-width", false, &conf_verify_unsigned}, + {"top-width", false, &conf_verify_unsigned}, + {"bottom-width", false, &conf_verify_unsigned}, {"color", false, &conf_verify_color}, - {"margin", false, &conf_verify_int}, - {"left-margin", false, &conf_verify_int}, - {"right-margin", false, &conf_verify_int}, - {"top-margin", false, &conf_verify_int}, - {"bottom-margin", false, &conf_verify_int}, + {"margin", false, &conf_verify_unsigned}, + {"left-margin", false, &conf_verify_unsigned}, + {"right-margin", false, &conf_verify_unsigned}, + {"top-margin", false, &conf_verify_unsigned}, + {"bottom-margin", false, &conf_verify_unsigned}, {NULL, false, NULL}, }; @@ -433,20 +433,20 @@ conf_verify_bar(const struct yml_node *bar) chain_push(&chain, "bar"); static const struct attr_info attrs[] = { - {"height", true, &conf_verify_int}, + {"height", true, &conf_verify_unsigned}, {"location", true, &verify_bar_location}, {"background", true, &conf_verify_color}, {"monitor", false, &conf_verify_string}, {"layer", false, &verify_bar_layer}, - {"spacing", false, &conf_verify_int}, - {"left-spacing", false, &conf_verify_int}, - {"right-spacing", false, &conf_verify_int}, + {"spacing", false, &conf_verify_unsigned}, + {"left-spacing", false, &conf_verify_unsigned}, + {"right-spacing", false, &conf_verify_unsigned}, - {"margin", false, &conf_verify_int}, - {"left-margin", false, &conf_verify_int}, - {"right-margin", false, &conf_verify_int}, + {"margin", false, &conf_verify_unsigned}, + {"left-margin", false, &conf_verify_unsigned}, + {"right-margin", false, &conf_verify_unsigned}, {"border", false, &verify_bar_border}, {"font", false, &conf_verify_font}, @@ -456,7 +456,7 @@ conf_verify_bar(const struct yml_node *bar) {"center", false, &verify_module_list}, {"right", false, &verify_module_list}, - {"trackpad-sensitivity", false, &conf_verify_int}, + {"trackpad-sensitivity", false, &conf_verify_unsigned}, {NULL, false, NULL}, }; From b6931c6ed0bd99f486201ea6d8b1504d6966e317 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 15 Nov 2021 18:17:12 +0100 Subject: [PATCH 295/611] decos: verify: all integer options are supposed to be unsigned --- decorations/border.c | 2 +- decorations/underline.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/decorations/border.c b/decorations/border.c index ac50c33..5868e88 100644 --- a/decorations/border.c +++ b/decorations/border.c @@ -76,7 +76,7 @@ verify_conf(keychain_t *chain, const struct yml_node *node) { static const struct attr_info attrs[] = { {"color", true, &conf_verify_color}, - {"size", false, &conf_verify_int}, + {"size", false, &conf_verify_unsigned}, DECORATION_COMMON_ATTRS, }; diff --git a/decorations/underline.c b/decorations/underline.c index a700bec..5b8bbd3 100644 --- a/decorations/underline.c +++ b/decorations/underline.c @@ -54,7 +54,7 @@ static bool verify_conf(keychain_t *chain, const struct yml_node *node) { static const struct attr_info attrs[] = { - {"size", true, &conf_verify_int}, + {"size", true, &conf_verify_unsigned}, {"color", true, &conf_verify_color}, DECORATION_COMMON_ATTRS, }; From f166bbbf5449e2c394c55659fc46482a245d4f03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 15 Nov 2021 18:17:29 +0100 Subject: [PATCH 296/611] modules: verify: use conf_verify_unsigned() for options that should be >= 0 --- modules/battery.c | 2 +- modules/i3.c | 6 +++--- modules/mpd.c | 2 +- modules/network.c | 2 +- modules/removables.c | 6 +++--- modules/script.c | 2 +- modules/sway-xkb.c | 6 +++--- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/modules/battery.c b/modules/battery.c index 4f17de4..e6526f5 100644 --- a/modules/battery.c +++ b/modules/battery.c @@ -496,7 +496,7 @@ verify_conf(keychain_t *chain, const struct yml_node *node) { static const struct attr_info attrs[] = { {"name", true, &conf_verify_string}, - {"poll-interval", false, &conf_verify_int}, + {"poll-interval", false, &conf_verify_unsigned}, MODULE_COMMON_ATTRS, }; diff --git a/modules/i3.c b/modules/i3.c index 5e5c80a..0ed6927 100644 --- a/modules/i3.c +++ b/modules/i3.c @@ -878,9 +878,9 @@ static bool verify_conf(keychain_t *chain, const struct yml_node *node) { static const struct attr_info attrs[] = { - {"spacing", false, &conf_verify_int}, - {"left-spacing", false, &conf_verify_int}, - {"right-spacing", false, &conf_verify_int}, + {"spacing", false, &conf_verify_unsigned}, + {"left-spacing", false, &conf_verify_unsigned}, + {"right-spacing", false, &conf_verify_unsigned}, {"sort", false, &verify_sort}, {"persistent", false, &verify_persistent}, {"content", true, &verify_content}, diff --git a/modules/mpd.c b/modules/mpd.c index bebd401..274fdc8 100644 --- a/modules/mpd.c +++ b/modules/mpd.c @@ -616,7 +616,7 @@ verify_conf(keychain_t *chain, const struct yml_node *node) { static const struct attr_info attrs[] = { {"host", true, &conf_verify_string}, - {"port", false, &conf_verify_int}, + {"port", false, &conf_verify_unsigned}, MODULE_COMMON_ATTRS, }; diff --git a/modules/network.c b/modules/network.c index 06ada9b..3bff716 100644 --- a/modules/network.c +++ b/modules/network.c @@ -1257,7 +1257,7 @@ verify_conf(keychain_t *chain, const struct yml_node *node) { static const struct attr_info attrs[] = { {"name", true, &conf_verify_string}, - {"poll-interval", false, &conf_verify_int}, + {"poll-interval", false, &conf_verify_unsigned}, MODULE_COMMON_ATTRS, }; diff --git a/modules/removables.c b/modules/removables.c index 04d2695..3a54379 100644 --- a/modules/removables.c +++ b/modules/removables.c @@ -652,9 +652,9 @@ static bool verify_conf(keychain_t *chain, const struct yml_node *node) { static const struct attr_info attrs[] = { - {"spacing", false, &conf_verify_int}, - {"left-spacing", false, &conf_verify_int}, - {"right-spacing", false, &conf_verify_int}, + {"spacing", false, &conf_verify_unsigned}, + {"left-spacing", false, &conf_verify_unsigned}, + {"right-spacing", false, &conf_verify_unsigned}, {"ignore", false, &verify_ignore}, MODULE_COMMON_ATTRS, }; diff --git a/modules/script.c b/modules/script.c index 2ddd722..254aac4 100644 --- a/modules/script.c +++ b/modules/script.c @@ -709,7 +709,7 @@ verify_conf(keychain_t *chain, const struct yml_node *node) static const struct attr_info attrs[] = { {"path", true, &conf_verify_path}, {"args", false, &conf_verify_args}, - {"poll-interval", false, &conf_verify_int}, + {"poll-interval", false, &conf_verify_unsigned}, MODULE_COMMON_ATTRS, }; diff --git a/modules/sway-xkb.c b/modules/sway-xkb.c index f4e7577..9841feb 100644 --- a/modules/sway-xkb.c +++ b/modules/sway-xkb.c @@ -374,9 +374,9 @@ static bool verify_conf(keychain_t *chain, const struct yml_node *node) { static const struct attr_info attrs[] = { - {"spacing", false, &conf_verify_int}, - {"left-spacing", false, &conf_verify_int}, - {"right-spacing", false, &conf_verify_int}, + {"spacing", false, &conf_verify_unsigned}, + {"left-spacing", false, &conf_verify_unsigned}, + {"right-spacing", false, &conf_verify_unsigned}, {"identifiers", true, &verify_identifiers}, MODULE_COMMON_ATTRS, }; From 4f0d27bc7efd1ed83a82db36a5b15b542764aba4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 15 Nov 2021 18:17:52 +0100 Subject: [PATCH 297/611] particles: verify: use conf_verify_unsigned() for options that should be >= 0 --- particle.h | 16 ++++++++-------- particles/list.c | 6 +++--- particles/progress-bar.c | 2 +- particles/string.c | 2 +- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/particle.h b/particle.h index 4ca520f..7c5685e 100644 --- a/particle.h +++ b/particle.h @@ -76,12 +76,12 @@ void exposable_default_on_mouse( enum mouse_event event, enum mouse_button btn, int x, int y); /* List of attributes *all* particles implement */ -#define PARTICLE_COMMON_ATTRS \ - {"margin", false, &conf_verify_int}, \ - {"left-margin", false, &conf_verify_int}, \ - {"right-margin", false, &conf_verify_int}, \ - {"on-click", false, &conf_verify_on_click}, \ - {"font", false, &conf_verify_font}, \ - {"foreground", false, &conf_verify_color}, \ - {"deco", false, &conf_verify_decoration}, \ +#define PARTICLE_COMMON_ATTRS \ + {"margin", false, &conf_verify_unsigned}, \ + {"left-margin", false, &conf_verify_unsigned}, \ + {"right-margin", false, &conf_verify_unsigned}, \ + {"on-click", false, &conf_verify_on_click}, \ + {"font", false, &conf_verify_font}, \ + {"foreground", false, &conf_verify_color}, \ + {"deco", false, &conf_verify_decoration}, \ {NULL, false, NULL} diff --git a/particles/list.c b/particles/list.c index 19ff15b..e5cf883 100644 --- a/particles/list.c +++ b/particles/list.c @@ -208,9 +208,9 @@ verify_conf(keychain_t *chain, const struct yml_node *node) { static const struct attr_info attrs[] = { {"items", true, &conf_verify_particle_list_items}, - {"spacing", false, &conf_verify_int}, - {"left-spacing", false, &conf_verify_int}, - {"right-spacing", false, &conf_verify_int}, + {"spacing", false, &conf_verify_unsigned}, + {"left-spacing", false, &conf_verify_unsigned}, + {"right-spacing", false, &conf_verify_unsigned}, PARTICLE_COMMON_ATTRS, }; diff --git a/particles/progress-bar.c b/particles/progress-bar.c index ebc51c3..462d536 100644 --- a/particles/progress-bar.c +++ b/particles/progress-bar.c @@ -323,7 +323,7 @@ verify_conf(keychain_t *chain, const struct yml_node *node) { static const struct attr_info attrs[] = { {"tag", true, &conf_verify_string}, - {"length", true, &conf_verify_int}, + {"length", true, &conf_verify_unsigned}, /* TODO: make these optional? Default to empty */ {"start", true, &conf_verify_particle}, {"end", true, &conf_verify_particle}, diff --git a/particles/string.c b/particles/string.c index c475d5b..f6083f6 100644 --- a/particles/string.c +++ b/particles/string.c @@ -323,7 +323,7 @@ verify_conf(keychain_t *chain, const struct yml_node *node) { static const struct attr_info attrs[] = { {"text", true, &conf_verify_string}, - {"max", false, &conf_verify_int}, + {"max", false, &conf_verify_unsigned}, PARTICLE_COMMON_ATTRS, }; From 9d94cec549c95b5c3b36a6e28247b22ec66c6934 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 15 Nov 2021 18:21:19 +0100 Subject: [PATCH 298/611] changelog: integer options incorrectly allowed < 0 values --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d102c7d..74021a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,10 @@ “refreshed” it (https://codeberg.org/dnkl/yambar/issues/116). * network: failure to retrieve wireless attributes (SSID, RX/TX bitrate, signal strength etc). +* Integer options that were supposed to be >= 0 were incorrectly + allowed, leading to various bad things; including yambar crashing, + or worse, the compositor crashing + (https://codeberg.org/dnkl/yambar/issues/129). ### Security From b562f1310b541cded6940226b97d97354c57ce81 Mon Sep 17 00:00:00 2001 From: Catterwocky Date: Sat, 4 Dec 2021 17:49:39 +0100 Subject: [PATCH 299/611] Fix yaml indentation in docs It is unfortunate that the first example given by the manpage is not working. --- doc/yambar.5.scd | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/yambar.5.scd b/doc/yambar.5.scd index 47041fb..d3a585f 100644 --- a/doc/yambar.5.scd +++ b/doc/yambar.5.scd @@ -161,8 +161,8 @@ bar: right: - clock: - content: - - string: {text: "{time}"} + content: + - string: {text: "{time}"} ``` # FILES From 39a549345067163a3e8fb357de9a38a7e01a7626 Mon Sep 17 00:00:00 2001 From: natemaia Date: Thu, 9 Dec 2021 02:07:24 +0100 Subject: [PATCH 300/611] Fix: X11 struts https://specifications.freedesktop.org/wm-spec/wm-spec-1.3.html#idm45643490065584 --- bar/xcb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bar/xcb.c b/bar/xcb.c index 4b0e7c1..d8c5f9c 100644 --- a/bar/xcb.c +++ b/bar/xcb.c @@ -215,14 +215,14 @@ setup(struct bar *_bar) uint32_t top_pair[2], bottom_pair[2]; if (bar->location == BAR_TOP) { - top_strut = backend->y + bar->height_with_border; + top_strut = bar->height_with_border; top_pair[0] = backend->x; top_pair[1] = backend->x + bar->width - 1; bottom_strut = 0; bottom_pair[0] = bottom_pair[1] = 0; } else { - bottom_strut = screen->height_in_pixels - backend->y; + bottom_strut = bar->height_with_border; bottom_pair[0] = backend->x; bottom_pair[1] = backend->x + bar->width - 1; From 6612a9af9f7a9aece96629e6032a5600a1704777 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 17 Dec 2021 11:23:59 +0100 Subject: [PATCH 301/611] module/backlight: handle poll() failures --- modules/backlight.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/modules/backlight.c b/modules/backlight.c index 292b067..af58280 100644 --- a/modules/backlight.c +++ b/modules/backlight.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -178,15 +179,24 @@ run(struct module *mod) bar->refresh(bar); + int ret = 1; while (true) { struct pollfd fds[] = { {.fd = mod->abort_fd, .events = POLLIN}, {.fd = udev_monitor_get_fd(mon), .events = POLLIN}, }; - poll(fds, 2, -1); + if (poll(fds, sizeof(fds) / sizeof(fds[0]), -1) < 0) { + if (errno == EINTR) + continue; - if (fds[0].revents & POLLIN) + LOG_ERRNO("failed to poll"); break; + } + + if (fds[0].revents & POLLIN) { + ret = 0; + break; + } struct udev_device *dev = udev_monitor_receive_device(mon); if (dev == NULL) @@ -209,7 +219,7 @@ run(struct module *mod) udev_unref(udev); close(current_fd); - return 0; + return ret; } static struct module * From f9229734501aca189c946479031c2b5e74fb9993 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 17 Dec 2021 11:25:37 +0100 Subject: [PATCH 302/611] module/battery: handle poll() failures --- modules/battery.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/modules/battery.c b/modules/battery.c index e6526f5..0833310 100644 --- a/modules/battery.c +++ b/modules/battery.c @@ -428,7 +428,15 @@ run(struct module *mod) {.fd = mod->abort_fd, .events = POLLIN}, {.fd = udev_monitor_get_fd(mon), .events = POLLIN}, }; - poll(fds, 2, m->poll_interval > 0 ? m->poll_interval * 1000 : -1); + if (poll(fds, sizeof(fds) / sizeof(fds[0]), + m->poll_interval > 0 ? m->poll_interval * 1000 : -1) < 0) + { + if (errno == EINTR) + continue; + + LOG_ERRNO("failed to poll"); + break; + } if (fds[0].revents & POLLIN) { ret = 0; From 82ef48f666a55c1f09be92431481951bb36b19bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 17 Dec 2021 11:27:53 +0100 Subject: [PATCH 303/611] module/clock: remove unused include --- modules/clock.c | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/clock.c b/modules/clock.c index 2956a40..eddc7be 100644 --- a/modules/clock.c +++ b/modules/clock.c @@ -67,7 +67,6 @@ content(struct module *mod) return exposable; } -#include static int run(struct module *mod) { From cdd0b5b4f06be3434b13205aa26be807282ab578 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 17 Dec 2021 11:28:05 +0100 Subject: [PATCH 304/611] module/clock: fold long line --- modules/clock.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/clock.c b/modules/clock.c index eddc7be..49b832e 100644 --- a/modules/clock.c +++ b/modules/clock.c @@ -131,7 +131,8 @@ run(struct module *mod) } static struct module * -clock_new(struct particle *label, const char *date_format, const char *time_format, bool utc) +clock_new(struct particle *label, const char *date_format, + const char *time_format, bool utc) { struct private *m = calloc(1, sizeof(*m)); m->label = label; From 8a11a3fbe5a8094857d8df001ceaff9906847870 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 17 Dec 2021 11:28:14 +0100 Subject: [PATCH 305/611] module/clock: handle poll() failures --- modules/clock.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/modules/clock.c b/modules/clock.c index 49b832e..15392aa 100644 --- a/modules/clock.c +++ b/modules/clock.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include @@ -74,6 +75,8 @@ run(struct module *mod) const struct bar *bar = mod->bar; bar->refresh(bar); + int ret = 1; + while (true) { struct timespec _now; clock_gettime(CLOCK_REALTIME, &_now); @@ -119,15 +122,23 @@ run(struct module *mod) now.tv_sec, now.tv_usec, timeout_ms); struct pollfd fds[] = {{.fd = mod->abort_fd, .events = POLLIN}}; - poll(fds, 1, timeout_ms); + if (poll(fds, 1, timeout_ms) < 0) { + if (errno == EINTR) + continue; - if (fds[0].revents & POLLIN) + LOG_ERRNO("failed to poll"); break; + } + + if (fds[0].revents & POLLIN) { + ret = 0; + break; + } bar->refresh(bar); } - return 0; + return ret; } static struct module * From 5d09e59f111c6a053697852e218cefea280cf58e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 17 Dec 2021 11:29:26 +0100 Subject: [PATCH 306/611] module/mpd: handle poll() failures --- modules/mpd.c | 47 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 36 insertions(+), 11 deletions(-) diff --git a/modules/mpd.c b/modules/mpd.c index 274fdc8..b2e9e6e 100644 --- a/modules/mpd.c +++ b/modules/mpd.c @@ -251,7 +251,13 @@ wait_for_socket_create(const struct module *mod) {.fd = fd, .events = POLLIN} }; - poll(fds, 2, -1); + if (poll(fds, sizeof(fds) / sizeof(fds[0]), -1) < 0) { + if (errno == EINTR) + continue; + + LOG_ERRNO("failed to poll"); + break; + } if (fds[0].revents & POLLIN) { ret = true; @@ -377,8 +383,9 @@ run(struct module *mod) struct private *m = mod->private; bool aborted = false; + int ret = 0; - while (!aborted) { + while (!aborted && ret == 0) { if (m->conn != NULL) { mpd_connection_free(m->conn); @@ -396,7 +403,7 @@ run(struct module *mod) mtx_unlock(&mod->lock); /* Keep trying to connect, until we succeed */ - while (!aborted) { + while (!aborted && ret == 0) { if (m->port == 0) { /* Use inotify to watch for socket creation */ aborted = wait_for_socket_create(mod); @@ -414,16 +421,27 @@ run(struct module *mod) * host), wait for a while until we try to re-connect * again. */ - struct pollfd fds[] = {{.fd = mod->abort_fd, .events = POLLIN}}; - int res = poll(fds, 1, 10 * 1000); + while (!aborted) { + struct pollfd fds[] = {{.fd = mod->abort_fd, .events = POLLIN}}; + int res = poll(fds, sizeof(fds) / sizeof(fds[0]), 10 * 1000); - if (res == 1) { - assert(fds[0].revents & POLLIN); - aborted = true; + if (res < 0) { + if (errno == EINTR) + continue; + + LOG_ERRNO("failed to poll"); + ret = 1; + break; + } + + if (res == 1) { + assert(fds[0].revents & POLLIN); + aborted = true; + } } } - if (aborted) + if (aborted || ret != 0) break; /* Initial state (after establishing a connection) */ @@ -446,7 +464,14 @@ run(struct module *mod) break; } - poll(fds, 2, -1); + if (poll(fds, sizeof(fds) / sizeof(fds[0]), -1) < 0) { + if (errno == EINTR) + continue; + + LOG_ERRNO("failed to poll"); + ret = 1; + break; + } if (fds[0].revents & POLLIN) { aborted = true; @@ -477,7 +502,7 @@ run(struct module *mod) m->conn = NULL; } - return 0; + return aborted ? 0 : ret; } struct refresh_context { From ffa86d84a5546c5a20f463b8eb3276c88415f885 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 17 Dec 2021 11:40:12 +0100 Subject: [PATCH 307/611] module/removables: handle poll() failures --- modules/removables.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/modules/removables.c b/modules/removables.c index 3a54379..736317f 100644 --- a/modules/removables.c +++ b/modules/removables.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -547,16 +548,26 @@ run(struct module *mod) * mount/unmount operations */ int mount_info_fd = open("/proc/self/mountinfo", O_RDONLY); + int ret = 1; + while (true) { struct pollfd fds[] = { {.fd = mod->abort_fd, .events = POLLIN}, {.fd = udev_monitor_get_fd(dev_mon), .events = POLLIN}, {.fd = mount_info_fd, .events = POLLPRI}, }; - poll(fds, 3, -1); + if (poll(fds, sizeof(fds) / sizeof(fds[0]), -1) < 0) { + if (errno == EINTR) + continue; - if (fds[0].revents & POLLIN) + LOG_ERRNO("failed to poll"); break; + } + + if (fds[0].revents & POLLIN) { + ret = 0; + break; + } bool update = false; @@ -587,7 +598,7 @@ run(struct module *mod) udev_monitor_unref(dev_mon); udev_unref(udev); - return 0; + return ret; } static struct module * From 96c75b7f73697c1f2cd84f24fbec8aa240ae0570 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 17 Dec 2021 11:42:53 +0100 Subject: [PATCH 308/611] module/xkb: handle poll() failures --- modules/xkb.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/modules/xkb.c b/modules/xkb.c index 5c2c1f9..965664d 100644 --- a/modules/xkb.c +++ b/modules/xkb.c @@ -399,7 +399,14 @@ event_loop(struct module *mod, xcb_connection_t *conn, int xkb_event_base) }; /* Use poll() since xcb_wait_for_events() doesn't return on signals */ - poll(pfds, sizeof(pfds) / sizeof(pfds[0]), -1); + if (poll(pfds, sizeof(pfds) / sizeof(pfds[0]), -1) < 0) { + if (errno == EINTR) + continue; + + LOG_ERRNO("failed to poll"); + break; + } + if (pfds[0].revents & POLLIN) { ret = true; break; From 9cff50a39ef757074ebec258aad2b20e27bc61af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 17 Dec 2021 11:42:59 +0100 Subject: [PATCH 309/611] module/xwindow: handle poll() failures --- modules/xwindow.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/modules/xwindow.c b/modules/xwindow.c index 3925f4f..baf1239 100644 --- a/modules/xwindow.c +++ b/modules/xwindow.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -247,14 +248,24 @@ run(struct module *mod) update_title(mod); mod->bar->refresh(mod->bar); + int ret = 1; + int xcb_fd = xcb_get_file_descriptor(m->conn); while (true) { struct pollfd fds[] = {{.fd = mod->abort_fd, .events = POLLIN}, {.fd = xcb_fd, .events = POLLIN}}; - poll(fds, 2, -1); + if (poll(fds, sizeof(fds) / sizeof(fds[0]), -1) < 0) { + if (errno == EINTR) + continue; - if (fds[0].revents & POLLIN) + LOG_ERRNO("failed to poll"); break; + } + + if (fds[0].revents & POLLIN) { + ret = 0; + break; + } for (xcb_generic_event_t *_e = xcb_wait_for_event(m->conn); _e != NULL; @@ -293,7 +304,7 @@ run(struct module *mod) xcb_destroy_window(m->conn, m->monitor_win); xcb_disconnect(m->conn); - return 0; + return ret; } static struct exposable * From 3e133d86186b0dcdcebca617591fe8256fe62044 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 19 Dec 2021 17:48:58 +0100 Subject: [PATCH 310/611] =?UTF-8?q?module/i3:=20add=20=E2=80=98empty?= =?UTF-8?q?=E2=80=99=20boolean=20tag?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Set to true for empty (no windows) workspaces. Mostly useful with persistent workspaces, to be able to differentiate between invisible non-empty workspaces and actually empty workspaces (the latter not being possible with non-persistent workspaces). --- modules/i3.c | 41 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/modules/i3.c b/modules/i3.c index 0ed6927..56dec4d 100644 --- a/modules/i3.c +++ b/modules/i3.c @@ -37,6 +37,7 @@ struct workspace { bool visible; bool focused; bool urgent; + bool empty; struct { unsigned id; @@ -85,14 +86,17 @@ static bool workspace_from_json(const struct json_object *json, struct workspace *ws) { /* Always present */ - struct json_object *name, *output; + struct json_object *name, *output, *focus; if (!json_object_object_get_ex(json, "name", &name) || - !json_object_object_get_ex(json, "output", &output)) + !json_object_object_get_ex(json, "output", &output) || + !json_object_object_get_ex(json, "focus", &focus)) { - LOG_ERR("workspace reply/event without 'name' and/or 'output' property"); + LOG_ERR("workspace reply/event without 'name' and/or 'output', " + "and/or 'focus' properties"); return false; } + /* Optional */ struct json_object *visible = NULL, *focused = NULL, *urgent = NULL; json_object_object_get_ex(json, "visible", &visible); @@ -101,6 +105,9 @@ workspace_from_json(const struct json_object *json, struct workspace *ws) const char *name_as_string = json_object_get_string(name); + const size_t node_count = json_object_array_length(focus); + const bool is_empty = node_count == 0; + *ws = (struct workspace) { .name = strdup(name_as_string), .name_as_int = workspace_name_as_int(name_as_string), @@ -109,6 +116,7 @@ workspace_from_json(const struct json_object *json, struct workspace *ws) .visible = json_object_get_boolean(visible), .focused = json_object_get_boolean(focused), .urgent = json_object_get_boolean(urgent), + .empty = is_empty, .window = {.title = NULL, .pid = -1}, }; @@ -353,6 +361,7 @@ handle_workspace_event(int type, const struct json_object *json, void *_mod) else { workspace_free(ws); ws->name = strdup(current_name); + ws->empty = true; assert(ws->persistent); } } @@ -425,11 +434,12 @@ handle_window_event(int type, const struct json_object *json, void *_mod) } const char *change_str = json_object_get_string(change); + bool is_new = strcmp(change_str, "new") == 0; bool is_focus = strcmp(change_str, "focus") == 0; bool is_close = strcmp(change_str, "close") == 0; bool is_title = strcmp(change_str, "title") == 0; - if (!is_focus && !is_close && !is_title) + if (!is_new && !is_focus && !is_close && !is_title) return true; mtx_lock(&mod->lock); @@ -454,12 +464,19 @@ handle_window_event(int type, const struct json_object *json, void *_mod) ws->window.title = ws->window.application = NULL; ws->window.pid = -1; + /* May not be true, but e.g. a subsequent “focus” event will + * reset it... */ + ws->empty = true; + m->dirty = true; mtx_unlock(&mod->lock); return true; } + /* Non-close event - thus workspace cannot be empty */ + ws->empty = false; + struct json_object *container, *id, *name; if (!json_object_object_get_ex(json, "container", &container) || !json_object_object_get_ex(container, "id", &id) || @@ -602,6 +619,7 @@ run(struct module *mod) .name = strdup(name_as_string), .name_as_int = workspace_name_as_int(name_as_string), .persistent = true, + .empty = true, }; workspace_add(m, ws); } @@ -695,12 +713,25 @@ content(struct module *mod) ws->visible ? ws->focused ? "focused" : "unfocused" : "invisible"; + LOG_DBG("%s: visible=%s, focused=%s, urgent=%s, empty=%s, state=%s, " + "application=%s, title=%s, mode=%s", + ws->name, + ws->visible ? "yes" : "no", + ws->focused ? "yes" : "no", + ws->urgent ? "yes" : "no", + ws->empty ? "yes" : "no", + state, + ws->window.application, + ws->window.title, + m->mode); + struct tag_set tags = { .tags = (struct tag *[]){ tag_new_string(mod, "name", ws->name), tag_new_bool(mod, "visible", ws->visible), tag_new_bool(mod, "focused", ws->focused), tag_new_bool(mod, "urgent", ws->urgent), + tag_new_bool(mod, "empty", ws->empty), tag_new_string(mod, "state", state), tag_new_string(mod, "application", ws->window.application), @@ -708,7 +739,7 @@ content(struct module *mod) tag_new_string(mod, "mode", m->mode), }, - .count = 8, + .count = 9, }; if (ws->focused) { From 8475ca160358c5db2bb3c8b80b15df957f71b07f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 19 Dec 2021 17:51:01 +0100 Subject: [PATCH 311/611] =?UTF-8?q?doc:=20i3:=20document=20the=20new=20?= =?UTF-8?q?=E2=80=98empty=E2=80=99=20tag?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/yambar-modules-i3.5.scd | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/yambar-modules-i3.5.scd b/doc/yambar-modules-i3.5.scd index 8bf4a75..2cae2f8 100644 --- a/doc/yambar-modules-i3.5.scd +++ b/doc/yambar-modules-i3.5.scd @@ -35,6 +35,9 @@ with the _application_ and _title_ tags to replace the X11-only | urgent : bool : True if the workspace has the urgent flag set +| empty +: bool +: True if the workspace is empty | state : string : One of *urgent*, *focused*, *unfocused* or *invisible* (note: From 26ea13793894202145da42d53c8ad045f226bd30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 19 Dec 2021 17:51:12 +0100 Subject: [PATCH 312/611] =?UTF-8?q?changelog:=20i3/sway:=20=E2=80=98empty?= =?UTF-8?q?=E2=80=99=20tag?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 74021a7..fba854f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,9 +10,12 @@ ## Unreleased ### Added + * ramp: can now have custom min and max values (https://codeberg.org/dnkl/yambar/issues/103). * border: new decoration. +* i3/sway: new boolean tag: `empty` + (https://codeberg.org/dnkl/yambar/issues/139). ### Changed From f7206ef08d59f1047a3a7c85bd9b6b12f3d0ac34 Mon Sep 17 00:00:00 2001 From: horus645 Date: Sat, 18 Dec 2021 22:17:20 -0300 Subject: [PATCH 313/611] Added documentation for discriminated on-click events --- doc/yambar-particles.5.scd | 45 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/doc/yambar-particles.5.scd b/doc/yambar-particles.5.scd index ab689a0..a633269 100644 --- a/doc/yambar-particles.5.scd +++ b/doc/yambar-particles.5.scd @@ -36,15 +36,56 @@ following attributes are supported by all particles: : no : Foreground (text) color. Just like _font_, this is an inherited attribute. | on-click +: associative array/string +: no +: When set to a string, executes the string as a command when the particle + is left-clicked. Tags can be used. Note that the string is *not* + executed in a shell. The same applies to all attributes associated with + it, below. +| on-click.left : string : no -: Command to execute when the particle is clicked. Tags can be - used. Note that the string is *not* executed in a shell. +: Command to execute when the particle is left-clicked. +| on-click.right +: string +: no +: Command to execute when the particle is right-clicked. +| on-click.middle +: string +: no +: Command to execute when the particle is middle-clicked. +| on-click.wheel-up +: string +: no +: Command to execute every time a 'wheel-up' event is triggered. +| on-click.wheel-down +: string +: no +: Command to execute every time a 'wheel-down' event is triggered. | deco : decoration : no : Decoration to apply to the particle. See *yambar-decorations*(5) +## EXAMPLES: + +*on-click* as a string (handles left click): +``` +content: + : + on-click: command args +``` + +*on-click* as an associative array (handles other buttons): +``` +content: + : + on-click: + left: command-1 + wheel-up: command-3 + wheel-down: command-4 +``` + # STRING This is the most basic particle. It takes a format string, consisting From d8d44b0f33547e25645d497edf3ee05a9f38040a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 21 Dec 2021 13:41:46 +0100 Subject: [PATCH 314/611] meson: generate-version: use CURRENT_SOURCE_DIR instead of SOURCE_ROOT --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index ed4d0d1..66f200c 100644 --- a/meson.build +++ b/meson.build @@ -107,7 +107,7 @@ version = custom_target( 'generate_version', build_always_stale: true, output: 'version.h', - command: [env, 'LC_ALL=C', generate_version_sh, meson.project_version(), '@SOURCE_ROOT@', '@OUTPUT@']) + command: [env, 'LC_ALL=C', generate_version_sh, meson.project_version(), '@CURRENT_SOURCE_DIR@', '@OUTPUT@']) yambar = executable( 'yambar', From 337ce7681f835bb3caf1a7027b7804e5ee9209d1 Mon Sep 17 00:00:00 2001 From: Alexandre Acebedo Date: Sat, 11 Dec 2021 18:18:21 +0100 Subject: [PATCH 315/611] modules: add mem module --- CHANGELOG.md | 1 + doc/meson.build | 1 + doc/yambar-modules-mem.5.scd | 51 ++++++++++ modules/mem.c | 192 +++++++++++++++++++++++++++++++++++ modules/meson.build | 1 + plugin.c | 2 + 6 files changed, 248 insertions(+) create mode 100644 doc/yambar-modules-mem.5.scd create mode 100644 modules/mem.c diff --git a/CHANGELOG.md b/CHANGELOG.md index fba854f..3947faf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ * border: new decoration. * i3/sway: new boolean tag: `empty` (https://codeberg.org/dnkl/yambar/issues/139). +* mem: a module handling system memory monitoring ### Changed diff --git a/doc/meson.build b/doc/meson.build index a1550c4..3bacdd8 100644 --- a/doc/meson.build +++ b/doc/meson.build @@ -13,6 +13,7 @@ foreach man_src : ['yambar.1.scd', 'yambar.5.scd', 'yambar-decorations.5.scd', 'yambar-modules-script.5.scd', 'yambar-modules-sway-xkb.5.scd', 'yambar-modules-sway.5.scd', 'yambar-modules-xkb.5.scd', 'yambar-modules-xwindow.5.scd', 'yambar-modules.5.scd', + 'yambar-modules-mem.5.scd', 'yambar-particles.5.scd', 'yambar-tags.5.scd'] parts = man_src.split('.') name = parts[-3] diff --git a/doc/yambar-modules-mem.5.scd b/doc/yambar-modules-mem.5.scd new file mode 100644 index 0000000..cec575c --- /dev/null +++ b/doc/yambar-modules-mem.5.scd @@ -0,0 +1,51 @@ +yambar-modules-mem(5) + +# NAME +mem - This module provides the memory usage + +# TAGS + +[[ *Name* +:[ *Type* +:[ *Description* +| free +: int +: Free memory in bytes +| used +: int +: Used memory in bytes +| total +: int +: Total memory in bytes +| percent_free +: range +: Free memory in percent +| percent_used +: range +: Used memory in percent + +# CONFIGURATION + +[[ *Name* +:[ *Type* +:[ *Req* +:[ *Description* +| interval +: string +: no +: Refresh interval of the memory usage stats in ms (default=500). Cannot be less then 500 ms + +# EXAMPLES + +``` +bar: + left: + - mem: + interval: 2500 + content: + string: {text: "{used:mb}MB"} +``` + +# SEE ALSO + +*yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5) diff --git a/modules/mem.c b/modules/mem.c new file mode 100644 index 0000000..d79861e --- /dev/null +++ b/modules/mem.c @@ -0,0 +1,192 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LOG_MODULE "mem" +#define LOG_ENABLE_DBG 0 +#define SMALLEST_INTERVAL 500 +#include "../bar/bar.h" +#include "../config-verify.h" +#include "../config.h" +#include "../log.h" +#include "../plugin.h" + +struct private +{ + struct particle *label; + uint16_t interval; +}; + +static void +destroy(struct module *mod) +{ + struct private *m = mod->private; + m->label->destroy(m->label); + free(m); + module_default_destroy(mod); +} + +static const char * +description(struct module *mod) +{ + return "mem"; +} + +static bool +get_mem_stats(uint64_t *mem_free, uint64_t *mem_total) +{ + bool mem_total_found = false; + bool mem_free_found = false; + + FILE *fp = NULL; + char *line = NULL; + size_t len = 0; + ssize_t read = 0; + + fp = fopen("/proc/meminfo", "r"); + if (NULL == fp) { + LOG_ERRNO("unable to open /proc/meminfo"); + return false; + } + + while ((read = getline(&line, &len, fp)) != -1) { + if (strncmp(line, "MemTotal:", sizeof("MemTotal:") - 1) == 0) { + read = sscanf(line + sizeof("MemTotal:") - 1, "%" SCNu64, mem_total); + mem_total_found = (read == 1); + } + if (strncmp(line, "MemAvailable:", sizeof("MemAvailable:") - 1) == 0) { + read = sscanf(line + sizeof("MemAvailable:"), "%" SCNu64, mem_free); + mem_free_found = (read == 1); + } + } + free(line); + + fclose(fp); + + return mem_free_found && mem_total_found; +} + +static struct exposable * +content(struct module *mod) +{ + const struct private *p = mod->private; + uint64_t mem_free = 0; + uint64_t mem_used = 0; + uint64_t mem_total = 0; + + if (!get_mem_stats(&mem_free, &mem_total)) { + LOG_ERR("unable to retrieve the memory stats"); + } + + mem_used = mem_total - mem_free; + + double percent_used = ((double)mem_used * 100) / (mem_total + 1); + double percent_free = ((double)mem_free * 100) / (mem_total + 1); + + struct tag_set tags = { + .tags = (struct tag *[]){tag_new_int(mod, "free", mem_free * 1024), tag_new_int(mod, "used", mem_used * 1024), + tag_new_int(mod, "total", mem_total * 1024), + tag_new_int_range(mod, "percent_free", round(percent_free), 0, 100), + tag_new_int_range(mod, "percent_used", round(percent_used), 0, 100)}, + .count = 5, + }; + + struct exposable *exposable = p->label->instantiate(p->label, &tags); + tag_set_destroy(&tags); + return exposable; +} + +static int +run(struct module *mod) +{ + const struct bar *bar = mod->bar; + bar->refresh(bar); + struct private *p = mod->private; + while (true) { + struct pollfd fds[] = {{.fd = mod->abort_fd, .events = POLLIN}}; + + int res = poll(fds, 1, p->interval); + if (res < 0) { + if (EINTR == errno) { + continue; + } + + LOG_ERRNO("unable to poll abort fd"); + return -1; + } + + if (fds[0].revents & POLLIN) + break; + + bar->refresh(bar); + } + + return 0; +} + +static struct module * +mem_new(uint16_t interval, struct particle *label) +{ + struct private *p = calloc(1, sizeof(*p)); + p->label = label; + p->interval = interval; + + struct module *mod = module_common_new(); + mod->private = p; + mod->run = &run; + mod->destroy = &destroy; + mod->content = &content; + mod->description = &description; + return mod; +} + +static struct module * +from_conf(const struct yml_node *node, struct conf_inherit inherited) +{ + const struct yml_node *interval = yml_get_value(node, "interval"); + const struct yml_node *c = yml_get_value(node, "content"); + + return mem_new(interval == NULL ? SMALLEST_INTERVAL : yml_value_as_int(interval), conf_to_particle(c, inherited)); +} + +static bool +conf_verify_interval(keychain_t *chain, const struct yml_node *node) +{ + if (!conf_verify_unsigned(chain, node)) + return false; + + if (yml_value_as_int(node) < SMALLEST_INTERVAL) { + LOG_ERR("%s: interval value cannot be less than %d ms", conf_err_prefix(chain, node), SMALLEST_INTERVAL); + return false; + } + + return true; +} + +static bool +verify_conf(keychain_t *chain, const struct yml_node *node) +{ + static const struct attr_info attrs[] = { + {"interval", false, &conf_verify_interval}, + MODULE_COMMON_ATTRS, + }; + + return conf_verify_dict(chain, node, attrs); +} + +const struct module_iface module_mem_iface = { + .verify_conf = &verify_conf, + .from_conf = &from_conf, +}; + +#if defined(CORE_PLUGINS_AS_SHARED_LIBRARIES) +extern const struct module_iface iface __attribute__((weak, alias("module_mem_iface"))); +#endif diff --git a/modules/meson.build b/modules/meson.build index 6b64958..635ac03 100644 --- a/modules/meson.build +++ b/modules/meson.build @@ -17,6 +17,7 @@ mod_data = { 'backlight': [[], [m, udev]], 'battery': [[], [udev]], 'clock': [[], []], + 'mem': [[], []], 'i3': [['i3-common.c', 'i3-common.h'], [dynlist, json]], 'label': [[], []], 'network': [[], []], diff --git a/plugin.c b/plugin.c index 5f31b2e..becc152 100644 --- a/plugin.c +++ b/plugin.c @@ -49,6 +49,7 @@ EXTERN_MODULE(sway_xkb); EXTERN_MODULE(script); EXTERN_MODULE(xkb); EXTERN_MODULE(xwindow); +EXTERN_MODULE(mem); EXTERN_PARTICLE(empty); EXTERN_PARTICLE(list); @@ -136,6 +137,7 @@ init(void) #if defined(HAVE_PLUGIN_xwindow) REGISTER_CORE_MODULE(xwindow, xwindow); #endif + REGISTER_CORE_MODULE(mem, mem); REGISTER_CORE_PARTICLE(empty, empty); REGISTER_CORE_PARTICLE(list, list); From ae5c7e0fc37cb19eb63bc9990beddcc23fb09833 Mon Sep 17 00:00:00 2001 From: Alexandre Acebedo Date: Sat, 11 Dec 2021 18:20:04 +0100 Subject: [PATCH 316/611] modules: add cpu module --- CHANGELOG.md | 1 + doc/meson.build | 1 + doc/yambar-modules-cpu.5.scd | 42 +++++ modules/cpu.c | 294 +++++++++++++++++++++++++++++++++++ modules/meson.build | 1 + plugin.c | 2 + 6 files changed, 341 insertions(+) create mode 100644 doc/yambar-modules-cpu.5.scd create mode 100644 modules/cpu.c diff --git a/CHANGELOG.md b/CHANGELOG.md index 3947faf..9a834b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ * i3/sway: new boolean tag: `empty` (https://codeberg.org/dnkl/yambar/issues/139). * mem: a module handling system memory monitoring +* cpu: a module offering cpu usage monitoring ### Changed diff --git a/doc/meson.build b/doc/meson.build index 3bacdd8..0d7f873 100644 --- a/doc/meson.build +++ b/doc/meson.build @@ -13,6 +13,7 @@ foreach man_src : ['yambar.1.scd', 'yambar.5.scd', 'yambar-decorations.5.scd', 'yambar-modules-script.5.scd', 'yambar-modules-sway-xkb.5.scd', 'yambar-modules-sway.5.scd', 'yambar-modules-xkb.5.scd', 'yambar-modules-xwindow.5.scd', 'yambar-modules.5.scd', + 'yambar-modules-cpu.5.scd', 'yambar-modules-mem.5.scd', 'yambar-particles.5.scd', 'yambar-tags.5.scd'] parts = man_src.split('.') diff --git a/doc/yambar-modules-cpu.5.scd b/doc/yambar-modules-cpu.5.scd new file mode 100644 index 0000000..2a05fdf --- /dev/null +++ b/doc/yambar-modules-cpu.5.scd @@ -0,0 +1,42 @@ +yambar-modules-cpu(5) + +# NAME +cpu - This module provides the CPU usage + +# TAGS + +[[ *Name* +:[ *Type* +:[ *Description* +| cpu +: range +: Current usage of the whole CPU in percent +| cpu<0..X> +: range +: Current usage of CPU core X in percent + +# CONFIGURATION + +[[ *Name* +:[ *Type* +:[ *Req* +:[ *Description* +| interval +: int +: no +: Refresh interval of the CPU usage stats in ms (default=500). Cannot be less then 500 ms + +# EXAMPLES + +``` +bar: + left: + - cpu: + interval: 2500 + content: + string: {text: "{cpu1}%"} +``` + +# SEE ALSO + +*yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5) diff --git a/modules/cpu.c b/modules/cpu.c new file mode 100644 index 0000000..0967dfd --- /dev/null +++ b/modules/cpu.c @@ -0,0 +1,294 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LOG_MODULE "cpu" +#define LOG_ENABLE_DBG 0 +#define SMALLEST_INTERVAL 500 +#include "../bar/bar.h" +#include "../config-verify.h" +#include "../config.h" +#include "../log.h" +#include "../plugin.h" +struct cpu_stats { + uint32_t *prev_cores_idle; + uint32_t *prev_cores_nidle; + + uint32_t *cur_cores_idle; + uint32_t *cur_cores_nidle; +}; + +struct private +{ + struct particle *label; + uint16_t interval; + struct cpu_stats cpu_stats; +}; + +static void +destroy(struct module *mod) +{ + struct private *m = mod->private; + m->label->destroy(m->label); + free(m->cpu_stats.prev_cores_idle); + free(m->cpu_stats.prev_cores_nidle); + free(m->cpu_stats.cur_cores_idle); + free(m->cpu_stats.cur_cores_nidle); + free(m); + module_default_destroy(mod); +} + +static const char * +description(struct module *mod) +{ + return "cpu"; +} + +static uint32_t +get_cpu_nb_cores() +{ + uint32_t nb_cores = 0; + FILE *fp = NULL; + char *line = NULL; + size_t len = 0; + ssize_t read; + + fp = fopen("/proc/cpuinfo", "r"); + if (NULL == fp) { + LOG_ERRNO("unable to open /proc/cpuinfo"); + return 0; + } + while ((read = getline(&line, &len, fp)) != -1) { + if (strncmp(line, "siblings", sizeof("siblings") - 1) == 0) { + char *pos = (char *)memchr(line, ':', read); + if (pos == NULL) { + LOG_ERR("unable to parse siblings field to find the number of cores"); + return 0; + } + errno = 0; + nb_cores = strtoul(pos + 1, NULL, 10); + if (errno == ERANGE) { + LOG_ERR("number of cores is out of range"); + } + break; + } + } + fclose(fp); + free(line); + + return nb_cores; +} + +static bool +parse_proc_stat_line(const char *line, int32_t *core, uint32_t *user, uint32_t *nice, uint32_t *system, uint32_t *idle, + uint32_t *iowait, uint32_t *irq, uint32_t *softirq, uint32_t *steal, uint32_t *guest, + uint32_t *guestnice) +{ + if (line[sizeof("cpu") - 1] == ' ') { + int read = sscanf(line, + "cpu %" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32 + " %" SCNu32 " %" SCNu32 " %" SCNu32, + user, nice, system, idle, iowait, irq, softirq, steal, guest, guestnice); + *core = -1; + return read == 10; + } else { + int read = sscanf(line, + "cpu%" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32 + " %" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32, + core, user, nice, system, idle, iowait, irq, softirq, steal, guest, guestnice); + return read == 11; + } +} + +static uint8_t +get_cpu_usage_percent(const struct cpu_stats *cpu_stats, int8_t core_idx) +{ + uint32_t prev_total = cpu_stats->prev_cores_idle[core_idx + 1] + cpu_stats->prev_cores_nidle[core_idx + 1]; + uint32_t cur_total = cpu_stats->cur_cores_idle[core_idx + 1] + cpu_stats->cur_cores_nidle[core_idx + 1]; + + double totald = cur_total - prev_total; + double nidled = cpu_stats->cur_cores_nidle[core_idx + 1] - cpu_stats->prev_cores_nidle[core_idx + 1]; + + double percent = (nidled * 100) / (totald + 1); + + return round(percent); +} + +static void +refresh_cpu_stats(struct cpu_stats *cpu_stats) +{ + uint32_t nb_cores = get_cpu_nb_cores(); + int32_t core = 0; + uint32_t user = 0; + uint32_t nice = 0; + uint32_t system = 0; + uint32_t idle = 0; + uint32_t iowait = 0; + uint32_t irq = 0; + uint32_t softirq = 0; + uint32_t steal = 0; + uint32_t guest = 0; + uint32_t guestnice = 0; + + FILE *fp = NULL; + char *line = NULL; + size_t len = 0; + ssize_t read; + + fp = fopen("/proc/stat", "r"); + if (NULL == fp) { + LOG_ERRNO("unable to open /proc/stat"); + return; + } + + while ((read = getline(&line, &len, fp)) != -1) { + if (strncmp(line, "cpu", sizeof("cpu") - 1) == 0) { + if (!parse_proc_stat_line(line, &core, &user, &nice, &system, &idle, &iowait, &irq, &softirq, &steal, + &guest, &guestnice) + || core < -1 || core >= (int32_t)nb_cores) { + LOG_ERR("unable to parse /proc/stat line"); + goto exit; + } + + cpu_stats->prev_cores_idle[core + 1] = cpu_stats->cur_cores_idle[core + 1]; + cpu_stats->prev_cores_nidle[core + 1] = cpu_stats->cur_cores_nidle[core + 1]; + + cpu_stats->cur_cores_idle[core + 1] = idle + iowait; + cpu_stats->cur_cores_nidle[core + 1] = user + nice + system + irq + softirq + steal; + } + } +exit: + fclose(fp); + free(line); +} + +static struct exposable * +content(struct module *mod) +{ + const struct private *p = mod->private; + uint32_t nb_cores = get_cpu_nb_cores(); + + char cpu_name[32]; + struct tag_set tags; + tags.count = nb_cores + 1; + tags.tags = calloc(tags.count, sizeof(*tags.tags)); + mtx_lock(&mod->lock); + uint8_t cpu_usage = get_cpu_usage_percent(&p->cpu_stats, -1); + tags.tags[0] = tag_new_int_range(mod, "cpu", cpu_usage, 0, 100); + + for (uint32_t i = 0; i < nb_cores; ++i) { + uint8_t cpu_usage = get_cpu_usage_percent(&p->cpu_stats, i); + snprintf(cpu_name, sizeof(cpu_name), "cpu%u", i); + tags.tags[i + 1] = tag_new_int_range(mod, cpu_name, cpu_usage, 0, 100); + } + mtx_unlock(&mod->lock); + + struct exposable *exposable = p->label->instantiate(p->label, &tags); + + tag_set_destroy(&tags); + free(tags.tags); + return exposable; +} + +static int +run(struct module *mod) +{ + const struct bar *bar = mod->bar; + bar->refresh(bar); + struct private *p = mod->private; + while (true) { + struct pollfd fds[] = {{.fd = mod->abort_fd, .events = POLLIN}}; + + int res = poll(fds, sizeof(fds) / sizeof(*fds), p->interval); + + if (res < 0) { + if (EINTR == errno) + continue; + LOG_ERRNO("unable to poll abort fd"); + return -1; + } + + if (fds[0].revents & POLLIN) + break; + + mtx_lock(&mod->lock); + refresh_cpu_stats(&p->cpu_stats); + mtx_unlock(&mod->lock); + bar->refresh(bar); + } + + return 0; +} + +static struct module * +cpu_new(uint16_t interval, struct particle *label) +{ + struct private *p = calloc(1, sizeof(*p)); + p->label = label; + uint32_t nb_cores = get_cpu_nb_cores(); + p->interval = interval; + p->cpu_stats.prev_cores_nidle = calloc(nb_cores + 1, sizeof(*p->cpu_stats.prev_cores_nidle)); + p->cpu_stats.prev_cores_idle = calloc(nb_cores + 1, sizeof(*p->cpu_stats.prev_cores_idle)); + + p->cpu_stats.cur_cores_nidle = calloc(nb_cores + 1, sizeof(*p->cpu_stats.cur_cores_nidle)); + p->cpu_stats.cur_cores_idle = calloc(nb_cores + 1, sizeof(*p->cpu_stats.cur_cores_idle)); + + struct module *mod = module_common_new(); + mod->private = p; + mod->run = &run; + mod->destroy = &destroy; + mod->content = &content; + mod->description = &description; + return mod; +} + +static struct module * +from_conf(const struct yml_node *node, struct conf_inherit inherited) +{ + const struct yml_node *interval = yml_get_value(node, "interval"); + const struct yml_node *c = yml_get_value(node, "content"); + + return cpu_new(interval == NULL ? SMALLEST_INTERVAL : yml_value_as_int(interval), conf_to_particle(c, inherited)); +} + +static bool +conf_verify_interval(keychain_t *chain, const struct yml_node *node) +{ + if (!conf_verify_unsigned(chain, node)) + return false; + + if (yml_value_as_int(node) < SMALLEST_INTERVAL) { + LOG_ERR("%s: interval value cannot be less than %d ms", conf_err_prefix(chain, node), SMALLEST_INTERVAL); + return false; + } + + return true; +} + +static bool +verify_conf(keychain_t *chain, const struct yml_node *node) +{ + static const struct attr_info attrs[] = { + {"interval", false, &conf_verify_interval}, + MODULE_COMMON_ATTRS, + }; + + return conf_verify_dict(chain, node, attrs); +} + +const struct module_iface module_cpu_iface = { + .verify_conf = &verify_conf, + .from_conf = &from_conf, +}; + +#if defined(CORE_PLUGINS_AS_SHARED_LIBRARIES) +extern const struct module_iface iface __attribute__((weak, alias("module_cpu_iface"))); +#endif diff --git a/modules/meson.build b/modules/meson.build index 635ac03..192b094 100644 --- a/modules/meson.build +++ b/modules/meson.build @@ -17,6 +17,7 @@ mod_data = { 'backlight': [[], [m, udev]], 'battery': [[], [udev]], 'clock': [[], []], + 'cpu': [[], []], 'mem': [[], []], 'i3': [['i3-common.c', 'i3-common.h'], [dynlist, json]], 'label': [[], []], diff --git a/plugin.c b/plugin.c index becc152..1faae0e 100644 --- a/plugin.c +++ b/plugin.c @@ -49,6 +49,7 @@ EXTERN_MODULE(sway_xkb); EXTERN_MODULE(script); EXTERN_MODULE(xkb); EXTERN_MODULE(xwindow); +EXTERN_MODULE(cpu); EXTERN_MODULE(mem); EXTERN_PARTICLE(empty); @@ -138,6 +139,7 @@ init(void) REGISTER_CORE_MODULE(xwindow, xwindow); #endif REGISTER_CORE_MODULE(mem, mem); + REGISTER_CORE_MODULE(cpu, cpu); REGISTER_CORE_PARTICLE(empty, empty); REGISTER_CORE_PARTICLE(list, list); From e83c4bd8c196a3713f36cac964316c7a830459d8 Mon Sep 17 00:00:00 2001 From: Alexandre Acebedo Date: Sun, 12 Dec 2021 18:13:52 +0100 Subject: [PATCH 317/611] misc: add format files for clang-format and editorconfig --- .clang-format | 17 +++++++++++++++++ .editorconfig | 17 +++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 .clang-format create mode 100644 .editorconfig diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..528a36b --- /dev/null +++ b/.clang-format @@ -0,0 +1,17 @@ +--- +BasedOnStyle: GNU +IndentWidth: 4 +--- +Language: Cpp +PointerAlignment: Right +ColumnLimit: 120 +BreakBeforeBraces: Custom +BraceWrapping: + AfterEnum: false + AfterClass: false + SplitEmptyFunction: true + AfterFunction: true + AfterStruct: false + +SpaceBeforeParens: ControlStatements +Cpp11BracedListStyle: true diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..ef74858 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,17 @@ +root = true + +[*] +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +charset = utf-8 +indent_style = space +indent_size = 4 +max_line_length = 70 + +[{meson.build,PKGBUILD}] +indent_size = 2 + +[*.scd] +indent_style = tab +trim_trailing_whitespace = false From d9316a202deb1429380952c39310ac021e4a2b94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 26 Dec 2021 12:17:49 +0100 Subject: [PATCH 318/611] module/removables: audio CD support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Audio CDs are special, in that they don’t (usually) have any data partitions. They also don’t have a volume label. They just have tracks. Before this patch, we ignored all optical mediums that did *not* have a filesystem (that includes audio CDs). Now, instead of using the ID_FS_USAGE property to determine whether there’s media present in the CD-ROM or not, we use the ID_CDROM_MEDIA. This property is set to ‘1’ for both audio CDs and data CDs. Then, we read the ID_CDROM_MEDIA_TRACK_COUNT_AUDIO property to determine how many audio tracks there are. If the CD has a filesystem, we treat it as a data CD, and use the already existing add_partition() function to track it. If the CD does _not_ have a filesystem, but it _does_ have at least one audio track, we treat it as an audio CD and use the new add_audio_cd() function to track it. This function is almost identical to add_partition(), but instead of reading the ID_FS_LABEL property, it reads the ID_CDROM_MEDIA_TRACK_COUNT_AUDIO property and synthesizes a label on the form “Audio CD - N tracks”. Finally, a new boolean tag, “audio”, has been added. It is set to true for audio CD “partitions”, and to false in all other cases. --- modules/removables.c | 147 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 121 insertions(+), 26 deletions(-) diff --git a/modules/removables.c b/modules/removables.c index 736317f..6727afa 100644 --- a/modules/removables.c +++ b/modules/removables.c @@ -36,8 +36,8 @@ struct partition { char *label; uint64_t size; + bool audio_cd; - /*tll(char *) mount_points;*/ mount_point_list_t mount_points; }; @@ -142,13 +142,14 @@ content(struct module *mod) tag_new_string(mod, "vendor", p->block->vendor), tag_new_string(mod, "model", p->block->model), tag_new_bool(mod, "optical", p->block->optical), + tag_new_bool(mod, "audio", p->audio_cd), tag_new_string(mod, "device", p->dev_path), tag_new_int_range(mod, "size", p->size, 0, p->block->size), tag_new_string(mod, "label", label), tag_new_bool(mod, "mounted", is_mounted), tag_new_string(mod, "mount_point", mount_point), }, - .count = 8, + .count = 9, }; exposables[idx++] = m->label->instantiate(m->label, &tags); @@ -278,6 +279,64 @@ add_partition(struct module *mod, struct block_device *block, .dev_path = strdup(udev_device_get_devnode(dev)), .label = label != NULL ? strdup(label) : NULL, .size = size, + .audio_cd = false, + .mount_points = tll_init()})); + + struct partition *p = &tll_back(block->partitions); + update_mount_points(p); + mtx_unlock(&mod->lock); + + return p; +} + +static struct partition * +add_audio_cd(struct module *mod, struct block_device *block, + struct udev_device *dev) +{ + struct private *m = mod->private; + const char *_size = udev_device_get_sysattr_value(dev, "size"); + uint64_t size = 0; + if (_size != NULL) + sscanf(_size, "%"SCNu64, &size); + +#if 0 + struct udev_list_entry *e = NULL; + udev_list_entry_foreach(e, udev_device_get_properties_list_entry(dev)) { + LOG_DBG("%s -> %s", udev_list_entry_get_name(e), udev_list_entry_get_value(e)); + } +#endif + + const char *devname = udev_device_get_property_value(dev, "DEVNAME"); + if (devname != NULL) { + tll_foreach(m->ignore, it) { + if (strcmp(it->item, devname) == 0) { + LOG_DBG("ignoring %s because it is on the ignore list", devname); + return NULL; + } + } + } + + const char *_track_count = udev_device_get_property_value( + dev, "ID_CDROM_MEDIA_TRACK_COUNT_AUDIO"); + unsigned long track_count = strtoul(_track_count, NULL, 10); + + char label[64]; + snprintf(label, sizeof(label), "Audio CD - %lu tracks", track_count); + + LOG_INFO("audio CD: add: %s: tracks=%lu, label=%s, size=%"PRIu64, + udev_device_get_devnode(dev), track_count, label, size); + + mtx_lock(&mod->lock); + + tll_push_back( + block->partitions, + ((struct partition){ + .block = block, + .sys_path = strdup(udev_device_get_devpath(dev)), + .dev_path = strdup(udev_device_get_devnode(dev)), + .label = label != NULL ? strdup(label) : NULL, + .size = size, + .audio_cd = true, .mount_points = tll_init()})); struct partition *p = &tll_back(block->partitions); @@ -296,7 +355,9 @@ del_partition(struct module *mod, struct block_device *block, tll_foreach(block->partitions, it) { if (strcmp(it->item.sys_path, sys_path) == 0) { - LOG_INFO("partition: del: %s", it->item.dev_path); + LOG_INFO("%s: del: %s", + it->item.audio_cd ? "audio CD" : "partition", + it->item.dev_path); free_partition(&it->item); tll_remove(block->partitions, it); @@ -351,8 +412,16 @@ add_device(struct module *mod, struct udev_device *dev) const char *_optical = udev_device_get_property_value(dev, "ID_CDROM"); bool optical = _optical != NULL && strcmp(_optical, "1") == 0; + const char *_media = udev_device_get_property_value(dev, "ID_CDROM_MEDIA"); + bool media = _media != NULL && strcmp(_media, "1") == 0; + const char *_fs_usage = udev_device_get_property_value(dev, "ID_FS_USAGE"); - bool media = _fs_usage != NULL && strcmp(_fs_usage, "filesystem") == 0; + bool have_fs = _fs_usage != NULL && strcmp(_fs_usage, "filesystem") == 0; + + const char *_audio_track_count = udev_device_get_property_value( + dev, "ID_CDROM_MEDIA_TRACK_COUNT_AUDIO"); + unsigned long audio_track_count = + _audio_track_count != NULL ? strtoul(_audio_track_count, NULL, 10) : 0; LOG_DBG("device: add: %s: vendor=%s, model=%s, optical=%d, size=%"PRIu64, udev_device_get_devnode(dev), vendor, model, optical, size); @@ -374,8 +443,12 @@ add_device(struct module *mod, struct udev_device *dev) mtx_unlock(&mod->lock); struct block_device *block = &tll_back(m->devices); - if (optical && media) - add_partition(mod, block, dev); + if (optical) { + if (have_fs) + add_partition(mod, block, dev); + else if (audio_track_count > 0) + add_audio_cd(mod, block, dev); + } return &tll_back(m->devices); } @@ -409,31 +482,53 @@ change_device(struct module *mod, struct udev_device *dev) const char *sys_path = udev_device_get_devpath(dev); mtx_lock(&mod->lock); + struct block_device *block = NULL; + tll_foreach(m->devices, it) { if (strcmp(it->item.sys_path, sys_path) == 0) { - LOG_DBG("device: change: %s", it->item.dev_path); - - if (it->item.optical) { - const char *_media = udev_device_get_property_value(dev, "ID_FS_USAGE"); - bool media = _media != NULL && strcmp(_media, "filesystem") == 0; - bool media_change = media != it->item.media; - - it->item.media = media; - mtx_unlock(&mod->lock); - - if (media_change) { - LOG_INFO("device: change: %s: media %s", - it->item.dev_path, media ? "inserted" : "removed"); - - if (media) - return add_partition(mod, &it->item, dev) != NULL; - else - return del_partition(mod, &it->item, dev); - } - } + block = &it->item; + break; } } + if (block == NULL) + goto out; + + LOG_DBG("device: change: %s", block->dev_path); + + if (!block->optical) + goto out; + + const char *_media = udev_device_get_property_value(dev, "ID_CDROM_MEDIA"); + bool media = _media != NULL && strcmp(_media, "1") == 0; + + const char *_fs_usage = udev_device_get_property_value(dev, "ID_FS_USAGE"); + bool have_fs = _fs_usage != NULL && strcmp(_fs_usage, "filesystem") == 0; + + const char *_audio_track_count = udev_device_get_property_value( + dev, "ID_CDROM_MEDIA_TRACK_COUNT_AUDIO"); + unsigned long audio_track_count = + _audio_track_count != NULL ? strtoul(_audio_track_count, NULL, 10) : 0; + + bool media_change = media != block->media; + + block->media = media; + mtx_unlock(&mod->lock); + + if (media_change) { + LOG_INFO("device: change: %s: media %s", + block->dev_path, media ? "inserted" : "removed"); + + if (media) { + if (have_fs) + return add_partition(mod, block, dev) != NULL; + else if (audio_track_count > 0) + return add_audio_cd(mod, block, dev) != NULL; + } else + return del_partition(mod, block, dev); + } + +out: mtx_unlock(&mod->lock); return false; } From 52e2540d42d870aed38b6832fe73596bc5a1db8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 26 Dec 2021 12:25:00 +0100 Subject: [PATCH 319/611] =?UTF-8?q?doc:=20yambar-modules-removables:=20add?= =?UTF-8?q?=20=E2=80=98audio=E2=80=99=20tag?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/yambar-modules-removables.5.scd | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/yambar-modules-removables.5.scd b/doc/yambar-modules-removables.5.scd index 4b984fa..cbffebe 100644 --- a/doc/yambar-modules-removables.5.scd +++ b/doc/yambar-modules-removables.5.scd @@ -22,6 +22,10 @@ instantiates the provided _content_ particle for each detected drive. | optical : bool : True if the drive is an optical drive (CD-ROM, DVD-ROM etc) +| audio +: bool +: True if an optical drive has an audio CD inserted (i.e. this + property is always false for non-optical drives). | device : string : Volume device name (typically */dev/sd?*) From 0aff641d0cd2b6bf05f42306f53df0b6de5cf4af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 26 Dec 2021 12:25:55 +0100 Subject: [PATCH 320/611] changelog: audio CD support --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a834b7..29d3d42 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,9 @@ (https://codeberg.org/dnkl/yambar/issues/139). * mem: a module handling system memory monitoring * cpu: a module offering cpu usage monitoring +* removables: support for audio CDs + (https://codeberg.org/dnkl/yambar/issues/146). +* removables: new boolean tag: `audio`. ### Changed From 2a0a722c1346d87d47ffbdf7d516bbcd29548dd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 19 Dec 2021 21:22:51 +0100 Subject: [PATCH 321/611] bar/wayland: handle layer surface being closed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the output we’re mapped on is disabled (or disconnected), the compositor will unmap us. Up until now, our response was to simply shutdown. Now, we destroy the surface, remove all pending rendering buffers, and all further calls to commit() will return immediately, without doing anything. If the user has configured a specific monitor to use, we wait for that output to come back. When it does, we re-create the layer surface and then we’re up and running again. Bars running on the “default” monitor are handled in a similar way. Since we don’t have an output name from the configuration, we instead store the name of the output we were mapped on, when we’re either unmapped from that output, or that output global is destroyed. As soon as we see that output come back, we re-create the layer surface. --- bar/wayland.c | 245 ++++++++++++++++++++++++++++++++++---------------- 1 file changed, 170 insertions(+), 75 deletions(-) diff --git a/bar/wayland.c b/bar/wayland.c index a5a6731..89e5183 100644 --- a/bar/wayland.c +++ b/bar/wayland.c @@ -46,6 +46,7 @@ struct monitor { struct wl_output *output; struct zxdg_output_v1 *xdg; char *name; + uint32_t wl_name; int x; int y; @@ -96,6 +97,7 @@ struct wayland_backend { tll(struct monitor) monitors; const struct monitor *monitor; + char *last_mapped_monitor; int scale; @@ -113,6 +115,7 @@ struct wayland_backend { tll(struct buffer) buffers; /* List of SHM buffers */ struct buffer *next_buffer; /* Bar is rendering to this one */ struct buffer *pending_buffer; /* Finished, but not yet rendered */ + struct wl_callback *frame_callback; double aggregated_scroll; bool have_discrete; @@ -519,6 +522,9 @@ xdg_output_handle_logical_size(void *data, struct zxdg_output_v1 *xdg_output, mon->height_px = height; } +static bool create_surface(struct wayland_backend *backend); +static void destroy_surface(struct wayland_backend *backend); + static void xdg_output_handle_done(void *data, struct zxdg_output_v1 *xdg_output) { @@ -531,13 +537,40 @@ xdg_output_handle_done(void *data, struct zxdg_output_v1 *xdg_output) struct wayland_backend *backend = mon->backend; struct private *bar = backend->bar->private; - if (bar->monitor != NULL && mon->name != NULL && - strcmp(bar->monitor, mon->name) == 0) - { - /* User specified a monitor, and this is one */ - backend->monitor = mon; + const bool is_mapped = backend->monitor != NULL; + if (is_mapped) { + assert(backend->surface != NULL); + assert(backend->last_mapped_monitor == NULL); + return; } + const bool output_is_our_configured_monitor = ( + bar->monitor != NULL && + mon->name != NULL && + strcmp(bar->monitor, mon->name) == 0); + + const bool output_is_last_mapped = ( + backend->last_mapped_monitor != NULL && + mon->name != NULL && + strcmp(backend->last_mapped_monitor, mon->name) == 0); + + if (output_is_our_configured_monitor) + LOG_DBG("%s: using this monitor (user configured)", mon->name); + else if (output_is_last_mapped) + LOG_DBG("%s: using this monitor (last mapped)", mon->name); + + if (output_is_our_configured_monitor || output_is_last_mapped) { + /* User specified a monitor, and this is one */ + backend->monitor = mon; + + free(backend->last_mapped_monitor); + backend->last_mapped_monitor = NULL; + + if (create_surface(backend) && update_size(backend)) { + if (backend->pipe_fds[1] >= 0) + refresh(backend->bar); + } + } } static void @@ -610,6 +643,7 @@ handle_global(void *data, struct wl_registry *registry, tll_push_back(backend->monitors, ((struct monitor){ .backend = backend, + .wl_name = name, .output = output})); struct monitor *mon = &tll_back(backend->monitors); @@ -679,9 +713,23 @@ handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) } } - LOG_WARN("unknown global removed: 0x%08x", name); + tll_foreach(backend->monitors, it) { + struct monitor *mon = &it->item; + if (mon->wl_name == name) { + LOG_INFO("%s disconnected/disabled", mon->name); - /* TODO: need to handle displays and seats */ + if (mon == backend->monitor) { + assert(backend->last_mapped_monitor == NULL); + backend->last_mapped_monitor = strdup(mon->name); + backend->monitor = NULL; + } + + tll_remove(backend->monitors, it); + return; + } + } + + LOG_WARN("unknown global removed: 0x%08x", name); } static const struct wl_registry_listener registry_listener = { @@ -703,22 +751,10 @@ layer_surface_configure(void *data, struct zwlr_layer_surface_v1 *surface, static void layer_surface_closed(void *data, struct zwlr_layer_surface_v1 *surface) { + LOG_DBG("layer surface closed by compositor"); + struct wayland_backend *backend = data; - - /* - * Called e.g. when an output is disabled. We don't get a - * corresponding event if/when that same output re-appears. So, - * for now, we simply shut down. In the future, we _could_ maybe - * destroy the surface, listen for output events and re-create the - * surface if the same output re-appears. - */ - LOG_WARN("compositor requested surface be closed - shutting down"); - - if (write(backend->bar->abort_fd, &(uint64_t){1}, sizeof(uint64_t)) - != sizeof(uint64_t)) - { - LOG_ERRNO("failed to signal abort to modules"); - } + destroy_surface(backend); } static const struct zwlr_layer_surface_v1_listener layer_surface_listener = { @@ -726,6 +762,82 @@ static const struct zwlr_layer_surface_v1_listener layer_surface_listener = { .closed = &layer_surface_closed, }; +static const struct wl_surface_listener surface_listener; + +static bool +create_surface(struct wayland_backend *backend) +{ + assert(tll_length(backend->monitors) > 0); + assert(backend->surface == NULL); + assert(backend->layer_surface == NULL); + + struct bar *_bar = backend->bar; + struct private *bar = _bar->private; + + backend->surface = wl_compositor_create_surface(backend->compositor); + if (backend->surface == NULL) { + LOG_ERR("failed to create panel surface"); + return false; + } + + wl_surface_add_listener(backend->surface, &surface_listener, backend); + + enum zwlr_layer_shell_v1_layer layer = bar->layer == BAR_LAYER_BOTTOM + ? ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM + : ZWLR_LAYER_SHELL_V1_LAYER_TOP; + + backend->layer_surface = zwlr_layer_shell_v1_get_layer_surface( + backend->layer_shell, backend->surface, + backend->monitor != NULL ? backend->monitor->output : NULL, + layer, "panel"); + + if (backend->layer_surface == NULL) { + LOG_ERR("failed to create layer shell surface"); + return false; + } + + zwlr_layer_surface_v1_add_listener( + backend->layer_surface, &layer_surface_listener, backend); + + /* Aligned to top, maximum width */ + enum zwlr_layer_surface_v1_anchor top_or_bottom = bar->location == BAR_TOP + ? ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP + : ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; + + zwlr_layer_surface_v1_set_anchor( + backend->layer_surface, + ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | + top_or_bottom); + + return true; +} + +static void +destroy_surface(struct wayland_backend *backend) +{ + if (backend->layer_surface != NULL) + zwlr_layer_surface_v1_destroy(backend->layer_surface); + if (backend->surface != NULL) + wl_surface_destroy(backend->surface); + if (backend->frame_callback != NULL) + wl_callback_destroy(backend->frame_callback); + + if (backend->pending_buffer != NULL) + backend->pending_buffer->busy = false; + if (backend->next_buffer != NULL) + backend->next_buffer->busy = false; + + backend->layer_surface = NULL; + backend->surface = NULL; + backend->frame_callback = NULL; + backend->pending_buffer = NULL; + backend->next_buffer = NULL; + + backend->scale = 0; + backend->render_scheduled = false; +} + static void buffer_release(void *data, struct wl_buffer *wl_buffer) { @@ -892,6 +1004,8 @@ update_size(struct wayland_backend *backend) const struct monitor *mon = backend->monitor; const int scale = mon != NULL ? mon->scale : guess_scale(backend); + assert(backend->surface != NULL); + if (backend->scale == scale) return true; @@ -942,8 +1056,6 @@ update_size(struct wayland_backend *backend) return true; } -static const struct wl_surface_listener surface_listener; - static bool setup(struct bar *_bar) { @@ -989,45 +1101,14 @@ setup(struct bar *_bar) /* Trigger listeners registered in previous roundtrip */ wl_display_roundtrip(backend->display); - backend->surface = wl_compositor_create_surface(backend->compositor); - if (backend->surface == NULL) { - LOG_ERR("failed to create panel surface"); - return false; + if (backend->surface == NULL && backend->layer_surface == NULL) { + if (!create_surface(backend)) + return false; + + if (!update_size(backend)) + return false; } - wl_surface_add_listener(backend->surface, &surface_listener, backend); - - enum zwlr_layer_shell_v1_layer layer = bar->layer == BAR_LAYER_BOTTOM - ? ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM - : ZWLR_LAYER_SHELL_V1_LAYER_TOP; - - backend->layer_surface = zwlr_layer_shell_v1_get_layer_surface( - backend->layer_shell, backend->surface, - backend->monitor != NULL ? backend->monitor->output : NULL, - layer, "panel"); - - if (backend->layer_surface == NULL) { - LOG_ERR("failed to create layer shell surface"); - return false; - } - - zwlr_layer_surface_v1_add_listener( - backend->layer_surface, &layer_surface_listener, backend); - - /* Aligned to top, maximum width */ - enum zwlr_layer_surface_v1_anchor top_or_bottom = bar->location == BAR_TOP - ? ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP - : ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; - - zwlr_layer_surface_v1_set_anchor( - backend->layer_surface, - ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | - ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | - top_or_bottom); - - if (!update_size(backend)) - return false; - assert(backend->monitor == NULL || backend->width / backend->monitor->scale <= backend->monitor->width_px); @@ -1051,16 +1132,6 @@ cleanup(struct bar *_bar) if (backend->pipe_fds[1] >= 0) close(backend->pipe_fds[1]); - tll_foreach(backend->buffers, it) { - if (it->item.wl_buf != NULL) - wl_buffer_destroy(it->item.wl_buf); - if (it->item.pix != NULL) - pixman_image_unref(it->item.pix); - - munmap(it->item.mmapped, it->item.size); - tll_remove(backend->buffers, it); - } - tll_foreach(backend->monitors, it) { struct monitor *mon = &it->item; free(mon->name); @@ -1071,6 +1142,7 @@ cleanup(struct bar *_bar) wl_output_release(mon->output); tll_remove(backend->monitors, it); } + free(backend->last_mapped_monitor); if (backend->xdg_output_manager != NULL) zxdg_output_manager_v1_destroy(backend->xdg_output_manager); @@ -1079,12 +1151,20 @@ cleanup(struct bar *_bar) seat_destroy(&it->item); tll_free(backend->seats); - if (backend->layer_surface != NULL) - zwlr_layer_surface_v1_destroy(backend->layer_surface); + destroy_surface(backend); + + tll_foreach(backend->buffers, it) { + if (it->item.wl_buf != NULL) + wl_buffer_destroy(it->item.wl_buf); + if (it->item.pix != NULL) + pixman_image_unref(it->item.pix); + + munmap(it->item.mmapped, it->item.size); + tll_remove(backend->buffers, it); + } + if (backend->layer_shell != NULL) zwlr_layer_shell_v1_destroy(backend->layer_shell); - if (backend->surface != NULL) - wl_surface_destroy(backend->surface); if (backend->compositor != NULL) wl_compositor_destroy(backend->compositor); if (backend->shm != NULL) @@ -1209,7 +1289,15 @@ surface_leave(void *data, struct wl_surface *wl_surface, struct wl_output *wl_output) { struct wayland_backend *backend = data; + const struct monitor *mon = backend->monitor; + + assert(mon != NULL); + assert(mon->output == wl_output); + backend->monitor = NULL; + + assert(backend->last_mapped_monitor == NULL); + backend->last_mapped_monitor = mon->name != NULL ? strdup(mon->name) : NULL; } static const struct wl_surface_listener surface_listener = { @@ -1233,7 +1321,9 @@ frame_callback(void *data, struct wl_callback *wl_callback, uint32_t callback_da backend->render_scheduled = false; + assert(wl_callback == backend->frame_callback); wl_callback_destroy(wl_callback); + backend->frame_callback = NULL; if (backend->pending_buffer != NULL) { struct buffer *buffer = backend->pending_buffer; @@ -1248,6 +1338,7 @@ frame_callback(void *data, struct wl_callback *wl_callback, uint32_t callback_da wl_surface_commit(backend->surface); wl_display_flush(backend->display); + backend->frame_callback = cb; backend->pending_buffer = NULL; backend->render_scheduled = true; } else @@ -1262,6 +1353,9 @@ commit(const struct bar *_bar) //printf("commit: %dxl%d\n", backend->width, backend->height); + if (backend->next_buffer == NULL) + return; + assert(backend->next_buffer != NULL); assert(backend->next_buffer->busy); @@ -1289,6 +1383,7 @@ commit(const struct bar *_bar) wl_display_flush(backend->display); backend->render_scheduled = true; + backend->frame_callback = cb; } backend->next_buffer = get_buffer(backend); From 56c4a1c751832c23bba0d9aef3932d59a3dcdd9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 25 Dec 2021 21:11:11 +0100 Subject: [PATCH 322/611] bar/wayland: add support for new events in wl-output v4 * name() * description() --- bar/wayland.c | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/bar/wayland.c b/bar/wayland.c index 89e5183..7894d00 100644 --- a/bar/wayland.c +++ b/bar/wayland.c @@ -495,12 +495,35 @@ output_scale(void *data, struct wl_output *wl_output, int32_t factor) } } +#if defined(WL_OUTPUT_NAME_SINCE_VERSION) +static void +output_name(void *data, struct wl_output *wl_output, const char *name) +{ + struct monitor *mon = data; + free(mon->name); + mon->name = name != NULL ? strdup(name) : NULL; +} +#endif + +#if defined(WL_OUTPUT_DESCRIPTION_SINCE_VERSION) +static void +output_description(void *data, struct wl_output *wl_output, + const char *description) +{ +} +#endif static const struct wl_output_listener output_listener = { .geometry = &output_geometry, .mode = &output_mode, .done = &output_done, .scale = &output_scale, +#if defined(WL_OUTPUT_NAME_SINCE_VERSION) + .name = &output_name, +#endif +#if defined(WL_OUTPUT_DESCRIPTION_SINCE_VERSION) + .description = &output_description, +#endif }; static void @@ -1431,7 +1454,7 @@ set_cursor(struct bar *_bar, const char *cursor) } static const char * -output_name(const struct bar *_bar) +bar_output_name(const struct bar *_bar) { const struct private *bar = _bar->private; const struct wayland_backend *backend = bar->backend.data; @@ -1446,5 +1469,5 @@ const struct backend wayland_backend_iface = { .commit = &commit, .refresh = &refresh, .set_cursor = &set_cursor, - .output_name = &output_name, + .output_name = &bar_output_name, }; From f053ddff7db9a829706d5d0f2238ba1412c5fa9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 1 Jan 2022 11:42:12 +0100 Subject: [PATCH 323/611] changelog: bar does not exit when monitor is disabled/unplugged --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 29d3d42..fa7a875 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,8 @@ * Made `libmpdclient` an optional dependency * battery: unknown battery states are now mapped to ‘unknown’, instead of ‘discharging’. +* Wayland: the bar no longer exits when the monitor is + disabled/unplugged (https://codeberg.org/dnkl/yambar/issues/106). ### Deprecated From 1d9297593e0d78d87a88e74aaed83c8cff285e5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 9 Jan 2022 23:12:52 +0100 Subject: [PATCH 324/611] bar/wayland: error handling when dispatching Wayland events --- bar/wayland.c | 46 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/bar/wayland.c b/bar/wayland.c index 7894d00..a57fdfa 100644 --- a/bar/wayland.c +++ b/bar/wayland.c @@ -1212,13 +1212,19 @@ loop(struct bar *_bar, { struct private *bar = _bar->private; struct wayland_backend *backend = bar->backend.data; + bool send_abort_to_modules = true; pthread_setname_np(pthread_self(), "bar(wayland)"); backend->bar_on_mouse = on_mouse; - while (wl_display_prepare_read(backend->display) != 0) - wl_display_dispatch_pending(backend->display); + while (wl_display_prepare_read(backend->display) != 0) { + if (wl_display_dispatch_pending(backend->display) < 0) { + LOG_ERRNO("failed to dispatch pending Wayland events"); + goto out; + } + } + wl_display_flush(backend->display); while (true) { @@ -1230,16 +1236,13 @@ loop(struct bar *_bar, poll(fds, sizeof(fds) / sizeof(fds[0]), -1); if (fds[0].revents & POLLIN) { + /* Already done by the bar */ + send_abort_to_modules = false; break; } if (fds[1].revents & POLLHUP) { LOG_INFO("disconnected from wayland"); - if (write(_bar->abort_fd, &(uint64_t){1}, sizeof(uint64_t)) - != sizeof(uint64_t)) - { - LOG_ERRNO("failed to signal abort to modules"); - } break; } @@ -1253,9 +1256,10 @@ loop(struct bar *_bar, ssize_t r = read(backend->pipe_fds[0], &command, sizeof(command)); if (r < 0 && errno == EAGAIN) break; + if (r != sizeof(command)) { LOG_ERRNO("failed to read from command pipe"); - break; + goto out; } assert(command == 1); @@ -1271,15 +1275,33 @@ loop(struct bar *_bar, } if (fds[1].revents & POLLIN) { - wl_display_read_events(backend->display); + if (wl_display_read_events(backend->display) < 0) { + LOG_ERRNO("failed to read events from the Wayland socket"); + goto out; + } + + while (wl_display_prepare_read(backend->display) != 0) { + if (wl_display_dispatch_pending(backend->display) < 0) { + LOG_ERRNO("failed to dispatch pending Wayland events"); + goto out; + } + } - while (wl_display_prepare_read(backend->display) != 0) - wl_display_dispatch_pending(backend->display); wl_display_flush(backend->display); } } - wl_display_cancel_read(backend->display); +out: + if (!send_abort_to_modules) + return; + + if (write(_bar->abort_fd, &(uint64_t){1}, sizeof(uint64_t)) + != sizeof(uint64_t)) + { + LOG_ERRNO("failed to signal abort to modules"); + } + + //wl_display_cancel_read(backend->display); } static void From 7945a561d0d53099735707ee8d4ab33c821090d0 Mon Sep 17 00:00:00 2001 From: "Soc Virnyl S. Estela" Date: Mon, 17 Jan 2022 09:58:11 +0800 Subject: [PATCH 325/611] Add river-tags example --- examples/configurations/river-tags.conf | 60 +++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 examples/configurations/river-tags.conf diff --git a/examples/configurations/river-tags.conf b/examples/configurations/river-tags.conf new file mode 100644 index 0000000..94e5874 --- /dev/null +++ b/examples/configurations/river-tags.conf @@ -0,0 +1,60 @@ +hack: &hack Hack Nerd Font:pixelsize=13 +bg_default: &bg_default {stack: [{background: {color: 81A1C1ff}}, {underline: {size: 4, color: D8DEE9ff}}]} +bar: + height: 40 + location: top + font: JuliaMono:pixelsize=10 + spacing: 2 + margin: 0 + layer: bottom + foreground: eeeeeeff + background: 2E3440dd + + left: + - river: + anchors: + - base: &river_base + left-margin: 10 + right-margin: 13 + tag: id + default: {string: {text: , font: *hack}} + values: + 1: {string: {text: ﳐ, font: *hack}} + 2: {string: {text: , font: *hack}} + 3: {string: {text: , font: *hack}} + 4: {string: {text: , font: *hack}} + 5: {string: {text: , font: *hack}} + 10: {string: {text: "scratchpad", font: *hack}} + 11: {string: {text: "work", font: *hack}} + + content: + map: + on-click: + left: sh -c "riverctl set-focused-tags $((1 << ({id} - 1)))" + right: sh -c "riverctl toggle-focused-tags $((1 << ({id} -1)))" + middle: sh -c "riverctl toggle-view-tags $((1 << ({id} -1)))" + tag: state + values: + urgent: + map: + <<: *river_base + deco: {background: {color: D08770ff}} + focused: + map: + <<: *river_base + deco: *bg_default + visible: + map: + tag: occupied + values: + false: {map: {<<: *river_base}} + true: {map: {<<: *river_base, deco: *bg_default}} + unfocused: + map: + <<: *river_base + invisible: + map: + tag: occupied + values: + false: {empty: {}} + true: {map: {<<: *river_base, deco: {underline: {size: 3, color: ea6962ff}}}} From 52e0b5f3d1bb8337b530d02e5eeecd72a32632e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 1 Feb 2022 19:01:04 +0100 Subject: [PATCH 326/611] tag: fix inverted logic for KiB vs KB logic --- CHANGELOG.md | 1 + tag.c | 12 ++++++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fa7a875..1a11471 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,6 +50,7 @@ allowed, leading to various bad things; including yambar crashing, or worse, the compositor crashing (https://codeberg.org/dnkl/yambar/issues/129). +* kib/kb, mib/mb and gib/gb formatters were inverted. ### Security diff --git a/tag.c b/tag.c index 2943423..66b7f02 100644 --- a/tag.c +++ b/tag.c @@ -545,12 +545,12 @@ tags_expand_template(const char *template, const struct tag_set *tags) case FMT_MIBYTE: case FMT_GIBYTE: { const long divider = - format == FMT_KBYTE ? 1024 : - format == FMT_MBYTE ? 1024 * 1024 : - format == FMT_GBYTE ? 1024 * 1024 * 1024 : - format == FMT_KIBYTE ? 1000 : - format == FMT_MIBYTE ? 1000 * 1000 : - format == FMT_GIBYTE ? 1000 * 1000 * 1000 : + format == FMT_KBYTE ? 1000 : + format == FMT_MBYTE ? 1000 * 1000 : + format == FMT_GBYTE ? 1000 * 1000 * 1000 : + format == FMT_KIBYTE ? 1024 : + format == FMT_MIBYTE ? 1024 * 1024 : + format == FMT_GIBYTE ? 1024 * 1024 * 1024 : 1; char str[24]; From 4fea561c6c0b899c8874cbd2d09abfd2ee6a9cc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 5 Feb 2022 12:50:39 +0100 Subject: [PATCH 327/611] log: fold long line --- log.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/log.c b/log.c index 467b6fd..5864853 100644 --- a/log.c +++ b/log.c @@ -40,7 +40,9 @@ log_init(enum log_colorize _colorize, bool _do_syslog, [LOG_FACILITY_DAEMON] = LOG_DAEMON, }; - colorize = _colorize == LOG_COLORIZE_NEVER ? false : _colorize == LOG_COLORIZE_ALWAYS ? true : isatty(STDERR_FILENO); + colorize = _colorize == LOG_COLORIZE_NEVER + ? false : _colorize == LOG_COLORIZE_ALWAYS + ? true : isatty(STDERR_FILENO); do_syslog = _do_syslog; log_level = _log_level; From ff238e62ba44bd749cc671d6b409f7203c46f15d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 3 Sep 2021 20:44:22 +0200 Subject: [PATCH 328/611] fcft: adapt to API changes in fcft-3.x --- .builds/alpine-x64.yml | 5 +++ .gitlab-ci.yml | 20 +++++++++++ .woodpecker.yml | 1 + CHANGELOG.md | 2 +- PKGBUILD | 2 +- PKGBUILD.wayland-only | 2 +- char32.c | 78 ++++++++++++++++++++++++++++++++++++++++++ char32.h | 7 ++++ main.c | 5 +-- meson.build | 3 +- particles/string.c | 27 +++++++-------- 11 files changed, 131 insertions(+), 21 deletions(-) create mode 100644 char32.c create mode 100644 char32.h diff --git a/.builds/alpine-x64.yml b/.builds/alpine-x64.yml index a5d4e9c..687af0e 100644 --- a/.builds/alpine-x64.yml +++ b/.builds/alpine-x64.yml @@ -37,6 +37,11 @@ tasks: pip install codespell cd yambar ~/.local/bin/codespell README.md CHANGELOG.md *.c *.h doc/*.scd + - fcft: | + cd yambar/subprojects + git clone https://codeberg.org/dnkl/fcft.git + cd fcft && git checkout 3.0-dev && cd .. + cd ../.. - setup: | mkdir -p bld/debug bld/release bld/x11-only bld/wayland-only bld/plugs-are-shared meson --buildtype=debug -Db_coverage=true yambar bld/debug diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 06df201..e5762c1 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -27,6 +27,10 @@ versions: debug: stage: build script: + - cd subprojects + - git clone https://codeberg.org/dnkl/fcft.git + - cd fcft && git checkout 3.0-dev && cd .. + - cd .. - apk add gcovr - mkdir -p bld/debug - cd bld/debug @@ -55,6 +59,10 @@ debug: release: stage: build script: + - cd subprojects + - git clone https://codeberg.org/dnkl/fcft.git + - cd fcft && git checkout 3.0-dev && cd .. + - cd .. - mkdir -p bld/release - cd bld/release - meson --buildtype=minsize ../../ @@ -64,6 +72,10 @@ release: x11_only: stage: build script: + - cd subprojects + - git clone https://codeberg.org/dnkl/fcft.git + - cd fcft && git checkout 3.0-dev && cd .. + - cd .. - mkdir -p bld/debug - cd bld/debug - meson --buildtype=debug -Dbackend-x11=enabled -Dbackend-wayland=disabled ../../ @@ -73,6 +85,10 @@ x11_only: wayland_only: stage: build script: + - cd subprojects + - git clone https://codeberg.org/dnkl/fcft.git + - cd fcft && git checkout 3.0-dev && cd .. + - cd .. - mkdir -p bld/debug - cd bld/debug - meson --buildtype=debug -Dbackend-x11=disabled -Dbackend-wayland=enabled ../../ @@ -82,6 +98,10 @@ wayland_only: plugins_as_shared_modules: stage: build script: + - cd subprojects + - git clone https://codeberg.org/dnkl/fcft.git + - cd fcft && git checkout 3.0-dev && cd .. + - cd .. - mkdir -p bld/debug - cd bld/debug - meson --buildtype=debug -Dcore-plugins-as-shared-libraries=true ../../ diff --git a/.woodpecker.yml b/.woodpecker.yml index 4ea84f1..7c0a7ba 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -22,6 +22,7 @@ pipeline: - mkdir -p subprojects && cd subprojects - git clone https://codeberg.org/dnkl/tllist.git - git clone https://codeberg.org/dnkl/fcft.git + - cd fcft && git checkout 3.0-dev && cd .. - cd .. x64: diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a11471..5d47017 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ ### Changed +* fcft >= 3.0 is now required. * Made `libmpdclient` an optional dependency * battery: unknown battery states are now mapped to ‘unknown’, instead of ‘discharging’. @@ -85,7 +86,6 @@ * network: `ssid`, `signal`, `rx-bitrate` and `rx-bitrate` tags. * network: `poll-interval` option (for the new `signal` and `*-bitrate` tags). -* tags: percentage formatter, for range tags: `{tag_name:%}`. * tags: percentage tag formatter, for range tags: `{tag_name:%}`. * tags: kb/mb/gb, and kib/mib/gib tag formatters. * clock: add a config option to show UTC time. diff --git a/PKGBUILD b/PKGBUILD index e4b343b..42bf855 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -15,7 +15,7 @@ depends=( 'libudev.so' 'json-c' 'libmpdclient' - 'fcft>=2.4.0') + 'fcft>=3.0.0' 'fcft<4.0.0') optdepends=('xcb-util-errors: better X error messages') source=() diff --git a/PKGBUILD.wayland-only b/PKGBUILD.wayland-only index d0963e4..a3bbd5d 100644 --- a/PKGBUILD.wayland-only +++ b/PKGBUILD.wayland-only @@ -16,7 +16,7 @@ depends=( 'libudev.so' 'json-c' 'libmpdclient' - 'fcft>=2.4.0') + 'fcft>=3.0.0' 'fcft<4.0.0') source=() pkgver() { diff --git a/char32.c b/char32.c new file mode 100644 index 0000000..4673779 --- /dev/null +++ b/char32.c @@ -0,0 +1,78 @@ +#include "char32.h" + +#include +#include +#include + +#include + +#define LOG_MODULE "char32" +#define LOG_ENABLE_DBG 0 +#include "log.h" + +/* + * For now, assume we can map directly to the corresponding wchar_t + * functions. This is true if: + * + * - both data types have the same size + * - both use the same encoding (though we require that encoding to be UTF-32) + */ + +_Static_assert( + sizeof(wchar_t) == sizeof(char32_t), "wchar_t vs. char32_t size mismatch"); + +#if !defined(__STDC_UTF_32__) || !__STDC_UTF_32__ + #error "char32_t does not use UTF-32" +#endif +#if (!defined(__STDC_ISO_10646__) || !__STDC_ISO_10646__) && !defined(__FreeBSD__) + #error "wchar_t does not use UTF-32" +#endif + +size_t +c32len(const char32_t *s) +{ + return wcslen((const wchar_t *)s); +} + +char32_t * +ambstoc32(const char *src) +{ + if (src == NULL) + return NULL; + + const size_t src_len = strlen(src); + + char32_t *ret = malloc((src_len + 1) * sizeof(ret[0])); + if (ret == NULL) + return NULL; + + mbstate_t ps = {0}; + char32_t *out = ret; + const char *in = src; + const char *const end = src + src_len + 1; + + size_t chars = 0; + size_t rc; + + while ((rc = mbrtoc32(out, in, end - in, &ps)) != 0) { + switch (rc) { + case (size_t)-1: + case (size_t)-2: + case (size_t)-3: + goto err; + } + + in += rc; + out++; + chars++; + } + + *out = U'\0'; + + ret = realloc(ret, (chars + 1) * sizeof(ret[0])); + return ret; + +err: + free(ret); + return NULL; +} diff --git a/char32.h b/char32.h new file mode 100644 index 0000000..b01a7d3 --- /dev/null +++ b/char32.h @@ -0,0 +1,7 @@ +#pragma once + +#include +#include + +size_t c32len(const char32_t *s); +char32_t *ambstoc32(const char *src); diff --git a/main.c b/main.c index 49d3985..7aab81a 100644 --- a/main.c +++ b/main.c @@ -296,8 +296,9 @@ main(int argc, char *const *argv) "fcft log level enum offset"); _Static_assert((int)LOG_COLORIZE_ALWAYS == (int)FCFT_LOG_COLORIZE_ALWAYS, "fcft colorize enum mismatch"); - fcft_log_init( - (enum fcft_log_colorize)log_colorize, log_syslog, (enum fcft_log_class)log_level); + fcft_init((enum fcft_log_colorize)log_colorize, log_syslog, + (enum fcft_log_class)log_level); + atexit(&fcft_fini); const struct sigaction sa = {.sa_handler = &signal_handler}; sigaction(SIGINT, &sa, NULL); diff --git a/meson.build b/meson.build index 66f200c..7b4aa3b 100644 --- a/meson.build +++ b/meson.build @@ -71,7 +71,7 @@ backend_wayland = wayland_client.found() and wayland_cursor.found() # "My" dependencies, fallback to subproject tllist = dependency('tllist', version: '>=1.0.1', fallback: 'tllist') -fcft = dependency('fcft', version: ['>=2.4.0', '<3.0.0'], fallback: 'fcft') +fcft = dependency('fcft', version: ['>=3.0.0', '<4.0.0'], fallback: 'fcft') add_project_arguments( ['-D_GNU_SOURCE'] + @@ -111,6 +111,7 @@ version = custom_target( yambar = executable( 'yambar', + 'char32.c', 'char32.h', 'color.h', 'config-verify.c', 'config-verify.h', 'config.c', 'config.h', diff --git a/particles/string.c b/particles/string.c index f6083f6..c5ade9f 100644 --- a/particles/string.c +++ b/particles/string.c @@ -5,6 +5,7 @@ #define LOG_MODULE "string" #define LOG_ENABLE_DBG 0 #include "../log.h" +#include "../char32.h" #include "../config.h" #include "../config-verify.h" #include "../particle.h" @@ -87,7 +88,7 @@ expose(const struct exposable *exposable, pixman_image_t *pix, int x, int y, int * its descent. This way, the part of the font *above* the * baseline is centered. * - * "EEEE" will typically be dead center, with the middle of each character being in the bar's center. + * "EEEE" will typically be dead center, with the middle of each character being in the bar's center. * "eee" will be slightly below the center. * "jjj" will be even further below the center. * @@ -149,7 +150,7 @@ instantiate(const struct particle *particle, const struct tag_set *tags) struct eprivate *e = calloc(1, sizeof(*e)); struct fcft_font *font = particle->font; - wchar_t *wtext = NULL; + char32_t *wtext = NULL; char *text = tags_expand_template(p->text, tags); e->glyphs = e->allocated_glyphs = NULL; @@ -173,17 +174,13 @@ instantiate(const struct particle *particle, const struct tag_set *tags) } } - /* Not in cache - we need to rasterize it. First, convert to wchar */ - size_t chars = mbstowcs(NULL, text, 0); - if (chars == (size_t)-1) - goto done; - - wtext = malloc((chars + 1) * sizeof(wtext[0])); - mbstowcs(wtext, text, chars + 1); + /* Not in cache - we need to rasterize it. First, convert to char32_t */ + wtext = ambstoc32(text); + size_t chars = c32len(wtext); /* Truncate, if necessary */ if (p->max_len > 0) { - const size_t len = wcslen(wtext); + const size_t len = c32len(wtext); if (len > p->max_len) { size_t end = p->max_len; @@ -193,11 +190,11 @@ instantiate(const struct particle *particle, const struct tag_set *tags) } if (p->max_len > 1) { - wtext[end] = L'…'; - wtext[end + 1] = L'\0'; + wtext[end] = U'…'; + wtext[end + 1] = U'\0'; chars = end + 1; } else { - wtext[end] = L'\0'; + wtext[end] = U'\0'; chars = 0; } } @@ -206,7 +203,7 @@ instantiate(const struct particle *particle, const struct tag_set *tags) e->kern_x = calloc(chars, sizeof(e->kern_x[0])); if (fcft_capabilities() & FCFT_CAPABILITY_TEXT_RUN_SHAPING) { - struct fcft_text_run *run = fcft_text_run_rasterize( + struct fcft_text_run *run = fcft_rasterize_text_run_utf32( font, chars, wtext, FCFT_SUBPIXEL_NONE); if (run != NULL) { @@ -250,7 +247,7 @@ instantiate(const struct particle *particle, const struct tag_set *tags) /* Convert text to glyph masks/images. */ for (size_t i = 0; i < chars; i++) { - const struct fcft_glyph *glyph = fcft_glyph_rasterize( + const struct fcft_glyph *glyph = fcft_rasterize_char_utf32( font, wtext[i], FCFT_SUBPIXEL_NONE); if (glyph == NULL) From 227f9c608adec7b9b23316ac359e0896124eccf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 5 Feb 2022 17:54:39 +0100 Subject: [PATCH 329/611] ci: use fcft master branch --- .builds/alpine-x64.yml | 1 - .gitlab-ci.yml | 5 ----- .woodpecker.yml | 1 - 3 files changed, 7 deletions(-) diff --git a/.builds/alpine-x64.yml b/.builds/alpine-x64.yml index 687af0e..fc657ce 100644 --- a/.builds/alpine-x64.yml +++ b/.builds/alpine-x64.yml @@ -40,7 +40,6 @@ tasks: - fcft: | cd yambar/subprojects git clone https://codeberg.org/dnkl/fcft.git - cd fcft && git checkout 3.0-dev && cd .. cd ../.. - setup: | mkdir -p bld/debug bld/release bld/x11-only bld/wayland-only bld/plugs-are-shared diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e5762c1..be21159 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -29,7 +29,6 @@ debug: script: - cd subprojects - git clone https://codeberg.org/dnkl/fcft.git - - cd fcft && git checkout 3.0-dev && cd .. - cd .. - apk add gcovr - mkdir -p bld/debug @@ -61,7 +60,6 @@ release: script: - cd subprojects - git clone https://codeberg.org/dnkl/fcft.git - - cd fcft && git checkout 3.0-dev && cd .. - cd .. - mkdir -p bld/release - cd bld/release @@ -74,7 +72,6 @@ x11_only: script: - cd subprojects - git clone https://codeberg.org/dnkl/fcft.git - - cd fcft && git checkout 3.0-dev && cd .. - cd .. - mkdir -p bld/debug - cd bld/debug @@ -87,7 +84,6 @@ wayland_only: script: - cd subprojects - git clone https://codeberg.org/dnkl/fcft.git - - cd fcft && git checkout 3.0-dev && cd .. - cd .. - mkdir -p bld/debug - cd bld/debug @@ -100,7 +96,6 @@ plugins_as_shared_modules: script: - cd subprojects - git clone https://codeberg.org/dnkl/fcft.git - - cd fcft && git checkout 3.0-dev && cd .. - cd .. - mkdir -p bld/debug - cd bld/debug diff --git a/.woodpecker.yml b/.woodpecker.yml index 7c0a7ba..4ea84f1 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -22,7 +22,6 @@ pipeline: - mkdir -p subprojects && cd subprojects - git clone https://codeberg.org/dnkl/tllist.git - git clone https://codeberg.org/dnkl/fcft.git - - cd fcft && git checkout 3.0-dev && cd .. - cd .. x64: From 973aa929c1346dbe7ff74847d4a00b8f154d6fd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 5 Feb 2022 17:57:17 +0100 Subject: [PATCH 330/611] changelog: prepare for 1.8.0 --- CHANGELOG.md | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d47017..dc5a277 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -* [Unreleased](#unreleased) +* [1.8.0](#1-8-0) * [1.7.0](#1-7-0) * [1.6.2](#1-6-2) * [1.6.1](#1-6-1) @@ -8,7 +8,8 @@ * [1.5.0](#1-5-0) -## Unreleased +## Unrelease1.8.0 + ### Added * ramp: can now have custom min and max values @@ -33,8 +34,6 @@ disabled/unplugged (https://codeberg.org/dnkl/yambar/issues/106). -### Deprecated -### Removed ### Fixed * `left-margin` and `right-margin` from being rejected as invalid @@ -54,10 +53,21 @@ * kib/kb, mib/mb and gib/gb formatters were inverted. -### Security ### Contributors * [sochotnicky](https://codeberg.org/sochotnicky) +* Alexandre Acebedo +* anb +* Baptiste Daroussin +* Catterwocky +* horus645 +* Jan Beich +* mz +* natemaia +* nogerine +* Soc Virnyl S. Estela +* Vincent Fischer + ## 1.7.0 From 5edc226c0094e2ad5c7882c43ae2efeb837b75fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 5 Feb 2022 17:57:40 +0100 Subject: [PATCH 331/611] meson/pkgbuild: bump version to 1.8.0 --- PKGBUILD | 2 +- PKGBUILD.wayland-only | 2 +- meson.build | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/PKGBUILD b/PKGBUILD index 42bf855..13ddc83 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -1,5 +1,5 @@ pkgname=yambar -pkgver=1.7.0 +pkgver=1.8.0 pkgrel=1 pkgdesc="Simplistic and highly configurable status panel for X and Wayland" arch=('x86_64' 'aarch64') diff --git a/PKGBUILD.wayland-only b/PKGBUILD.wayland-only index a3bbd5d..5dc6cfd 100644 --- a/PKGBUILD.wayland-only +++ b/PKGBUILD.wayland-only @@ -1,5 +1,5 @@ pkgname=yambar-wayland -pkgver=1.7.0 +pkgver=1.8.0 pkgrel=1 pkgdesc="Simplistic and highly configurable status panel for Wayland" arch=('x86_64' 'aarch64') diff --git a/meson.build b/meson.build index 7b4aa3b..4a479ec 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('yambar', 'c', - version: '1.7.0', + version: '1.8.0', license: 'MIT', meson_version: '>=0.53.0', default_options: ['c_std=c18', From 3cc142a27302adbb4a314ea95c901a5216b06d98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 5 Feb 2022 18:00:01 +0100 Subject: [PATCH 332/611] changelog: fix 1.8.0 header --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dc5a277..ab31475 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ * [1.5.0](#1-5-0) -## Unrelease1.8.0 +## 1.8.0 ### Added From 2cfe45ee812407dae5fdafe4f82510cdaa024c82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 5 Feb 2022 18:02:50 +0100 Subject: [PATCH 333/611] =?UTF-8?q?changelog:=20add=20new=20=E2=80=98unrel?= =?UTF-8?q?eased=E2=80=99=20section?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ab31475..58b6346 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # Changelog +* [Unreleased](#unreleased) * [1.8.0](#1-8-0) * [1.7.0](#1-7-0) * [1.6.2](#1-6-2) @@ -8,6 +9,16 @@ * [1.5.0](#1-5-0) +## Unreleased +### Added +### Changed +### Deprecated +### Removed +### Fixed +### Security +### Contributors + + ## 1.8.0 ### Added From 6ac046dec31353cce847fb7a6b1c4cc99aedd84f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 10 Feb 2022 18:30:19 +0100 Subject: [PATCH 334/611] config: implement font fallback Fonts in the configuration may now be a comma separated list of fonts (all using the fontconfig format). The first font is the primary font, and the rest are fallback fonts that will be searched, in order. --- CHANGELOG.md | 3 +++ config.c | 34 +++++++++++++++++++++++++++++++++- doc/yambar.5.scd | 9 +++++++-- 3 files changed, 43 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 58b6346..43c95a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,9 @@ ## Unreleased ### Added + +* Support for custom font fallbacks + (https://codeberg.org/dnkl/yambar/issues/153). ### Changed ### Deprecated ### Removed diff --git a/config.c b/config.c index 7f9583e..580781b 100644 --- a/config.c +++ b/config.c @@ -4,6 +4,7 @@ #include #include #include +#include #include @@ -66,7 +67,38 @@ conf_to_color(const struct yml_node *node) struct fcft_font * conf_to_font(const struct yml_node *node) { - return fcft_from_name(1, &(const char *){yml_value_as_string(node)}, NULL); + const char *font_spec = yml_value_as_string(node); + + size_t count = 0; + size_t size = 0; + const char **fonts = NULL; + + char *copy = strdup(font_spec); + for (const char *font = strtok(copy, ","); + font != NULL; + font = strtok(NULL, ",")) + { + /* Trim spaces, strictly speaking not necessary, but looks nice :) */ + while (isspace(font[0])) + font++; + + if (font[0] == '\0') + continue; + + if (count + 1 > size) { + size += 4; + fonts = realloc(fonts, size * sizeof(fonts[0])); + } + + assert(count + 1 <= size); + fonts[count++] = font; + } + + struct fcft_font *ret = fcft_from_name(count, fonts, NULL); + + free(fonts); + free(copy); + return ret; } struct deco * diff --git a/doc/yambar.5.scd b/doc/yambar.5.scd index d3a585f..4e70ef2 100644 --- a/doc/yambar.5.scd +++ b/doc/yambar.5.scd @@ -12,7 +12,8 @@ and reference them using anchors. Besides the normal yaml types, there are a couple of yambar specific types that are frequently used: -- *font*: this is a string in _fontconfig_ format. Example of valid values: +- *font*: this is a comma separated list of fonts in _fontconfig_ + format. Example of valid values: - Font Awesome 5 Brands - Font Awesome 5 Free:style=solid - Dina:pixelsize=10:slant=italic @@ -124,7 +125,11 @@ types that are frequently used: | font : font : no -: Default font to use in modules and particles +: Default font to use in modules and particles. May also be a comma + separated list of several fonts, in which case the first font is + the primary font, and the rest fallback fonts. These are yambar + custom fallback fonts that will be searched before the fontconfig + provided fallback list. | foreground : color : no From 605490c872d637d95424b8add5ad12f22263c417 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 10 Feb 2022 18:35:45 +0100 Subject: [PATCH 335/611] overline: new decoration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Similar to the ‘underline’ decoration --- CHANGELOG.md | 4 ++ decorations/meson.build | 2 +- decorations/overline.c | 72 ++++++++++++++++++++++++++++++++++++ doc/yambar-decorations.5.scd | 32 ++++++++++++++++ plugin.c | 2 + 5 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 decorations/overline.c diff --git a/CHANGELOG.md b/CHANGELOG.md index 43c95a1..1f8113d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,10 @@ * Support for custom font fallbacks (https://codeberg.org/dnkl/yambar/issues/153). +* overline: new decoration + (https://codeberg.org/dnkl/yambar/issues/153). + + ### Changed ### Deprecated ### Removed diff --git a/decorations/meson.build b/decorations/meson.build index e8b289c..c64164c 100644 --- a/decorations/meson.build +++ b/decorations/meson.build @@ -1,7 +1,7 @@ deco_sdk = declare_dependency(dependencies: [pixman, tllist, fcft]) decorations = [] -foreach deco : ['background', 'border', 'stack', 'underline'] +foreach deco : ['background', 'border', 'stack', 'underline', 'overline'] if plugs_as_libs shared_module('@0@'.format(deco), '@0@.c'.format(deco), dependencies: deco_sdk, diff --git a/decorations/overline.c b/decorations/overline.c new file mode 100644 index 0000000..1beb896 --- /dev/null +++ b/decorations/overline.c @@ -0,0 +1,72 @@ +#include + +#include "../config.h" +#include "../config-verify.h" +#include "../decoration.h" +#include "../plugin.h" + +struct private { + int size; + pixman_color_t color; +}; + +static void +destroy(struct deco *deco) +{ + struct private *d = deco->private; + free(d); + free(deco); +} + +static void +expose(const struct deco *deco, pixman_image_t *pix, int x, int y, int width, int height) +{ + const struct private *d = deco->private; + pixman_image_fill_rectangles( + PIXMAN_OP_OVER, pix, &d->color, 1, + &(pixman_rectangle16_t){x, y, width, d->size}); +} + +static struct deco * +overline_new(int size, pixman_color_t color) +{ + struct private *priv = calloc(1, sizeof(*priv)); + priv->size = size; + priv->color = color; + + struct deco *deco = calloc(1, sizeof(*deco)); + deco->private = priv; + deco->expose = &expose; + deco->destroy = &destroy; + + return deco; +} + +static struct deco * +from_conf(const struct yml_node *node) +{ + const struct yml_node *size = yml_get_value(node, "size"); + const struct yml_node *color = yml_get_value(node, "color"); + return overline_new(yml_value_as_int(size), conf_to_color(color)); +} + +static bool +verify_conf(keychain_t *chain, const struct yml_node *node) +{ + static const struct attr_info attrs[] = { + {"size", true, &conf_verify_unsigned}, + {"color", true, &conf_verify_color}, + DECORATION_COMMON_ATTRS, + }; + + return conf_verify_dict(chain, node, attrs); +} + +const struct deco_iface deco_overline_iface = { + .verify_conf = &verify_conf, + .from_conf = &from_conf, +}; + +#if defined(CORE_PLUGINS_AS_SHARED_LIBRARIES) +extern const struct deco_iface iface __attribute__((weak, alias("deco_overline_iface"))); +#endif diff --git a/doc/yambar-decorations.5.scd b/doc/yambar-decorations.5.scd index 8c972f2..24282b5 100644 --- a/doc/yambar-decorations.5.scd +++ b/doc/yambar-decorations.5.scd @@ -71,6 +71,38 @@ content: ``` +# OVERLINE + +Similar to _underline_, this decoration renders a line of configurable +size and color at the top of the particle. + +## CONFIGURATION + +[[ *Name* +:[ *Type* +:[ *Req* +:[ *Description* +| size +: int +: yes +: The size (height/thickness) of the line, in pixels +| color +: color +: yes +: The color of the line. See *yambar*(5) for format. + +## EXAMPLES + +``` +content: + string: + deco: + overline: + size: 2 + color: ff0000ff +``` + + # BORDER This decoration renders a border of configurable size (i.e border diff --git a/plugin.c b/plugin.c index 1faae0e..43e8672 100644 --- a/plugin.c +++ b/plugin.c @@ -63,6 +63,7 @@ EXTERN_DECORATION(background); EXTERN_DECORATION(border); EXTERN_DECORATION(stack); EXTERN_DECORATION(underline); +EXTERN_DECORATION(overline); #undef EXTERN_DECORATION #undef EXTERN_PARTICLE @@ -152,6 +153,7 @@ init(void) REGISTER_CORE_DECORATION(border, border); REGISTER_CORE_DECORATION(stack, stack); REGISTER_CORE_DECORATION(underline, underline); + REGISTER_CORE_DECORATION(overline, overline); #undef REGISTER_CORE_DECORATION #undef REGISTER_CORE_PARTICLE From a2cf05a64d00b14c0d5fbe001d6cb82aca59df81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 11 Feb 2022 21:44:43 +0100 Subject: [PATCH 336/611] =?UTF-8?q?module/i3:=20add=20=E2=80=98strip-works?= =?UTF-8?q?pace-numbers=E2=80=99=20option?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a boolean option. When set, “N:” prefixes will be stripped from the workspaces’ name tag, *after* having been sorted (if the ‘sort’ option is being used). This makes it useful to arrange the workspaces in a fixed order, by prefixing the names with a number in the Sway config: set $ws1 “1:xyz” set $ws2 “2:abc” Then, in the yambar config: i3: sort: ascending strip-workspace-numbers: true --- CHANGELOG.md | 1 + doc/yambar-modules-i3.5.scd | 4 ++++ modules/i3.c | 34 +++++++++++++++++++++++++++++----- 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f8113d..18ea8b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ (https://codeberg.org/dnkl/yambar/issues/153). * overline: new decoration (https://codeberg.org/dnkl/yambar/issues/153). +* i3/sway: boolean option `strip-workspace-numbers`. ### Changed diff --git a/doc/yambar-modules-i3.5.scd b/doc/yambar-modules-i3.5.scd index 2cae2f8..b2ba394 100644 --- a/doc/yambar-modules-i3.5.scd +++ b/doc/yambar-modules-i3.5.scd @@ -68,6 +68,10 @@ with the _application_ and _title_ tags to replace the X11-only : enum : no : How to sort the list of workspaces; one of _none_, _ascending_ or _descending_, defaults to _none_. +| strip-workspace-numbers +: bool +: no +: If true, *N:* prefixes will be stripped from workspace names. Useful together with *sort*, to have the workspace order fixed. | persistent : list of strings : no diff --git a/modules/i3.c b/modules/i3.c index 56dec4d..fcd9395 100644 --- a/modules/i3.c +++ b/modules/i3.c @@ -60,6 +60,7 @@ struct private { size_t count; } ws_content; + bool strip_workspace_numbers; enum sort_mode sort_mode; tll(struct workspace) workspaces; @@ -107,10 +108,11 @@ workspace_from_json(const struct json_object *json, struct workspace *ws) const size_t node_count = json_object_array_length(focus); const bool is_empty = node_count == 0; + int name_as_int = workspace_name_as_int(name_as_string); *ws = (struct workspace) { .name = strdup(name_as_string), - .name_as_int = workspace_name_as_int(name_as_string), + .name_as_int = name_as_int, .persistent = false, .output = strdup(json_object_get_string(output)), .visible = json_object_get_boolean(visible), @@ -615,9 +617,16 @@ run(struct module *mod) for (size_t i = 0; i < m->persistent_count; i++) { const char *name_as_string = m->persistent_workspaces[i]; + int name_as_int = workspace_name_as_int(name_as_string); + if (m->strip_workspace_numbers) { + const char *colon = strchr(name_as_string, ':'); + if (colon != NULL) + name_as_string = colon++; + } + struct workspace ws = { .name = strdup(name_as_string), - .name_as_int = workspace_name_as_int(name_as_string), + .name_as_int = name_as_int, .persistent = true, .empty = true, }; @@ -725,9 +734,17 @@ content(struct module *mod) ws->window.title, m->mode); + const char *name = ws->name; + + if (m->strip_workspace_numbers) { + const char *colon = strchr(name, ':'); + if (colon != NULL) + name = colon + 1; + } + struct tag_set tags = { .tags = (struct tag *[]){ - tag_new_string(mod, "name", ws->name), + tag_new_string(mod, "name", name), tag_new_bool(mod, "visible", ws->visible), tag_new_bool(mod, "focused", ws->focused), tag_new_bool(mod, "urgent", ws->urgent), @@ -778,7 +795,8 @@ static struct module * i3_new(struct i3_workspaces workspaces[], size_t workspace_count, int left_spacing, int right_spacing, enum sort_mode sort_mode, size_t persistent_count, - const char *persistent_workspaces[static persistent_count]) + const char *persistent_workspaces[static persistent_count], + bool strip_workspace_numbers) { struct private *m = calloc(1, sizeof(*m)); @@ -794,6 +812,7 @@ i3_new(struct i3_workspaces workspaces[], size_t workspace_count, m->ws_content.v[i].content = workspaces[i].content; } + m->strip_workspace_numbers = strip_workspace_numbers; m->sort_mode = sort_mode; m->persistent_count = persistent_count; @@ -821,6 +840,8 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) const struct yml_node *right_spacing = yml_get_value(node, "right-spacing"); const struct yml_node *sort = yml_get_value(node, "sort"); const struct yml_node *persistent = yml_get_value(node, "persistent"); + const struct yml_node *strip_workspace_number = yml_get_value( + node, "strip-workspace-numbers"); int left = spacing != NULL ? yml_value_as_int(spacing) : left_spacing != NULL ? yml_value_as_int(left_spacing) : 0; @@ -859,7 +880,9 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) } return i3_new(workspaces, yml_dict_length(c), left, right, sort_mode, - persistent_count, persistent_workspaces); + persistent_count, persistent_workspaces, + (strip_workspace_number != NULL + ? yml_value_as_bool(strip_workspace_number) : false)); } static bool @@ -914,6 +937,7 @@ verify_conf(keychain_t *chain, const struct yml_node *node) {"right-spacing", false, &conf_verify_unsigned}, {"sort", false, &verify_sort}, {"persistent", false, &verify_persistent}, + {"strip-workspace-numbers", false, &conf_verify_bool}, {"content", true, &verify_content}, {"anchors", false, NULL}, {NULL, false, NULL}, From ca407cd1669e6c871a25c86db92ea8e121fda46d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 14 Feb 2022 18:33:14 +0100 Subject: [PATCH 337/611] module/i3: treat workspaces on the form N:name as numerical --- modules/i3.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/modules/i3.c b/modules/i3.c index fcd9395..bb51794 100644 --- a/modules/i3.c +++ b/modules/i3.c @@ -72,6 +72,22 @@ static int workspace_name_as_int(const char *name) { int name_as_int = 0; + + /* First check for N:name pattern (set $ws1 “1:foobar”) */ + const char *colon = strchr(name, ':'); + if (colon != NULL) { + for (const char *p = name; p < colon; p++) { + if (!(*p >= '0' && *p < '9')) + return -1; + + name_as_int *= 10; + name_as_int += *p - '0'; + } + + return name_as_int; + } + + /* Then, if the name is a number *only* (set $ws1 1) */ for (const char *p = name; *p != '\0'; p++) { if (!(*p >= '0' && *p <= '9')) return -1; From c44970717b52c1678b6a8dabdeda2ca75a42cdd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 15 Feb 2022 21:14:08 +0100 Subject: [PATCH 338/611] module/i3: workspace::focus is apparently Sway only MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On i3, users are currently greeted with: err: modules/i3.c:94: workspace reply/event without 'name' and/or 'output', and/or 'focus' properties This patch makes ‘focus’ an optional attribute. When missing, we assume a node-count of 0, which means the workspace’s ‘empty’ tag will always be true. Document this in the i3 man page. --- CHANGELOG.md | 6 ++++++ doc/yambar-modules-i3.5.scd | 2 +- modules/i3.c | 17 +++++++++++------ 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 18ea8b4..63f48f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,12 @@ ### Deprecated ### Removed ### Fixed + +* i3: fixed “missing workspace indicator” (_err: modules/i3.c:94: + workspace reply/event without 'name' and/or 'output', and/or 'focus' + properties_). + + ### Security ### Contributors diff --git a/doc/yambar-modules-i3.5.scd b/doc/yambar-modules-i3.5.scd index b2ba394..9184e46 100644 --- a/doc/yambar-modules-i3.5.scd +++ b/doc/yambar-modules-i3.5.scd @@ -37,7 +37,7 @@ with the _application_ and _title_ tags to replace the X11-only : True if the workspace has the urgent flag set | empty : bool -: True if the workspace is empty +: True if the workspace is empty (Sway only) | state : string : One of *urgent*, *focused*, *unfocused* or *invisible* (note: diff --git a/modules/i3.c b/modules/i3.c index bb51794..a7f3cc0 100644 --- a/modules/i3.c +++ b/modules/i3.c @@ -103,16 +103,18 @@ static bool workspace_from_json(const struct json_object *json, struct workspace *ws) { /* Always present */ - struct json_object *name, *output, *focus; + struct json_object *name, *output; if (!json_object_object_get_ex(json, "name", &name) || - !json_object_object_get_ex(json, "output", &output) || - !json_object_object_get_ex(json, "focus", &focus)) + !json_object_object_get_ex(json, "output", &output)) { - LOG_ERR("workspace reply/event without 'name' and/or 'output', " - "and/or 'focus' properties"); + LOG_ERR("workspace reply/event without 'name' and/or 'output' " + "properties"); return false; } + /* Sway only */ + struct json_object *focus = NULL; + json_object_object_get_ex(json, "focus", &focus); /* Optional */ struct json_object *visible = NULL, *focused = NULL, *urgent = NULL; @@ -122,7 +124,10 @@ workspace_from_json(const struct json_object *json, struct workspace *ws) const char *name_as_string = json_object_get_string(name); - const size_t node_count = json_object_array_length(focus); + const size_t node_count = focus != NULL + ? json_object_array_length(focus) + : 0; + const bool is_empty = node_count == 0; int name_as_int = workspace_name_as_int(name_as_string); From 265188ca4cb4ec261ca145fa8c874bd3f65a1e73 Mon Sep 17 00:00:00 2001 From: Leonardo Neumann Date: Mon, 21 Feb 2022 02:18:54 -0300 Subject: [PATCH 339/611] char32: add missing header to work with musl --- char32.c | 1 + 1 file changed, 1 insertion(+) diff --git a/char32.c b/char32.c index 4673779..71d2ae5 100644 --- a/char32.c +++ b/char32.c @@ -5,6 +5,7 @@ #include #include +#include #define LOG_MODULE "char32" #define LOG_ENABLE_DBG 0 From ffccabbb13cdd8e681b739e0e93a2466e858f2f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 23 Feb 2022 18:43:13 +0100 Subject: [PATCH 340/611] =?UTF-8?q?config:=20add=20inheritable=20option=20?= =?UTF-8?q?=E2=80=9Cfont-shaping=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds an inheritable option, “font-shaping”, that controls whether a particle that renders text should enable font-shaping or not. The option works similar to the ‘font’ option: one can set it at the top-level, and it gets inherited down through all modules and to their particles. Or, you can set it on a module and it gets inherited to all its particles, but not to other modules’ particles. Finally, you can set it on individual particles, in which case it only applies to them (or “child” particles). When font-shaping is enabled (the default), the string particle shapes full text runs using the fcft_rasterize_text_run_utf32() API. In fcft, this results in HarfBuzz being used to shape the string. When disabled, the string particle instead uses the simpler fcft_rasterize_char_utf32() API, which rasterizes individual characters. This gives user greater control over the font rendering. One example is bitmap fonts, which HarfBuzz often doesn’t get right. Closes #159 --- CHANGELOG.md | 3 ++ bar/bar.h | 2 ++ config-verify.c | 8 ++++++ config-verify.h | 1 + config.c | 56 ++++++++++++++++++++++++++++++++++++-- config.h | 3 ++ doc/yambar-particles.5.scd | 6 ++++ doc/yambar.5.scd | 6 ++++ font-shaping.h | 7 +++++ meson.build | 1 + particle.c | 5 ++-- particle.h | 6 +++- particles/list.c | 2 +- particles/map.c | 1 + particles/progress-bar.c | 1 + particles/ramp.c | 2 +- particles/string.c | 4 ++- 17 files changed, 106 insertions(+), 8 deletions(-) create mode 100644 font-shaping.h diff --git a/CHANGELOG.md b/CHANGELOG.md index 63f48f6..db94656 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,9 @@ * overline: new decoration (https://codeberg.org/dnkl/yambar/issues/153). * i3/sway: boolean option `strip-workspace-numbers`. +* font-shaping: new inheritable configuration option, allowing you to + configure whether strings should be _shaped_ using HarfBuzz, or not + (https://codeberg.org/dnkl/yambar/issues/159). ### Changed diff --git a/bar/bar.h b/bar/bar.h index c9f94f4..717b690 100644 --- a/bar/bar.h +++ b/bar/bar.h @@ -1,6 +1,7 @@ #pragma once #include "../color.h" +#include "../font-shaping.h" #include "../module.h" struct bar { @@ -26,6 +27,7 @@ struct bar_config { const char *monitor; enum bar_layer layer; enum bar_location location; + enum font_shaping font_shaping; int height; int left_spacing, right_spacing; int left_margin, right_margin; diff --git a/config-verify.c b/config-verify.c index b92fb71..68a50c8 100644 --- a/config-verify.c +++ b/config-verify.c @@ -228,6 +228,13 @@ conf_verify_font(keychain_t *chain, const struct yml_node *node) return true; } +bool +conf_verify_font_shaping(keychain_t *chain, const struct yml_node *node) +{ + return conf_verify_enum( + chain, node, (const char *[]){"full", /*"graphemes",*/ "none"}, 2); +} + bool conf_verify_decoration(keychain_t *chain, const struct yml_node *node) { @@ -450,6 +457,7 @@ conf_verify_bar(const struct yml_node *bar) {"border", false, &verify_bar_border}, {"font", false, &conf_verify_font}, + {"font-shaping", false, &conf_verify_font_shaping}, {"foreground", false, &conf_verify_color}, {"left", false, &verify_module_list}, diff --git a/config-verify.h b/config-verify.h index 8e5ab29..0a4ae34 100644 --- a/config-verify.h +++ b/config-verify.h @@ -45,6 +45,7 @@ bool conf_verify_dict(keychain_t *chain, const struct yml_node *node, bool conf_verify_on_click(keychain_t *chain, const struct yml_node *node); bool conf_verify_color(keychain_t *chain, const struct yml_node *node); bool conf_verify_font(keychain_t *chain, const struct yml_node *node); +bool conf_verify_font_shaping(keychain_t *chain, const struct yml_node *node); bool conf_verify_particle(keychain_t *chain, const struct yml_node *node); bool conf_verify_particle_list_items(keychain_t *chain, const struct yml_node *node); diff --git a/config.c b/config.c index 580781b..d2c11a6 100644 --- a/config.c +++ b/config.c @@ -101,6 +101,44 @@ conf_to_font(const struct yml_node *node) return ret; } +enum font_shaping +conf_to_font_shaping(const struct yml_node *node) +{ + const char *v = yml_value_as_string(node); + + if (strcmp(v, "none") == 0) + return FONT_SHAPE_NONE; + + else if (strcmp(v, "graphemes") == 0) { + static bool have_warned = false; + + if (!have_warned && + !(fcft_capabilities() & FCFT_CAPABILITY_GRAPHEME_SHAPING)) + { + have_warned = true; + LOG_WARN("cannot enable grapheme shaping; no support in fcft"); + } + return FONT_SHAPE_GRAPHEMES; + } + + else if (strcmp(v, "full") == 0) { + static bool have_warned = false; + + if (!have_warned && + !(fcft_capabilities() & FCFT_CAPABILITY_TEXT_RUN_SHAPING)) + { + have_warned = true; + LOG_WARN("cannot enable full text shaping; no support in fcft"); + } + return FONT_SHAPE_FULL; + } + + else { + assert(false); + return FONT_SHAPE_NONE; + } +} + struct deco * conf_to_deco(const struct yml_node *node) { @@ -144,7 +182,8 @@ particle_simple_list_from_config(const struct yml_node *node, } struct particle *common = particle_common_new( - 0, 0, NULL, fcft_clone(inherited.font), inherited.foreground, NULL); + 0, 0, NULL, fcft_clone(inherited.font), inherited.font_shaping, + inherited.foreground, NULL); return particle_list_new(common, parts, count, 0, 2); } @@ -163,6 +202,7 @@ conf_to_particle(const struct yml_node *node, struct conf_inherit inherited) const struct yml_node *right_margin = yml_get_value(pair.value, "right-margin"); const struct yml_node *on_click = yml_get_value(pair.value, "on-click"); const struct yml_node *font_node = yml_get_value(pair.value, "font"); + const struct yml_node *font_shaping_node = yml_get_value(pair.value, "font-shaping"); const struct yml_node *foreground_node = yml_get_value(pair.value, "foreground"); const struct yml_node *deco_node = yml_get_value(pair.value, "deco"); @@ -215,12 +255,14 @@ conf_to_particle(const struct yml_node *node, struct conf_inherit inherited) */ struct fcft_font *font = font_node != NULL ? conf_to_font(font_node) : fcft_clone(inherited.font); + enum font_shaping font_shaping = font_shaping_node != NULL + ? conf_to_font_shaping(font_shaping_node) : inherited.font_shaping; pixman_color_t foreground = foreground_node != NULL ? conf_to_color(foreground_node) : inherited.foreground; /* Instantiate base/common particle */ struct particle *common = particle_common_new( - left, right, on_click_templates, font, foreground, deco); + left, right, on_click_templates, font, font_shaping, foreground, deco); const struct particle_iface *iface = plugin_load_particle(type); @@ -237,6 +279,7 @@ conf_to_bar(const struct yml_node *bar, enum bar_backend backend) struct bar_config conf = { .backend = backend, .layer = BAR_LAYER_BOTTOM, + .font_shaping = FONT_SHAPE_FULL, }; /* @@ -359,6 +402,7 @@ conf_to_bar(const struct yml_node *bar, enum bar_backend backend) * foreground color at top-level. */ struct fcft_font *font = fcft_from_name(1, &(const char *){"sans"}, NULL); + enum font_shaping font_shaping = FONT_SHAPE_FULL; pixman_color_t foreground = {0xffff, 0xffff, 0xffff, 0xffff}; /* White */ const struct yml_node *font_node = yml_get_value(bar, "font"); @@ -367,12 +411,17 @@ conf_to_bar(const struct yml_node *bar, enum bar_backend backend) font = conf_to_font(font_node); } + const struct yml_node *font_shaping_node = yml_get_value(bar, "font-shaping"); + if (font_shaping_node != NULL) + font_shaping = conf_to_font_shaping(font_shaping_node); + const struct yml_node *foreground_node = yml_get_value(bar, "foreground"); if (foreground_node != NULL) foreground = conf_to_color(foreground_node); struct conf_inherit inherited = { .font = font, + .font_shaping = font_shaping, .foreground = foreground, }; @@ -402,12 +451,15 @@ conf_to_bar(const struct yml_node *bar, enum bar_backend backend) * applied to all its particles. */ const struct yml_node *mod_font = yml_get_value(m.value, "font"); + const struct yml_node *mod_font_shaping = yml_get_value(m.value, "font-shaping"); const struct yml_node *mod_foreground = yml_get_value( m.value, "foreground"); struct conf_inherit mod_inherit = { .font = mod_font != NULL ? conf_to_font(mod_font) : inherited.font, + .font_shaping = mod_font_shaping != NULL + ? conf_to_font_shaping(mod_font_shaping) : inherited.font_shaping, .foreground = mod_foreground != NULL ? conf_to_color(mod_foreground) : inherited.foreground, }; diff --git a/config.h b/config.h index 56f5b2e..ceb4b85 100644 --- a/config.h +++ b/config.h @@ -3,6 +3,7 @@ #include #include "yml.h" #include "bar/bar.h" +#include "font-shaping.h" struct bar; struct particle; @@ -16,9 +17,11 @@ struct bar *conf_to_bar(const struct yml_node *bar, enum bar_backend backend); pixman_color_t conf_to_color(const struct yml_node *node); struct fcft_font *conf_to_font(const struct yml_node *node); +enum font_shaping conf_to_font_shaping(const struct yml_node *node); struct conf_inherit { const struct fcft_font *font; + enum font_shaping font_shaping; pixman_color_t foreground; }; diff --git a/doc/yambar-particles.5.scd b/doc/yambar-particles.5.scd index a633269..312fb99 100644 --- a/doc/yambar-particles.5.scd +++ b/doc/yambar-particles.5.scd @@ -31,6 +31,12 @@ following attributes are supported by all particles: : Font to use. Note that this is an inherited attribute; i.e. you can set it on e.g. a _list_ particle, and it will apply to all particles in the list. +| font-shaping +: enum +: no +: font-shaping; one of _full_ or _none_. When set to _full_ (the + default), strings will be "shaped" using HarfBuzz. Requires support + in fcft. | foreground : color : no diff --git a/doc/yambar.5.scd b/doc/yambar.5.scd index 4e70ef2..35c87b8 100644 --- a/doc/yambar.5.scd +++ b/doc/yambar.5.scd @@ -130,6 +130,12 @@ types that are frequently used: the primary font, and the rest fallback fonts. These are yambar custom fallback fonts that will be searched before the fontconfig provided fallback list. +| font-shaping +: enum +: no +: Default setting for font-shaping, for use in particles. One of + _full_ or _none_. When set to _full_ (the default), strings will be + "shaped" using HarfBuzz. Requires support in fcft. | foreground : color : no diff --git a/font-shaping.h b/font-shaping.h new file mode 100644 index 0000000..3ae3817 --- /dev/null +++ b/font-shaping.h @@ -0,0 +1,7 @@ +#pragma once + +enum font_shaping { + FONT_SHAPE_NONE, + FONT_SHAPE_GRAPHEMES, + FONT_SHAPE_FULL, +}; diff --git a/meson.build b/meson.build index 4a479ec..b43d3fb 100644 --- a/meson.build +++ b/meson.build @@ -116,6 +116,7 @@ yambar = executable( 'config-verify.c', 'config-verify.h', 'config.c', 'config.h', 'decoration.h', + 'font-shaping.h', 'log.c', 'log.h', 'main.c', 'module.c', 'module.h', diff --git a/particle.c b/particle.c index 1b06f9a..7fd747c 100644 --- a/particle.c +++ b/particle.c @@ -31,14 +31,15 @@ particle_default_destroy(struct particle *particle) struct particle * particle_common_new(int left_margin, int right_margin, const char **on_click_templates, - struct fcft_font *font, pixman_color_t foreground, - struct deco *deco) + struct fcft_font *font, enum font_shaping font_shaping, + pixman_color_t foreground, struct deco *deco) { struct particle *p = calloc(1, sizeof(*p)); p->left_margin = left_margin; p->right_margin = right_margin; p->foreground = foreground; p->font = font; + p->font_shaping = font_shaping; p->deco = deco; if (on_click_templates != NULL) { diff --git a/particle.h b/particle.h index 7c5685e..bdf01f2 100644 --- a/particle.h +++ b/particle.h @@ -5,6 +5,7 @@ #include "color.h" #include "decoration.h" +#include "font-shaping.h" #include "tag.h" enum mouse_event { @@ -35,6 +36,7 @@ struct particle { pixman_color_t foreground; struct fcft_font *font; + enum font_shaping font_shaping; struct deco *deco; void (*destroy)(struct particle *particle); @@ -61,7 +63,8 @@ struct exposable { struct particle *particle_common_new( int left_margin, int right_margin, const char *on_click_templates[], - struct fcft_font *font, pixman_color_t foreground, struct deco *deco); + struct fcft_font *font, enum font_shaping font_shaping, + pixman_color_t foreground, struct deco *deco); void particle_default_destroy(struct particle *particle); @@ -82,6 +85,7 @@ void exposable_default_on_mouse( {"right-margin", false, &conf_verify_unsigned}, \ {"on-click", false, &conf_verify_on_click}, \ {"font", false, &conf_verify_font}, \ + {"font-shaping", false, &conf_verify_font_shaping}, \ {"foreground", false, &conf_verify_color}, \ {"deco", false, &conf_verify_decoration}, \ {NULL, false, NULL} diff --git a/particles/list.c b/particles/list.c index e5cf883..a2c37c6 100644 --- a/particles/list.c +++ b/particles/list.c @@ -197,7 +197,7 @@ from_conf(const struct yml_node *node, struct particle *common) yml_list_next(&it), idx++) { parts[idx] = conf_to_particle( - it.node, (struct conf_inherit){common->font, common->foreground}); + it.node, (struct conf_inherit){common->font, common->font_shaping, common->foreground}); } return particle_list_new(common, parts, count, left_spacing, right_spacing); diff --git a/particles/map.c b/particles/map.c index 980bdd1..abf76a6 100644 --- a/particles/map.c +++ b/particles/map.c @@ -215,6 +215,7 @@ from_conf(const struct yml_node *node, struct particle *common) struct conf_inherit inherited = { .font = common->font, + .font_shaping = common->font_shaping, .foreground = common->foreground }; diff --git a/particles/progress-bar.c b/particles/progress-bar.c index 462d536..f9e3999 100644 --- a/particles/progress-bar.c +++ b/particles/progress-bar.c @@ -304,6 +304,7 @@ from_conf(const struct yml_node *node, struct particle *common) struct conf_inherit inherited = { .font = common->font, + .font_shaping = common->font_shaping, .foreground = common->foreground, }; diff --git a/particles/ramp.c b/particles/ramp.c index 7815d99..0127519 100644 --- a/particles/ramp.c +++ b/particles/ramp.c @@ -209,7 +209,7 @@ from_conf(const struct yml_node *node, struct particle *common) yml_list_next(&it), idx++) { parts[idx] = conf_to_particle( - it.node, (struct conf_inherit){common->font, common->foreground}); + it.node, (struct conf_inherit){common->font, common->font_shaping, common->foreground}); } long min_v = min != NULL ? yml_value_as_int(min) : 0; diff --git a/particles/string.c b/particles/string.c index c5ade9f..f1e1faf 100644 --- a/particles/string.c +++ b/particles/string.c @@ -202,7 +202,9 @@ instantiate(const struct particle *particle, const struct tag_set *tags) e->kern_x = calloc(chars, sizeof(e->kern_x[0])); - if (fcft_capabilities() & FCFT_CAPABILITY_TEXT_RUN_SHAPING) { + if (particle->font_shaping == FONT_SHAPE_FULL && + fcft_capabilities() & FCFT_CAPABILITY_TEXT_RUN_SHAPING) + { struct fcft_text_run *run = fcft_rasterize_text_run_utf32( font, chars, wtext, FCFT_SUBPIXEL_NONE); From 4daa3d99047628608e6e0e094b5a78c05e90f120 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 27 Feb 2022 11:32:46 +0100 Subject: [PATCH 341/611] meson: stop using deprecated functions, require meson >= 0.58 * get_pkgconfig_variable() -> get_variable() * prog.path() -> prog.full_path() * meson.build_root() -> meson.global_build_root() --- CHANGELOG.md | 4 ++++ bar/meson.build | 4 ++-- doc/meson.build | 4 ++-- meson.build | 4 ++-- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index db94656..01d9ab8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,10 @@ ### Changed + +* Minimum required meson version is now 0.58. + + ### Deprecated ### Removed ### Fixed diff --git a/bar/meson.build b/bar/meson.build index 90f050b..6ca5ec9 100644 --- a/bar/meson.build +++ b/bar/meson.build @@ -7,11 +7,11 @@ endif if backend_wayland wayland_protocols = dependency('wayland-protocols') - wayland_protocols_datadir = wayland_protocols.get_pkgconfig_variable('pkgdatadir') + wayland_protocols_datadir = wayland_protocols.get_variable('pkgdatadir') wscanner = dependency('wayland-scanner', native: true) wscanner_prog = find_program( - wscanner.get_pkgconfig_variable('wayland_scanner'), native: true) + wscanner.get_variable('wayland_scanner'), native: true) wl_proto_headers = [] wl_proto_src = [] diff --git a/doc/meson.build b/doc/meson.build index 0d7f873..e882f52 100644 --- a/doc/meson.build +++ b/doc/meson.build @@ -1,7 +1,7 @@ sh = find_program('sh', native: true) scdoc = dependency('scdoc', native: true) -scdoc_prog = find_program(scdoc.get_pkgconfig_variable('scdoc'), native: true) +scdoc_prog = find_program(scdoc.get_variable('scdoc'), native: true) foreach man_src : ['yambar.1.scd', 'yambar.5.scd', 'yambar-decorations.5.scd', 'yambar-modules-alsa.5.scd', 'yambar-modules-backlight.5.scd', @@ -25,7 +25,7 @@ foreach man_src : ['yambar.1.scd', 'yambar.5.scd', 'yambar-decorations.5.scd', out, output: out, input: man_src, - command: [sh, '-c', '@0@ < @INPUT@'.format(scdoc_prog.path())], + command: [sh, '-c', '@0@ < @INPUT@'.format(scdoc_prog.full_path())], capture: true, install: true, install_dir: join_paths(get_option('mandir'), 'man@0@'.format(section))) diff --git a/meson.build b/meson.build index b43d3fb..ef28c58 100644 --- a/meson.build +++ b/meson.build @@ -1,7 +1,7 @@ project('yambar', 'c', version: '1.8.0', license: 'MIT', - meson_version: '>=0.53.0', + meson_version: '>=0.58.0', default_options: ['c_std=c18', 'warning_level=1', 'werror=true', @@ -18,7 +18,7 @@ endif # Compute the relative path used by compiler invocations. source_root = meson.current_source_dir().split('/') -build_root = meson.build_root().split('/') +build_root = meson.global_build_root().split('/') relative_dir_parts = [] i = 0 in_prefix = true From 3ff1c95208c2a933d193f38cbd75dbc896a6776f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 20 Mar 2022 10:50:57 +0100 Subject: [PATCH 342/611] char32: only include stdc-predef.h if it is available Use the (relatively new) macro __has_include() to check if stdc-predef.h exists, and only include it if it does. If stdc-predef.h does not exist, or if the compiler does not implement __has_include(), stdc-predef.h is *not* included. --- char32.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/char32.c b/char32.c index 71d2ae5..0ca029a 100644 --- a/char32.c +++ b/char32.c @@ -5,7 +5,12 @@ #include #include -#include + +#if defined __has_include + #if __has_include () + #include + #endif +#endif #define LOG_MODULE "char32" #define LOG_ENABLE_DBG 0 From 4bb81e89406077b8402630f240d244ce446dfa54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 29 Mar 2022 18:21:13 +0200 Subject: [PATCH 343/611] modules: add SOCK_CLOEXEC to all socket() calls --- modules/i3.c | 2 +- modules/mpd.c | 2 +- modules/network.c | 4 ++-- modules/sway-xkb.c | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/i3.c b/modules/i3.c index a7f3cc0..22b6d4b 100644 --- a/modules/i3.c +++ b/modules/i3.c @@ -621,7 +621,7 @@ run(struct module *mod) if (!i3_get_socket_address(&addr)) return 1; - int sock = socket(AF_UNIX, SOCK_STREAM, 0); + int sock = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); if (sock == -1) { LOG_ERRNO("failed to create UNIX socket"); return 1; diff --git a/modules/mpd.c b/modules/mpd.c index b2e9e6e..27992d3 100644 --- a/modules/mpd.c +++ b/modules/mpd.c @@ -223,7 +223,7 @@ wait_for_socket_create(const struct module *mod) struct stat st; if (stat(m->host, &st) == 0 && S_ISSOCK(st.st_mode)) { - int s = socket(AF_UNIX, SOCK_STREAM, 0); + int s = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); struct sockaddr_un addr = {.sun_family = AF_UNIX}; strncpy(addr.sun_path, m->host, sizeof(addr.sun_path) - 1); diff --git a/modules/network.c b/modules/network.c index 3bff716..34980fa 100644 --- a/modules/network.c +++ b/modules/network.c @@ -167,7 +167,7 @@ nl_pid_value(void) static int netlink_connect_rt(void) { - int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + int sock = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE); if (sock == -1) { LOG_ERRNO("failed to create netlink socket"); return -1; @@ -191,7 +191,7 @@ netlink_connect_rt(void) static int netlink_connect_genl(void) { - int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); + int sock = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_GENERIC); if (sock == -1) { LOG_ERRNO("failed to create netlink socket"); return -1; diff --git a/modules/sway-xkb.c b/modules/sway-xkb.c index 9841feb..6632ab7 100644 --- a/modules/sway-xkb.c +++ b/modules/sway-xkb.c @@ -267,7 +267,7 @@ run(struct module *mod) if (!i3_get_socket_address(&addr)) return 1; - int sock = socket(AF_UNIX, SOCK_STREAM, 0); + int sock = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); if (sock == -1) { LOG_ERRNO("failed to create UNIX socket"); return 1; From 2b6f5b1e36c8c38943736a1853ad469aac3e94d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 29 Mar 2022 18:21:44 +0200 Subject: [PATCH 344/611] module/removables: open /proc/self/mountinfo with CLOEXEC --- modules/removables.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/modules/removables.c b/modules/removables.c index 6727afa..d8fe7c9 100644 --- a/modules/removables.c +++ b/modules/removables.c @@ -164,11 +164,17 @@ content(struct module *mod) static void find_mount_points(const char *dev_path, mount_point_list_t *mount_points) { - FILE *f = fopen("/proc/self/mountinfo", "r"); - assert(f != NULL); + int fd = open("/proc/self/mountinfo", O_RDONLY | O_CLOEXEC); + FILE *f = fd >= 0 ? fdopen(fd, "r") : NULL; + + if (fd < 0 || f == NULL) { + LOG_ERRNO("failed to open /proc/self/mountinfo"); + if (fd >= 0) + close(fd); + return; + } char line[4096]; - while (fgets(line, sizeof(line), f) != NULL) { char *dev = NULL, *path = NULL; @@ -641,7 +647,7 @@ run(struct module *mod) /* To be able to poll() mountinfo for changes, to detect * mount/unmount operations */ - int mount_info_fd = open("/proc/self/mountinfo", O_RDONLY); + int mount_info_fd = open("/proc/self/mountinfo", O_RDONLY | O_CLOEXEC); int ret = 1; From 068c25d8f6db0cbd0aa4668f45b2135a4b83f8b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 29 Mar 2022 18:22:08 +0200 Subject: [PATCH 345/611] module/script: open comm-pipe + /dev/null with CLOEXEC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This ensures we don’t leak FDs when exec:ing e.g. on-click handlers. Note that the comm-pipe FD is *supposed* to stay open when we execing the script. This is handled by the call to dup2(), which drops the CLOEXEC flag. Since dup2() is called after the fork, the dup:ed FD is never visible in the “parent” yambar process. --- modules/script.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/script.c b/modules/script.c index 254aac4..5b3b51a 100644 --- a/modules/script.c +++ b/modules/script.c @@ -396,7 +396,7 @@ execute_script(struct module *mod) /* Stdout redirection pipe */ int comm_pipe[2]; - if (pipe(comm_pipe) < 0) { + if (pipe2(comm_pipe, O_CLOEXEC) < 0) { LOG_ERRNO("failed to create stdin/stdout redirection pipe"); close(exec_pipe[0]); close(exec_pipe[1]); @@ -444,7 +444,7 @@ execute_script(struct module *mod) close(comm_pipe[0]); /* Re-direct stdin/stdout */ - int dev_null = open("/dev/null", O_RDONLY); + int dev_null = open("/dev/null", O_RDONLY | O_CLOEXEC); if (dev_null < 0) goto fail; From fd014dc33ba9285db30a5609352bc768978c9d03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 29 Mar 2022 18:23:55 +0200 Subject: [PATCH 346/611] =?UTF-8?q?Don=E2=80=99t=20loop=2065536=20FDs,=20t?= =?UTF-8?q?rying=20to=20close=20them,=20when=20fork+exec:ing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All FDs should now have the CLOEXEC flag set, and thus there’s no longer needed to manually loop “all” possible FDs and (trying to) close them. Note: the alsa module (alsalib, actually) is “racy” - while booting up, it temporarily opens the asoundrc file without CLOEXEC. If e.g. the script module starts its script inside this window, it’ll have a leaked FD. Not much we can do about it though :/ Closes #169 --- CHANGELOG.md | 3 +++ modules/script.c | 10 ---------- particle.c | 5 ----- 3 files changed, 3 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 01d9ab8..d6e8b90 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,9 @@ * i3: fixed “missing workspace indicator” (_err: modules/i3.c:94: workspace reply/event without 'name' and/or 'output', and/or 'focus' properties_). +* Slow/laggy behavior when quickly spawning many `on-click` handlers, + e.g. when handling mouse wheel events + (https://codeberg.org/dnkl/yambar/issues/169). ### Security diff --git a/modules/script.c b/modules/script.c index 5b3b51a..f701e99 100644 --- a/modules/script.c +++ b/modules/script.c @@ -458,16 +458,6 @@ execute_script(struct module *mod) close(comm_pipe[1]); comm_pipe[1] = -1; - /* Close *all* other FDs */ - for (int i = STDERR_FILENO + 1; i < 65536; i++) { - if (i == exec_pipe[1]) { - /* Needed for error reporting. Automatically closed - * when execvp() succeeds */ - continue; - } - close(i); - } - execvp(m->path, argv); fail: diff --git a/particle.c b/particle.c index 7fd747c..5f6b04d 100644 --- a/particle.c +++ b/particle.c @@ -277,11 +277,6 @@ exposable_default_on_mouse(struct exposable *exposable, struct bar *bar, goto fail; } - /* Close *all* other FDs (e.g. script modules' FDs) */ - for (int i = STDERR_FILENO + 1; i < 65536; i++) - if (i != pipe_fds[1]) - close(i); - execvp(argv[0], argv); fail: From fce2787bdf319b5464d2c37e05044abecb0fa78f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 7 Apr 2022 13:21:41 +0200 Subject: [PATCH 347/611] module/cpu: use get_nprocs() to retrieve the CPU count --- modules/cpu.c | 32 ++++---------------------------- 1 file changed, 4 insertions(+), 28 deletions(-) diff --git a/modules/cpu.c b/modules/cpu.c index 0967dfd..d85afc8 100644 --- a/modules/cpu.c +++ b/modules/cpu.c @@ -10,6 +10,8 @@ #include #include +#include + #define LOG_MODULE "cpu" #define LOG_ENABLE_DBG 0 #define SMALLEST_INTERVAL 500 @@ -55,34 +57,8 @@ description(struct module *mod) static uint32_t get_cpu_nb_cores() { - uint32_t nb_cores = 0; - FILE *fp = NULL; - char *line = NULL; - size_t len = 0; - ssize_t read; - - fp = fopen("/proc/cpuinfo", "r"); - if (NULL == fp) { - LOG_ERRNO("unable to open /proc/cpuinfo"); - return 0; - } - while ((read = getline(&line, &len, fp)) != -1) { - if (strncmp(line, "siblings", sizeof("siblings") - 1) == 0) { - char *pos = (char *)memchr(line, ':', read); - if (pos == NULL) { - LOG_ERR("unable to parse siblings field to find the number of cores"); - return 0; - } - errno = 0; - nb_cores = strtoul(pos + 1, NULL, 10); - if (errno == ERANGE) { - LOG_ERR("number of cores is out of range"); - } - break; - } - } - fclose(fp); - free(line); + int nb_cores = get_nprocs(); + LOG_DBG("CPU count: %d", nb_cores); return nb_cores; } From 62ca06eccbdb6627059af02edf8485243b71f2b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 7 Apr 2022 13:28:35 +0200 Subject: [PATCH 348/611] =?UTF-8?q?module/cpu:=20don=E2=80=99t=20use=20cor?= =?UTF-8?q?e=20ID=20from=20/proc/stat=20as=20array=20index?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit /proc/stat lists CPU usage, in the form: cpu ... cpu0 ... cpu1 ... ... cpuN ... where the first line is a summary line. We’ve been using the CPU numbers from /proc/stat to index into our internal stats array. This doesn’t work on systems where the core IDs aren’t consecutive. Examples of such systems are SMT systems with SMT disabled. Here, /proc/stat may look like this instead: cpu ... cpu0 ... cpu2 ... cpu4 ... ... With this patch, we ignore the CPU ID from /proc/stat. Instead, we use a simple counter that is incremented for each (valid) cpu line found in /proc/stat. To protect against corrupt /proc/stat content, stop parsing /proc/stat if the number of parsed CPU lines exceed what we consider to the be total number of CPUs in the system. Closes #172 --- modules/cpu.c | 44 ++++++++++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/modules/cpu.c b/modules/cpu.c index d85afc8..b4067cd 100644 --- a/modules/cpu.c +++ b/modules/cpu.c @@ -64,22 +64,27 @@ get_cpu_nb_cores() } static bool -parse_proc_stat_line(const char *line, int32_t *core, uint32_t *user, uint32_t *nice, uint32_t *system, uint32_t *idle, +parse_proc_stat_line(const char *line, uint32_t *user, uint32_t *nice, uint32_t *system, uint32_t *idle, uint32_t *iowait, uint32_t *irq, uint32_t *softirq, uint32_t *steal, uint32_t *guest, uint32_t *guestnice) { + int32_t core_id; if (line[sizeof("cpu") - 1] == ' ') { - int read = sscanf(line, - "cpu %" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32 - " %" SCNu32 " %" SCNu32 " %" SCNu32, - user, nice, system, idle, iowait, irq, softirq, steal, guest, guestnice); - *core = -1; + int read = sscanf( + line, + "cpu %" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32 + " %" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32, + user, nice, system, idle, iowait, irq, softirq, steal, guest, + guestnice); return read == 10; } else { - int read = sscanf(line, - "cpu%" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32 - " %" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32, - core, user, nice, system, idle, iowait, irq, softirq, steal, guest, guestnice); + int read = sscanf( + line, + "cpu%" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32 + " %" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32 + " %" SCNu32, + &core_id, user, nice, system, idle, iowait, irq, softirq, steal, + guest, guestnice); return read == 11; } } @@ -125,20 +130,23 @@ refresh_cpu_stats(struct cpu_stats *cpu_stats) return; } - while ((read = getline(&line, &len, fp)) != -1) { + while ((read = getline(&line, &len, fp)) != -1 && core <= nb_cores) { if (strncmp(line, "cpu", sizeof("cpu") - 1) == 0) { - if (!parse_proc_stat_line(line, &core, &user, &nice, &system, &idle, &iowait, &irq, &softirq, &steal, - &guest, &guestnice) - || core < -1 || core >= (int32_t)nb_cores) { + if (!parse_proc_stat_line( + line, &user, &nice, &system, &idle, &iowait, &irq, &softirq, &steal, + &guest, &guestnice)) + { LOG_ERR("unable to parse /proc/stat line"); goto exit; } - cpu_stats->prev_cores_idle[core + 1] = cpu_stats->cur_cores_idle[core + 1]; - cpu_stats->prev_cores_nidle[core + 1] = cpu_stats->cur_cores_nidle[core + 1]; + cpu_stats->prev_cores_idle[core] = cpu_stats->cur_cores_idle[core]; + cpu_stats->prev_cores_nidle[core] = cpu_stats->cur_cores_nidle[core]; - cpu_stats->cur_cores_idle[core + 1] = idle + iowait; - cpu_stats->cur_cores_nidle[core + 1] = user + nice + system + irq + softirq + steal; + cpu_stats->cur_cores_idle[core] = idle + iowait; + cpu_stats->cur_cores_nidle[core] = user + nice + system + irq + softirq + steal; + + core++; } } exit: From 9c28d368988300733073ef1a6891446ef8ad2c0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 7 Apr 2022 13:33:41 +0200 Subject: [PATCH 349/611] =?UTF-8?q?changelog:=20cpu:=20don=E2=80=99t=20err?= =?UTF-8?q?or=20out=20on=20systems=20with=20SMT=20disabled?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d6e8b90..7f1a713 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ ## Unreleased + ### Added * Support for custom font fallbacks @@ -37,6 +38,8 @@ * Slow/laggy behavior when quickly spawning many `on-click` handlers, e.g. when handling mouse wheel events (https://codeberg.org/dnkl/yambar/issues/169). +* cpu: don’t error out on systems where SMT has been disabled + (https://codeberg.org/dnkl/yambar/issues/172). ### Security From c1e549df54ccf46b16e92676a351728e44f533dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 16 Apr 2022 11:59:10 +0200 Subject: [PATCH 350/611] examples: config: remove duplicate volume icons --- examples/configurations/laptop.conf | 2 -- 1 file changed, 2 deletions(-) diff --git a/examples/configurations/laptop.conf b/examples/configurations/laptop.conf index 3a6c439..888b19e 100644 --- a/examples/configurations/laptop.conf +++ b/examples/configurations/laptop.conf @@ -239,8 +239,6 @@ bar: items: - string: {text: , font: *awesome} - string: {text: , font: *awesome} - - string: {text: , font: *awesome} - - string: {text: , font: *awesome} - string: {text: , font: *awesome} - backlight: name: intel_backlight From acb8a0937627c1692ccc73029948f14185ea3423 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 22 Apr 2022 20:23:08 +0200 Subject: [PATCH 351/611] changelog: turn all issue links into markdown hyperlinks --- CHANGELOG.md | 74 ++++++++++++++++++++++++++-------------------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f1a713..f317bef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,13 +14,13 @@ ### Added * Support for custom font fallbacks - (https://codeberg.org/dnkl/yambar/issues/153). + ([#153](https://codeberg.org/dnkl/yambar/issues/153)). * overline: new decoration - (https://codeberg.org/dnkl/yambar/issues/153). + ([#153](https://codeberg.org/dnkl/yambar/issues/153)). * i3/sway: boolean option `strip-workspace-numbers`. * font-shaping: new inheritable configuration option, allowing you to configure whether strings should be _shaped_ using HarfBuzz, or not - (https://codeberg.org/dnkl/yambar/issues/159). + ([#159](https://codeberg.org/dnkl/yambar/issues/159)). ### Changed @@ -37,9 +37,9 @@ properties_). * Slow/laggy behavior when quickly spawning many `on-click` handlers, e.g. when handling mouse wheel events - (https://codeberg.org/dnkl/yambar/issues/169). + ([#169](https://codeberg.org/dnkl/yambar/issues/169)). * cpu: don’t error out on systems where SMT has been disabled - (https://codeberg.org/dnkl/yambar/issues/172). + ([#172](https://codeberg.org/dnkl/yambar/issues/172)). ### Security @@ -51,14 +51,14 @@ ### Added * ramp: can now have custom min and max values - (https://codeberg.org/dnkl/yambar/issues/103). + ([#103](https://codeberg.org/dnkl/yambar/issues/103)). * border: new decoration. * i3/sway: new boolean tag: `empty` - (https://codeberg.org/dnkl/yambar/issues/139). + ([#139](https://codeberg.org/dnkl/yambar/issues/139)). * mem: a module handling system memory monitoring * cpu: a module offering cpu usage monitoring * removables: support for audio CDs - (https://codeberg.org/dnkl/yambar/issues/146). + ([#146](https://codeberg.org/dnkl/yambar/issues/146)). * removables: new boolean tag: `audio`. @@ -69,7 +69,7 @@ * battery: unknown battery states are now mapped to ‘unknown’, instead of ‘discharging’. * Wayland: the bar no longer exits when the monitor is - disabled/unplugged (https://codeberg.org/dnkl/yambar/issues/106). + disabled/unplugged ([#106](https://codeberg.org/dnkl/yambar/issues/106)). ### Fixed @@ -78,16 +78,16 @@ options. * Crash when `udev_monitor_receive_device()` returned `NULL`. This affected the “backlight”, “battery” and “removables” modules - (https://codeberg.org/dnkl/yambar/issues/109). + ([#109](https://codeberg.org/dnkl/yambar/issues/109)). * foreign-toplevel: update bar when a top-level is closed. * Bar not being mapped on an output before at least one module has - “refreshed” it (https://codeberg.org/dnkl/yambar/issues/116). + “refreshed” it ([#116](https://codeberg.org/dnkl/yambar/issues/116)). * network: failure to retrieve wireless attributes (SSID, RX/TX bitrate, signal strength etc). * Integer options that were supposed to be >= 0 were incorrectly allowed, leading to various bad things; including yambar crashing, or worse, the compositor crashing - (https://codeberg.org/dnkl/yambar/issues/129). + ([#129](https://codeberg.org/dnkl/yambar/issues/129)). * kib/kb, mib/mb and gib/gb formatters were inverted. @@ -112,17 +112,17 @@ ### Added * i3: `persistent` attribute, allowing persistent workspaces - (https://codeberg.org/dnkl/yambar/issues/72). + ([#72](https://codeberg.org/dnkl/yambar/issues/72)). * bar: `border.{left,right,top,bottom}-width`, allowing the width of each side of the border to be configured individually. `border.width` is now a short-hand for setting all four borders to the same value - (https://codeberg.org/dnkl/yambar/issues/77). + ([#77](https://codeberg.org/dnkl/yambar/issues/77)). * bar: `layer: top|bottom`, allowing the layer which the bar is rendered on to be changed. Wayland only - ignored on X11. * river: `all-monitors: false|true`. * `-d,--log-level=info|warning|error|none` command line option - (https://codeberg.org/dnkl/yambar/issues/84). + ([#84](https://codeberg.org/dnkl/yambar/issues/84)). * river: support for the river-status protocol, version 2 (‘urgent’ views). * `online` tag to the `alsa` module. @@ -143,7 +143,7 @@ * bar: do not add `spacing` around empty (zero-width) modules. * alsa: do not error out if we fail to connect to the ALSA device, or if we get disconnected. Instead, keep retrying until we succeed - (https://codeberg.org/dnkl/yambar/issues/86). + ([#86](https://codeberg.org/dnkl/yambar/issues/86)). ### Fixed @@ -153,7 +153,7 @@ * Regression: `{where}` tag not being expanded in progress-bar `on-click` handlers. * `alsa` module causing yambar to use 100% CPU if the ALSA device is - disconnected (https://codeberg.org/dnkl/yambar/issues/61). + disconnected ([#61](https://codeberg.org/dnkl/yambar/issues/61)). ### Contributors @@ -169,39 +169,39 @@ * Text shaping support. * Support for middle and right mouse buttons, mouse wheel and trackpad - scrolling (https://codeberg.org/dnkl/yambar/issues/39). + scrolling ([#39](https://codeberg.org/dnkl/yambar/issues/39)). * script: polling mode. See the new `poll-interval` option - (https://codeberg.org/dnkl/yambar/issues/67). + ([#67](https://codeberg.org/dnkl/yambar/issues/67)). ### Changed * doc: split up **yambar-modules**(5) into multiple man pages, one for - each module (https://codeberg.org/dnkl/yambar/issues/15). + each module ([#15](https://codeberg.org/dnkl/yambar/issues/15)). * fcft >= 2.4.0 is now required. * sway-xkb: non-keyboard inputs are now ignored - (https://codeberg.org/dnkl/yambar/issues/51). + ([#51](https://codeberg.org/dnkl/yambar/issues/51)). * battery: don’t terminate (causing last status to “freeze”) when failing to update; retry again later - (https://codeberg.org/dnkl/yambar/issues/44). + ([#44](https://codeberg.org/dnkl/yambar/issues/44)). * battery: differentiate "Not Charging" and "Discharging" in state tag of battery module. - (https://codeberg.org/dnkl/yambar/issues/57). + ([#57](https://codeberg.org/dnkl/yambar/issues/57)). * string: use HORIZONTAL ELLIPSIS instead of three regular periods when truncating a string - (https://codeberg.org/dnkl/yambar/issues/73). + ([#73](https://codeberg.org/dnkl/yambar/issues/73)). ### Fixed * Crash when merging non-dictionary anchors in the YAML configuration - (https://codeberg.org/dnkl/yambar/issues/32). + ([#32](https://codeberg.org/dnkl/yambar/issues/32)). * Crash in the `ramp` particle when the tag’s value was out-of-bounds - (https://codeberg.org/dnkl/yambar/issues/45). + ([#45](https://codeberg.org/dnkl/yambar/issues/45)). * Crash when a string particle contained `{}` - (https://codeberg.org/dnkl/yambar/issues/48). + ([#48](https://codeberg.org/dnkl/yambar/issues/48)). * `script` module rejecting range tag end values containing the digit - `9` (https://codeberg.org/dnkl/yambar/issues/60). + `9` ([#60](https://codeberg.org/dnkl/yambar/issues/60)). ### Contributors @@ -216,7 +216,7 @@ * i3: workspaces with numerical names are sorted separately from non-numerically named workspaces - (https://codeberg.org/dnkl/yambar/issues/30). + ([#30](https://codeberg.org/dnkl/yambar/issues/30)). ### Fixed @@ -224,7 +224,7 @@ * mpd: `elapsed` tag not working (regression, introduced in 1.6.0). * Wrong background color for (semi-) transparent backgrounds. * battery: stats sometimes getting stuck at 0, or impossibly large - values (https://codeberg.org/dnkl/yambar/issues/25). + values ([#25](https://codeberg.org/dnkl/yambar/issues/25)). ## 1.6.0 @@ -233,17 +233,17 @@ * alsa: `percent` tag. This is an integer tag that represents the current volume as a percentage value - (https://codeberg.org/dnkl/yambar/issues/10). + ([#10](https://codeberg.org/dnkl/yambar/issues/10)). * river: added documentation - (https://codeberg.org/dnkl/yambar/issues/9). + ([#9](https://codeberg.org/dnkl/yambar/issues/9)). * script: new module, adds support for custom user scripts - (https://codeberg.org/dnkl/yambar/issues/11). + ([#11](https://codeberg.org/dnkl/yambar/issues/11)). * mpd: `volume` tag. This is a range tag that represents MPD's current volume in percentage (0-100) * i3: `sort` configuration option, that controls how the workspace list is sorted. Can be set to one of `none`, `ascending` or `descending`. Default is `none` - (https://codeberg.org/dnkl/yambar/issues/17). + ([#17](https://codeberg.org/dnkl/yambar/issues/17)). * i3: `mode` tag: the name of the currently active mode @@ -253,12 +253,12 @@ error”_. * Memory leak when a YAML parsing error was encountered. * clock: update every second when necessary - (https://codeberg.org/dnkl/yambar/issues/12). + ([#12](https://codeberg.org/dnkl/yambar/issues/12)). * mpd: fix compilation with clang - (https://codeberg.org/dnkl/yambar/issues/16). + ([#16](https://codeberg.org/dnkl/yambar/issues/16)). * Crash when the alpha component in a color value was 0. * XCB: Fallback to non-primary monitor when the primary monitor is - disconnected (https://codeberg.org/dnkl/yambar/issues/20) + disconnected ([#20](https://codeberg.org/dnkl/yambar/issues/20)) ### Contributors From 1206153b03ae53c47136ff531aa77ec873ff50e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 22 Apr 2022 20:25:11 +0200 Subject: [PATCH 352/611] =?UTF-8?q?changelog:=20convert=20all=20issue=20li?= =?UTF-8?q?nks=20to=20reference=20links=20in=20=E2=80=98unreleased?= =?UTF-8?q?=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f317bef..758ddb1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,14 +13,12 @@ ### Added -* Support for custom font fallbacks - ([#153](https://codeberg.org/dnkl/yambar/issues/153)). -* overline: new decoration - ([#153](https://codeberg.org/dnkl/yambar/issues/153)). +* Support for custom font fallbacks ([#153][153]). +* overline: new decoration ([#153][153]). * i3/sway: boolean option `strip-workspace-numbers`. * font-shaping: new inheritable configuration option, allowing you to configure whether strings should be _shaped_ using HarfBuzz, or not - ([#159](https://codeberg.org/dnkl/yambar/issues/159)). + ([#159][159]). ### Changed @@ -36,15 +34,19 @@ workspace reply/event without 'name' and/or 'output', and/or 'focus' properties_). * Slow/laggy behavior when quickly spawning many `on-click` handlers, - e.g. when handling mouse wheel events - ([#169](https://codeberg.org/dnkl/yambar/issues/169)). + e.g. when handling mouse wheel events ([#169][169]). * cpu: don’t error out on systems where SMT has been disabled - ([#172](https://codeberg.org/dnkl/yambar/issues/172)). + ([#172][172]). ### Security ### Contributors +[153]: https://codeberg.org/dnkl/yambar/issues/153 +[159]: https://codeberg.org/dnkl/yambar/issues/159 +[169]: https://codeberg.org/dnkl/yambar/issues/169 +[172]: https://codeberg.org/dnkl/yambar/issues/172 + ## 1.8.0 From 4496d82cfbd62406933ad950152a64c443ec85a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 23 Apr 2022 11:14:50 +0200 Subject: [PATCH 353/611] changelog: hyperlink lists under their corresponding sub-section --- CHANGELOG.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 758ddb1..da20255 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,9 @@ configure whether strings should be _shaped_ using HarfBuzz, or not ([#159][159]). +[153]: https://codeberg.org/dnkl/yambar/issues/153 +[159]: https://codeberg.org/dnkl/yambar/issues/159 + ### Changed @@ -38,15 +41,13 @@ * cpu: don’t error out on systems where SMT has been disabled ([#172][172]). +[169]: https://codeberg.org/dnkl/yambar/issues/169 +[172]: https://codeberg.org/dnkl/yambar/issues/172 + ### Security ### Contributors -[153]: https://codeberg.org/dnkl/yambar/issues/153 -[159]: https://codeberg.org/dnkl/yambar/issues/159 -[169]: https://codeberg.org/dnkl/yambar/issues/169 -[172]: https://codeberg.org/dnkl/yambar/issues/172 - ## 1.8.0 From 2b103b7acdbfe3748303d7ab313f3d51a56845f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Gibrowski=20Fa=C3=A9?= Date: Sun, 10 Apr 2022 00:10:07 -0300 Subject: [PATCH 354/611] Implement conditions on tag A condition is formed by: is the normal yambar tag. is one of '==', '!=', '<', '<=', '>', or '>='. is what you wish to compare it to. 'boolean' tags must be used directly. They falsehood is matched with '~': ~ Finally, to match an empty string, one must use ' "" ': "" --- doc/yambar-modules-foreign-toplevel.5.scd | 7 +- doc/yambar-modules-i3.5.scd | 5 +- doc/yambar-modules-removables.5.scd | 7 +- doc/yambar-modules-river.5.scd | 7 +- doc/yambar-modules-script.5.scd | 7 +- doc/yambar-modules.5.scd | 17 +- doc/yambar-particles.5.scd | 51 +++- particles/map.c | 349 ++++++++++++++++++++-- tag.c | 28 ++ tag.h | 8 + test/full-conf-good.yml | 5 +- 11 files changed, 416 insertions(+), 75 deletions(-) diff --git a/doc/yambar-modules-foreign-toplevel.5.scd b/doc/yambar-modules-foreign-toplevel.5.scd index 6a65ec3..5ef8c57 100644 --- a/doc/yambar-modules-foreign-toplevel.5.scd +++ b/doc/yambar-modules-foreign-toplevel.5.scd @@ -67,10 +67,9 @@ bar: - foreign-toplevel: content: map: - tag: activated - values: - false: {empty: {}} - true: + conditions: + (activated == false): {empty: {}} + (activated == true): - string: {text: "{app-id}: {title}"} ``` diff --git a/doc/yambar-modules-i3.5.scd b/doc/yambar-modules-i3.5.scd index 9184e46..e646183 100644 --- a/doc/yambar-modules-i3.5.scd +++ b/doc/yambar-modules-i3.5.scd @@ -102,10 +102,9 @@ bar: content: "": map: - tag: state default: {string: {text: "{name}"}} - values: - focused: {string: {text: "{name}*"}} + conditions: + (state == focused): {string: {text: "{name}*"}} current: { string: {text: "{application}: {title}"}} ``` diff --git a/doc/yambar-modules-removables.5.scd b/doc/yambar-modules-removables.5.scd index cbffebe..5c34aef 100644 --- a/doc/yambar-modules-removables.5.scd +++ b/doc/yambar-modules-removables.5.scd @@ -74,13 +74,12 @@ bar: - removables: content: map: - tag: mounted - values: - false: + conditions: + (mounted == false): string: on-click: udisksctl mount -b {device} text: "{label}" - true: + (mounted == true): string: on-click: udisksctl unmount -b {device} text: "{label}" diff --git a/doc/yambar-modules-river.5.scd b/doc/yambar-modules-river.5.scd index 96be605..2255dc0 100644 --- a/doc/yambar-modules-river.5.scd +++ b/doc/yambar-modules-river.5.scd @@ -79,10 +79,9 @@ bar: title: {string: { text: "{seat} - {title}" }} content: map: - tag: occupied - values: - false: {empty: {}} - true: + conditions: + (occupied == false): {empty: {}} + (occupied == true): string: margin: 5 text: "{id}: {state}" diff --git a/doc/yambar-modules-script.5.scd b/doc/yambar-modules-script.5.scd index c1f31c6..357bae1 100644 --- a/doc/yambar-modules-script.5.scd +++ b/doc/yambar-modules-script.5.scd @@ -133,10 +133,9 @@ bar: title|string|{{title}} content: map: - tag: status - values: - Paused: {empty: {}} - Playing: + conditions: + (status == Paused): {empty: {}} + (status == Playing): content: {string: {text: "{artist} - {title}"}} ``` diff --git a/doc/yambar-modules.5.scd b/doc/yambar-modules.5.scd index 266d9b7..b7177c4 100644 --- a/doc/yambar-modules.5.scd +++ b/doc/yambar-modules.5.scd @@ -68,20 +68,17 @@ in red. ``` content: map: - tag: carrier - values: - false: {empty: {}} - true: + conditions: + (carrier == false): {empty: {}} + (carrier == true): map: - tag: state default: {string: {text: , font: *awesome, foreground: ffffff66}} - values: - up: + conditions: + (state == up): map: - tag: ipv4 default: {string: {text: , font: *awesome}} - values: - "": {string: {text: , font: *awesome, foreground: ffffff66}} + conditions: + (ipv4 == ""): {string: {text: , font: *awesome, foreground: ffffff66}} ``` ## Use yaml anchors diff --git a/doc/yambar-particles.5.scd b/doc/yambar-particles.5.scd index 312fb99..f500644 100644 --- a/doc/yambar-particles.5.scd +++ b/doc/yambar-particles.5.scd @@ -208,7 +208,37 @@ content: # MAP This particle maps the values of a specific tag to different -particles. In addition to explicit tag values, you can also specify a +particles based on conditions. A condition takes the form of: + + + +Where is the tag you would like to map, is one of: + +[- == +:- != +:- >= +:- > +:- <= +:- < + +and is the value you would like to compare it to. Conditions +may be chained together using either '&&' or '||': + + && + +You may surround the *whole expression* with parenthesis to make it +more readable: + +( && ) + +Note that "nested" conditions are *NOT* supported. That is, something +like ( && ( || )) will *NOT* work. + +Furthermore, *conditions are evaluated with a strcmp*. This means +some odd behaviour may arise if prefixes (such as zeroes) are added +to numerical constants. + +In addition to explicit tag values, you can also specify a default/fallback particle. ## CONFIGURATION @@ -217,34 +247,29 @@ default/fallback particle. :[ *Type* :[ *Req* :[ *Description* -| tag -: string -: yes -: The tag (name of) which values should be mapped -| values +| conditions : associative array : yes -: An associative array of tag values mapped to particles +: An associative array of conditions (see above) mapped to particles | default : particle : no -: Default particle to use, when tag's value does not match any of the - mapped values. +: Default particle to use, none of the conditions are true ## EXAMPLES ``` content: map: - tag: tag_name default: string: text: this is the default particle; the tag's value is now {tag_name} - values: - one_value: + # Note, below, how the parenthesis are optional + conditions: + (tag == one_value): string: text: tag's value is now one_value - another_value: + tag == another_value: string: text: tag's value is now another_value diff --git a/particles/map.c b/particles/map.c index abf76a6..a0f0903 100644 --- a/particles/map.c +++ b/particles/map.c @@ -1,5 +1,6 @@ #include #include +#include #include #define LOG_MODULE "map" @@ -10,13 +11,218 @@ #include "../plugin.h" #include "dynlist.h" +enum map_op{ + MAP_OP_EQ, + MAP_OP_NE, + MAP_OP_LE, + MAP_OP_LT, + MAP_OP_GE, + MAP_OP_GT, + /* The next two are for bool types */ + MAP_OP_SELF, + MAP_OP_NOT, +}; + +struct map_condition { + char *tag; + enum map_op op; + char *value; +}; + +static char * +trim(char *s) +{ + while (*s == ' ') + s++; + + char *end = s + strlen(s) - 1; + while (*end == ' ') { + *end = '\0'; + end--; + } + + return s; +} + +bool +int_condition(const long tag_value, const long cond_value, enum map_op op) +{ + switch (op) { + case MAP_OP_EQ: return tag_value == cond_value; + case MAP_OP_NE: return tag_value != cond_value; + case MAP_OP_LE: return tag_value <= cond_value; + case MAP_OP_LT: return tag_value < cond_value; + case MAP_OP_GE: return tag_value >= cond_value; + case MAP_OP_GT: return tag_value > cond_value; + default: return false; + } +} + +bool +float_condition(const double tag_value, const double cond_value, enum map_op op) +{ + switch (op) { + case MAP_OP_EQ: return tag_value == cond_value; + case MAP_OP_NE: return tag_value != cond_value; + case MAP_OP_LE: return tag_value <= cond_value; + case MAP_OP_LT: return tag_value < cond_value; + case MAP_OP_GE: return tag_value >= cond_value; + case MAP_OP_GT: return tag_value > cond_value; + default: return false; + } +} + +bool +str_condition(const char* tag_value, const char* cond_value, enum map_op op) +{ + switch (op) { + case MAP_OP_EQ: return strcmp(tag_value, cond_value) == 0; + case MAP_OP_NE: return strcmp(tag_value, cond_value) != 0; + case MAP_OP_LE: return strcmp(tag_value, cond_value) <= 0; + case MAP_OP_LT: return strcmp(tag_value, cond_value) < 0; + case MAP_OP_GE: return strcmp(tag_value, cond_value) >= 0; + case MAP_OP_GT: return strcmp(tag_value, cond_value) > 0; + default: return false; + } +} + +bool +eval_map_condition(const struct map_condition* map_cond, const struct tag_set *tags) +{ + const struct tag *tag = tag_for_name(tags, map_cond->tag); + if (tag == NULL) { + LOG_WARN("tag %s not found", map_cond->tag); + return false; + } + + switch (tag->type(tag)) { + case TAG_TYPE_INT: { + errno = 0; + char *end; + const long cond_value = strtol(map_cond->value, &end, 0); + + if (errno==ERANGE) { + LOG_WARN("value %s is too large", map_cond->value); + return false; + } else if (*end != '\0') { + LOG_WARN("failed to parse %s into int", map_cond->value); + return false; + } + + const long tag_value = tag->as_int(tag); + return int_condition(tag_value, cond_value, map_cond->op); + } + case TAG_TYPE_FLOAT: { + errno = 0; + char *end; + const double cond_value = strtod(map_cond->value, &end); + + if (errno==ERANGE) { + LOG_WARN("value %s is too large", map_cond->value); + return false; + } else if (*end != '\0') { + LOG_WARN("failed to parse %s into float", map_cond->value); + return false; + } + + const double tag_value = tag->as_float(tag); + return float_condition(tag_value, cond_value, map_cond->op); + } + case TAG_TYPE_BOOL: + if (map_cond->op == MAP_OP_SELF) + return tag->as_bool(tag); + else if (map_cond->op == MAP_OP_NOT) + return !tag->as_bool(tag); + else { + LOG_WARN("boolean tag '%s' should be used directly", map_cond->tag); + return false; + } + case TAG_TYPE_STRING: { + const char* tag_value = tag->as_string(tag); + return str_condition(tag_value, map_cond->value, map_cond->op); + } + } + return false; +} + +struct map_condition* +map_condition_from_str(const char *str) +{ + struct map_condition *cond = malloc(sizeof(*cond)); + + /* This strdup is just to discard the 'const' qualifier */ + char *str_cpy = strdup(str); + char *op_str = strpbrk(str_cpy, "=!<>~"); + + /* we've already checked things in the verify functions, so these should all work */ + char *tag = str_cpy; + char *value = NULL; + + if (op_str == NULL) { + cond->tag = strdup(trim(tag)); + cond->op = MAP_OP_SELF; + cond->value = NULL; + free(str_cpy); + return cond; + } + + switch (op_str[0]) { + case '=': + cond->op = MAP_OP_EQ; + value = op_str + 2; + break; + case '!': + cond->op = MAP_OP_NE; + value = op_str + 2; + break; + case '<': + if (op_str[1] == '=') { + cond->op = MAP_OP_LE; + value = op_str + 2; + } else { + cond->op = MAP_OP_LT; + value = op_str + 1; + } + break; + case '>': + if (op_str[1] == '=') { + cond->op = MAP_OP_GE; + value = op_str + 2; + } else { + cond->op = MAP_OP_GT; + value = op_str + 1; + } + break; + case '~': + tag = op_str + 1; + cond->op = MAP_OP_NOT; + break; + } + + /* NULL terminate the tag value */ + op_str[0] = '\0'; + + cond->tag = strdup(trim(tag)); + cond->value = value != NULL ? strdup(trim(value)) : NULL; + + free(str_cpy); + return cond; +} + +void +free_map_condition(struct map_condition* mc) +{ + free(mc->tag); + free(mc->value); + free(mc); +} + struct particle_map { - const char *tag_value; + struct map_condition *condition; struct particle *particle; }; struct private { - char *tag; struct particle *default_particle; struct particle_map *map; size_t count; @@ -92,21 +298,12 @@ static struct exposable * instantiate(const struct particle *particle, const struct tag_set *tags) { const struct private *p = particle->private; - const struct tag *tag = tag_for_name(tags, p->tag); - - if (tag == NULL) { - return p->default_particle != NULL - ? p->default_particle->instantiate(p->default_particle, tags) - : dynlist_exposable_new(NULL, 0, 0, 0); - } - - const char *tag_value = tag->as_string(tag); struct particle *pp = NULL; for (size_t i = 0; i < p->count; i++) { const struct particle_map *e = &p->map[i]; - if (strcmp(e->tag_value, tag_value) != 0) + if (!eval_map_condition(e->condition, tags)) continue; pp = e->particle; @@ -144,28 +341,25 @@ particle_destroy(struct particle *particle) for (size_t i = 0; i < p->count; i++) { struct particle *pp = p->map[i].particle; pp->destroy(pp); - free((char *)p->map[i].tag_value); + free_map_condition(p->map[i].condition); } free(p->map); - free(p->tag); free(p); particle_default_destroy(particle); } static struct particle * -map_new(struct particle *common, const char *tag, - const struct particle_map particle_map[], size_t count, - struct particle *default_particle) +map_new(struct particle *common, const struct particle_map particle_map[], + size_t count, struct particle *default_particle) { struct private *priv = calloc(1, sizeof(*priv)); - priv->tag = strdup(tag); priv->default_particle = default_particle; priv->count = count; priv->map = malloc(count * sizeof(priv->map[0])); for (size_t i = 0; i < count; i++) { - priv->map[i].tag_value = strdup(particle_map[i].tag_value); + priv->map[i].condition = particle_map[i].condition; priv->map[i].particle = particle_map[i].particle; } @@ -176,7 +370,103 @@ map_new(struct particle *common, const char *tag, } static bool -verify_map_values(keychain_t *chain, const struct yml_node *node) +verify_map_condition_syntax(keychain_t *chain, const struct yml_node *node, + const char *line) +{ + /* We need this strdup to discard the 'const' qualifier */ + char *cond = strdup(line); + char *op_str = strpbrk(cond, " =!<>~"); + if (op_str == NULL) { + char *tag = trim(cond); + if (tag[0] == '\0') + goto syntax_fail_missing_tag; + + free(cond); + return true; + } + op_str = trim(op_str); + + char *tag = cond; + char *value = NULL; + + switch (op_str[0]) { + case '=': + if (op_str[1] != '=') + goto syntax_fail_invalid_op; + + value = op_str + 2; + break; + + case '!': + if (op_str[1] != '=') + goto syntax_fail_invalid_op; + + value = op_str + 2; + break; + + case '<': + if (op_str[1] == '=') + value = op_str + 2; + else + value = op_str + 1; + break; + + case '>': + if (op_str[1] == '=') + value = op_str + 2; + else + value = op_str + 1; + break; + + case '~': + tag = op_str + 1; + if (strpbrk(tag, " =!<>~") != NULL) + goto syntax_fail_bad_not; + value = NULL; + break; + default: + goto syntax_fail_invalid_op; + } + + /* NULL terminate the tag value */ + op_str[0] = '\0'; + + tag = trim(tag); + if (tag[0] == '\0') + goto syntax_fail_missing_tag; + + value = value != NULL ? trim(value) : NULL; + + if (value != NULL && value[0] == '\0') + goto syntax_fail_missing_value; + + free(cond); + return true; + +syntax_fail_invalid_op: + LOG_ERR("%s: \"%s\" invalid operator", conf_err_prefix(chain, node), line); + goto err; + +syntax_fail_missing_tag: + LOG_ERR("%s: \"%s\" missing tag", conf_err_prefix(chain, node), line); + goto err; + +syntax_fail_missing_value: + LOG_ERR("%s: \"%s\" missing value", conf_err_prefix(chain, node), line); + goto err; + +syntax_fail_bad_not: + LOG_ERR("%s: \"%s\" '~' cannot be used with other operators", + conf_err_prefix(chain, node), line); + goto err; + +err: + free(cond); + return false; +} + +static bool +verify_map_conditions(keychain_t *chain, const struct yml_node *node) { if (!yml_is_dict(node)) { LOG_ERR( @@ -195,6 +485,9 @@ verify_map_values(keychain_t *chain, const struct yml_node *node) return false; } + if (!verify_map_condition_syntax(chain, it.key, key)) + return false; + if (!conf_verify_particle(chain_push(chain, key), it.value)) return false; @@ -207,11 +500,10 @@ verify_map_values(keychain_t *chain, const struct yml_node *node) static struct particle * from_conf(const struct yml_node *node, struct particle *common) { - const struct yml_node *tag = yml_get_value(node, "tag"); - const struct yml_node *values = yml_get_value(node, "values"); + const struct yml_node *conditions = yml_get_value(node, "conditions"); const struct yml_node *def = yml_get_value(node, "default"); - struct particle_map particle_map[yml_dict_length(values)]; + struct particle_map particle_map[yml_dict_length(conditions)]; struct conf_inherit inherited = { .font = common->font, @@ -220,28 +512,25 @@ from_conf(const struct yml_node *node, struct particle *common) }; size_t idx = 0; - for (struct yml_dict_iter it = yml_dict_iter(values); + for (struct yml_dict_iter it = yml_dict_iter(conditions); it.key != NULL; yml_dict_next(&it), idx++) { - particle_map[idx].tag_value = yml_value_as_string(it.key); + particle_map[idx].condition = map_condition_from_str(yml_value_as_string(it.key)); particle_map[idx].particle = conf_to_particle(it.value, inherited); } struct particle *default_particle = def != NULL ? conf_to_particle(def, inherited) : NULL; - return map_new( - common, yml_value_as_string(tag), particle_map, yml_dict_length(values), - default_particle); + return map_new(common, particle_map, yml_dict_length(conditions), default_particle); } static bool verify_conf(keychain_t *chain, const struct yml_node *node) { static const struct attr_info attrs[] = { - {"tag", true, &conf_verify_string}, - {"values", true, &verify_map_values}, + {"conditions", true, &verify_map_conditions}, {"default", false, &conf_verify_particle}, PARTICLE_COMMON_ATTRS, }; diff --git a/tag.c b/tag.c index 66b7f02..5c5684b 100644 --- a/tag.c +++ b/tag.c @@ -32,6 +32,30 @@ tag_name(const struct tag *tag) return priv->name; } +static enum tag_type +bool_type(const struct tag *tag) +{ + return TAG_TYPE_BOOL; +} + +static enum tag_type +int_type(const struct tag *tag) +{ + return TAG_TYPE_INT; +} + +static enum tag_type +float_type(const struct tag *tag) +{ + return TAG_TYPE_FLOAT; +} + +static enum tag_type +string_type(const struct tag *tag) +{ + return TAG_TYPE_STRING; +} + static long unimpl_min_max(const struct tag *tag) { @@ -264,6 +288,7 @@ tag_new_int_realtime(struct module *owner, const char *name, long value, tag->owner = owner; tag->destroy = &destroy_int_and_float; tag->name = &tag_name; + tag->type = &int_type; tag->min = &int_min; tag->max = &int_max; tag->realtime = &int_realtime; @@ -287,6 +312,7 @@ tag_new_bool(struct module *owner, const char *name, bool value) tag->owner = owner; tag->destroy = &destroy_int_and_float; tag->name = &tag_name; + tag->type = &bool_type; tag->min = &unimpl_min_max; tag->max = &unimpl_min_max; tag->realtime = &no_realtime; @@ -310,6 +336,7 @@ tag_new_float(struct module *owner, const char *name, double value) tag->owner = owner; tag->destroy = &destroy_int_and_float; tag->name = &tag_name; + tag->type = &float_type; tag->min = &unimpl_min_max; tag->max = &unimpl_min_max; tag->realtime = &no_realtime; @@ -333,6 +360,7 @@ tag_new_string(struct module *owner, const char *name, const char *value) tag->owner = owner; tag->destroy = &destroy_string; tag->name = &tag_name; + tag->type = &string_type; tag->min = &unimpl_min_max; tag->max = &unimpl_min_max; tag->realtime = &no_realtime; diff --git a/tag.h b/tag.h index d6bfe6a..1e9742a 100644 --- a/tag.h +++ b/tag.h @@ -3,6 +3,13 @@ #include #include +enum tag_type { + TAG_TYPE_BOOL, + TAG_TYPE_INT, + TAG_TYPE_FLOAT, + TAG_TYPE_STRING, +}; + enum tag_realtime_unit { TAG_REALTIME_NONE, TAG_REALTIME_SECS, @@ -17,6 +24,7 @@ struct tag { void (*destroy)(struct tag *tag); const char *(*name)(const struct tag *tag); + enum tag_type (*type)(const struct tag *tag); const char *(*as_string)(const struct tag *tag); long (*as_int)(const struct tag *tag); bool (*as_bool)(const struct tag *tag); diff --git a/test/full-conf-good.yml b/test/full-conf-good.yml index 6270487..d89d494 100644 --- a/test/full-conf-good.yml +++ b/test/full-conf-good.yml @@ -62,10 +62,9 @@ bar: - clock: content: map: - tag: date default: {string: {text: default value}} - values: - 1234: {string: {text: specific value}} + conditions: + date == 1234: {string: {text: specific value}} - clock: content: progress-bar: From 4c4a20d83575abc567fac36c1f57c8af6421cee4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Gibrowski=20Fa=C3=A9?= Date: Tue, 19 Apr 2022 22:05:42 -0300 Subject: [PATCH 355/611] Updated docs to comply with new map syntax --- doc/yambar-modules-foreign-toplevel.5.scd | 4 +- doc/yambar-modules-i3.5.scd | 2 +- doc/yambar-modules-removables.5.scd | 4 +- doc/yambar-modules-river.5.scd | 4 +- doc/yambar-modules-script.5.scd | 4 +- doc/yambar-modules.5.scd | 8 +- doc/yambar-particles.5.scd | 40 +++-- examples/configurations/laptop.conf | 187 ++++++++++------------ examples/configurations/river-tags.conf | 44 +++-- 9 files changed, 138 insertions(+), 159 deletions(-) diff --git a/doc/yambar-modules-foreign-toplevel.5.scd b/doc/yambar-modules-foreign-toplevel.5.scd index 5ef8c57..94c4b84 100644 --- a/doc/yambar-modules-foreign-toplevel.5.scd +++ b/doc/yambar-modules-foreign-toplevel.5.scd @@ -68,8 +68,8 @@ bar: content: map: conditions: - (activated == false): {empty: {}} - (activated == true): + ~activated: {empty: {}} + activated: - string: {text: "{app-id}: {title}"} ``` diff --git a/doc/yambar-modules-i3.5.scd b/doc/yambar-modules-i3.5.scd index e646183..f82d131 100644 --- a/doc/yambar-modules-i3.5.scd +++ b/doc/yambar-modules-i3.5.scd @@ -104,7 +104,7 @@ bar: map: default: {string: {text: "{name}"}} conditions: - (state == focused): {string: {text: "{name}*"}} + state == focused: {string: {text: "{name}*"}} current: { string: {text: "{application}: {title}"}} ``` diff --git a/doc/yambar-modules-removables.5.scd b/doc/yambar-modules-removables.5.scd index 5c34aef..ee7911f 100644 --- a/doc/yambar-modules-removables.5.scd +++ b/doc/yambar-modules-removables.5.scd @@ -75,11 +75,11 @@ bar: content: map: conditions: - (mounted == false): + ~mounted: string: on-click: udisksctl mount -b {device} text: "{label}" - (mounted == true): + mounted: string: on-click: udisksctl unmount -b {device} text: "{label}" diff --git a/doc/yambar-modules-river.5.scd b/doc/yambar-modules-river.5.scd index 2255dc0..7ba2441 100644 --- a/doc/yambar-modules-river.5.scd +++ b/doc/yambar-modules-river.5.scd @@ -80,8 +80,8 @@ bar: content: map: conditions: - (occupied == false): {empty: {}} - (occupied == true): + ~occupied: {empty: {}} + occupied: string: margin: 5 text: "{id}: {state}" diff --git a/doc/yambar-modules-script.5.scd b/doc/yambar-modules-script.5.scd index 357bae1..d021bf5 100644 --- a/doc/yambar-modules-script.5.scd +++ b/doc/yambar-modules-script.5.scd @@ -134,8 +134,8 @@ bar: content: map: conditions: - (status == Paused): {empty: {}} - (status == Playing): + status == Paused: {empty: {}} + status == Playing: content: {string: {text: "{artist} - {title}"}} ``` diff --git a/doc/yambar-modules.5.scd b/doc/yambar-modules.5.scd index b7177c4..ef47f62 100644 --- a/doc/yambar-modules.5.scd +++ b/doc/yambar-modules.5.scd @@ -69,16 +69,16 @@ in red. content: map: conditions: - (carrier == false): {empty: {}} - (carrier == true): + ~carrier: {empty: {}} + carrier: map: default: {string: {text: , font: *awesome, foreground: ffffff66}} conditions: - (state == up): + state == up: map: default: {string: {text: , font: *awesome}} conditions: - (ipv4 == ""): {string: {text: , font: *awesome, foreground: ffffff66}} + ipv4 == "": {string: {text: , font: *awesome, foreground: ffffff66}} ``` ## Use yaml anchors diff --git a/doc/yambar-particles.5.scd b/doc/yambar-particles.5.scd index f500644..aaf8a92 100644 --- a/doc/yambar-particles.5.scd +++ b/doc/yambar-particles.5.scd @@ -208,10 +208,14 @@ content: # MAP This particle maps the values of a specific tag to different -particles based on conditions. A condition takes the form of: +particles based on conditions. A condition takes either the form of: +Or, for boolean tags: + + + Where is the tag you would like to map, is one of: [- == @@ -221,22 +225,11 @@ Where is the tag you would like to map, is one of: :- <= :- < -and is the value you would like to compare it to. Conditions -may be chained together using either '&&' or '||': +and is the value you would like to compare it to. - && +For boolean tags, negation is done with a preceding '~': -You may surround the *whole expression* with parenthesis to make it -more readable: - -( && ) - -Note that "nested" conditions are *NOT* supported. That is, something -like ( && ( || )) will *NOT* work. - -Furthermore, *conditions are evaluated with a strcmp*. This means -some odd behaviour may arise if prefixes (such as zeroes) are added -to numerical constants. +~ In addition to explicit tag values, you can also specify a default/fallback particle. @@ -264,9 +257,8 @@ content: default: string: text: this is the default particle; the tag's value is now {tag_name} - # Note, below, how the parenthesis are optional conditions: - (tag == one_value): + tag == one_value: string: text: tag's value is now one_value tag == another_value: @@ -275,6 +267,20 @@ content: ``` +For a boolean tag: + +``` +content: + map: + conditions: + tag: + string: + text: tag is true + ~tag: + string: + text: tag is false +``` + # RAMP This particle uses a range tag to index into an array of diff --git a/examples/configurations/laptop.conf b/examples/configurations/laptop.conf index 888b19e..573516c 100644 --- a/examples/configurations/laptop.conf +++ b/examples/configurations/laptop.conf @@ -46,73 +46,65 @@ bar: foreground: 000000ff deco: {stack: [background: {color: bc2b3fff}, <<: *std_underline]} - map: &i3_mode - tag: mode default: - string: margin: 5 text: "{mode}" deco: {background: {color: cc421dff}} - empty: {right-margin: 7} - values: - default: {empty: {}} + conditions: + mode == default: {empty: {}} content: "": map: - tag: state - values: - focused: {string: {<<: [*default, *focused]}} - unfocused: {string: {<<: *default}} - invisible: {string: {<<: [*default, *invisible]}} - urgent: {string: {<<: [*default, *urgent]}} + conditions: + state == focused: {string: {<<: [*default, *focused]}} + state == unfocused: {string: {<<: *default}} + state == invisible: {string: {<<: [*default, *invisible]}} + state == urgent: {string: {<<: [*default, *urgent]}} main: map: - tag: state - values: - focused: {string: {<<: [*main, *focused]}} - unfocused: {string: {<<: *main}} - invisible: {string: {<<: [*main, *invisible]}} - urgent: {string: {<<: [*main, *urgent]}} + conditions: + state == focused: {string: {<<: [*main, *focused]}} + state == unfocused: {string: {<<: *main}} + state == invisible: {string: {<<: [*main, *invisible]}} + state == urgent: {string: {<<: [*main, *urgent]}} surfing: map: - tag: state - values: - focused: {string: {<<: [*surfing, *focused]}} - unfocused: {string: {<<: *surfing}} - invisible: {string: {<<: [*surfing, *invisible]}} - urgent: {string: {<<: [*surfing, *urgent]}} + conditions: + state == focused: {string: {<<: [*surfing, *focused]}} + state == unfocused: {string: {<<: *surfing}} + state == invisible: {string: {<<: [*surfing, *invisible]}} + state == urgent: {string: {<<: [*surfing, *urgent]}} misc: map: - tag: state - values: - focused: {string: {<<: [*misc, *focused]}} - unfocused: {string: {<<: *misc}} - invisible: {string: {<<: [*misc, *invisible]}} - urgent: {string: {<<: [*misc, *urgent]}} + conditions: + state == focused: {string: {<<: [*misc, *focused]}} + state == unfocused: {string: {<<: *misc}} + state == invisible: {string: {<<: [*misc, *invisible]}} + state == urgent: {string: {<<: [*misc, *urgent]}} mail: map: - tag: state - values: - focused: {string: {<<: [*mail, *focused]}} - unfocused: {string: {<<: *mail}} - invisible: {string: {<<: [*mail, *invisible]}} - urgent: {string: {<<: [*mail, *urgent]}} + conditions: + state == focused: {string: {<<: [*mail, *focused]}} + state == unfocused: {string: {<<: *mail}} + state == invisible: {string: {<<: [*mail, *invisible]}} + state == urgent: {string: {<<: [*mail, *urgent]}} music: map: - tag: state - values: - focused: {string: {<<: [*music, *focused]}} - unfocused: {string: {<<: *music}} - invisible: {string: {<<: [*music, *invisible]}} - urgent: {string: {<<: [*music, *urgent]}} + conditions: + state == focused: {string: {<<: [*music, *focused]}} + state == unfocused: {string: {<<: *music}} + state == invisible: {string: {<<: [*music, *invisible]}} + state == urgent: {string: {<<: [*music, *urgent]}} - foreign-toplevel: content: map: - tag: activated - values: - false: {empty: {}} - true: + conditions: + ~activated: {empty: {}} + activated: - string: {text: "{app-id}", foreground: ffa0a0ff} - string: {text: ": {title}"} center: @@ -123,32 +115,28 @@ bar: spacing: 0 items: - map: - tag: state - values: - playing: {string: {text: "{artist}"}} - paused: {string: {text: "{artist}", foreground: ffffff66}} + conditions: + state == playing: {string: {text: "{artist}"}} + state == paused: {string: {text: "{artist}", foreground: ffffff66}} - string: {text: " | ", foreground: ffffff66} - map: - tag: state - values: - playing: {string: {text: "{album}"}} - paused: {string: {text: "{album}", foreground: ffffff66}} + conditions: + state == playing: {string: {text: "{album}"}} + state == paused: {string: {text: "{album}", foreground: ffffff66}} - string: {text: " | ", foreground: ffffff66} - map: - tag: state - values: - playing: {string: {text: "{title}", foreground: ffa0a0ff}} - paused: {string: {text: "{title}", foreground: ffffff66}} + conditions: + state == playing: {string: {text: "{title}", foreground: ffa0a0ff}} + state == paused: {string: {text: "{title}", foreground: ffffff66}} content: map: margin: 10 - tag: state - values: - offline: {string: {text: offline, foreground: ff0000ff}} - stopped: {string: {text: stopped}} - paused: {list: *artist_album_title} - playing: {list: *artist_album_title} + conditions: + state == offline: {string: {text: offline, foreground: ff0000ff}} + state == stopped: {string: {text: stopped}} + state == paused: {list: *artist_album_title} + state == playing: {list: *artist_album_title} right: - removables: @@ -158,24 +146,21 @@ bar: spacing: 5 content: map: - tag: mounted - values: - false: + conditions: + ~mounted: map: - tag: optical on-click: udisksctl mount -b {device} - values: - false: [{string: *drive}, {string: {text: "{label}"}}] - true: [{string: *optical}, {string: {text: "{label}"}}] - true: + conditions: + ~optical: [{string: *drive}, {string: {text: "{label}"}}] + optical: [{string: *optical}, {string: {text: "{label}"}}] + mounted: map: - tag: optical on-click: udisksctl unmount -b {device} - values: - false: + conditions: + ~optical: - string: {<<: *drive, deco: *std_underline} - string: {text: "{label}"} - true: + optical: - string: {<<: *optical, deco: *std_underline} - string: {text: "{label}"} - sway-xkb: @@ -187,36 +172,31 @@ bar: name: enp1s0 content: map: - tag: carrier - values: - false: {empty: {}} - true: + conditions: + ~carrier: {empty: {}} + carrier: map: - tag: state default: {string: {text: , font: *awesome, foreground: ffffff66}} - values: - up: + conditions: + state == up: map: - tag: ipv4 default: {string: {text: , font: *awesome}} - values: - "": {string: {text: , font: *awesome, foreground: ffffff66}} + conditions: + ipv4 == "": {string: {text: , font: *awesome, foreground: ffffff66}} - network: name: wlp2s0 content: map: - tag: state default: {string: {text: , font: *awesome, foreground: ffffff66}} - values: - down: {string: {text: , font: *awesome, foreground: ff0000ff}} - up: + conditions: + state == down: {string: {text: , font: *awesome, foreground: ff0000ff}} + state == up: map: - tag: ipv4 default: - string: {text: , font: *awesome} - string: {text: "{ssid}"} - values: - "": + conditions: + ipv4 == "": - string: {text: , font: *awesome, foreground: ffffff66} - string: {text: "{ssid}", foreground: ffffff66} - alsa: @@ -224,16 +204,14 @@ bar: mixer: Master content: map: - tag: online - values: - false: {string: {text: , font: *awesome, foreground: ff0000ff}} - true: + conditions: + ~online: {string: {text: , font: *awesome, foreground: ff0000ff}} + online: map: on-click: /bin/sh -c "amixer -q sset Speaker unmute && amixer -q sset Headphone unmute && amixer -q sset Master toggle" - tag: muted - values: - true: {string: {text: , font: *awesome, foreground: ffffff66}} - false: + conditions: + muted: {string: {text: , font: *awesome, foreground: ffffff66}} + ~muted: ramp: tag: volume items: @@ -266,19 +244,18 @@ bar: - string: {text: "{capacity}% {estimate}"} content: map: - tag: state - values: - unknown: + conditions: + state == unknown: <<: *discharging - discharging: + state == discharging: <<: *discharging - charging: + state == charging: - string: {text: , foreground: 00ff00ff, font: *awesome} - string: {text: "{capacity}% {estimate}"} - full: + state == full: - string: {text: , foreground: 00ff00ff, font: *awesome} - string: {text: "{capacity}% full"} - not charging: + state == not charging: - ramp: tag: capacity items: diff --git a/examples/configurations/river-tags.conf b/examples/configurations/river-tags.conf index 94e5874..462a329 100644 --- a/examples/configurations/river-tags.conf +++ b/examples/configurations/river-tags.conf @@ -16,16 +16,15 @@ bar: - base: &river_base left-margin: 10 right-margin: 13 - tag: id default: {string: {text: , font: *hack}} - values: - 1: {string: {text: ﳐ, font: *hack}} - 2: {string: {text: , font: *hack}} - 3: {string: {text: , font: *hack}} - 4: {string: {text: , font: *hack}} - 5: {string: {text: , font: *hack}} - 10: {string: {text: "scratchpad", font: *hack}} - 11: {string: {text: "work", font: *hack}} + conditions: + id == 1: {string: {text: ﳐ, font: *hack}} + id == 2: {string: {text: , font: *hack}} + id == 3: {string: {text: , font: *hack}} + id == 4: {string: {text: , font: *hack}} + id == 5: {string: {text: , font: *hack}} + id == 10: {string: {text: "scratchpad", font: *hack}} + id == 11: {string: {text: "work", font: *hack}} content: map: @@ -33,28 +32,25 @@ bar: left: sh -c "riverctl set-focused-tags $((1 << ({id} - 1)))" right: sh -c "riverctl toggle-focused-tags $((1 << ({id} -1)))" middle: sh -c "riverctl toggle-view-tags $((1 << ({id} -1)))" - tag: state - values: - urgent: + conditions: + state == urgent: map: <<: *river_base deco: {background: {color: D08770ff}} - focused: + state == focused: map: <<: *river_base deco: *bg_default - visible: + state == visible: map: - tag: occupied - values: - false: {map: {<<: *river_base}} - true: {map: {<<: *river_base, deco: *bg_default}} - unfocused: + conditions: + ~occupied: {map: {<<: *river_base}} + occupied: {map: {<<: *river_base, deco: *bg_default}} + state == unfocused: map: <<: *river_base - invisible: + state == invisible: map: - tag: occupied - values: - false: {empty: {}} - true: {map: {<<: *river_base, deco: {underline: {size: 3, color: ea6962ff}}}} + conditions: + ~occupied: {empty: {}} + occupied: {map: {<<: *river_base, deco: {underline: {size: 3, color: ea6962ff}}}} From 0d878e8b5c956653cf15963d3b0bbc405aac2887 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Gibrowski=20Fa=C3=A9?= Date: Thu, 21 Apr 2022 01:06:04 -0300 Subject: [PATCH 356/611] Trimming outer '"' when parsing the values. --- doc/yambar-particles.5.scd | 4 ++++ particles/map.c | 11 ++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/doc/yambar-particles.5.scd b/doc/yambar-particles.5.scd index aaf8a92..0ac7e72 100644 --- a/doc/yambar-particles.5.scd +++ b/doc/yambar-particles.5.scd @@ -231,6 +231,10 @@ For boolean tags, negation is done with a preceding '~': ~ +To match for empty strings, use ' "" ': + + == "" + In addition to explicit tag values, you can also specify a default/fallback particle. diff --git a/particles/map.c b/particles/map.c index a0f0903..2c610c8 100644 --- a/particles/map.c +++ b/particles/map.c @@ -203,7 +203,16 @@ map_condition_from_str(const char *str) op_str[0] = '\0'; cond->tag = strdup(trim(tag)); - cond->value = value != NULL ? strdup(trim(value)) : NULL; + + cond->value = NULL; + if (value != NULL){ + value = trim(value); + if (value[0] == '"' && value[strlen(value) - 1] == '"'){ + value[strlen(value) - 1] = '\0'; + ++value; + } + cond->value = strdup(value); + } free(str_cpy); return cond; From 82a3b2ae112cfffef7ea34b3f0664ae45ef48402 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Gibrowski=20Fa=C3=A9?= Date: Thu, 21 Apr 2022 11:48:38 -0300 Subject: [PATCH 357/611] Updates CHANGELOG.md and changes map.c formatting --- CHANGELOG.md | 42 ++++++++++++++++++++++++++++++++++++++++++ particles/map.c | 11 ++++++----- 2 files changed, 48 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index da20255..f3d8642 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,46 @@ ### Changed * Minimum required meson version is now 0.58. +* **BREAKING CHANGE**: overhaul of the `map` particle. Instead of + specifying a `tag` and then an array of `values`, you must now + simply use an array of `conditions`, that consist of: + + ` ` + + where `` is one of: + + `== != < <= > >=` + + Note that boolean tags must be used as is: + + `online` + + `~online # use '~' to match for their falsehood` + + As an example, if you previously had something like: + + ``` + map: + tag: State + values: + unrecognized: + ... + ``` + + You would now write it as: + + ``` + map: + conditions: + State == unrecognized: + ... + ``` + + For a more thorough explanation, see the updated map section in the + man page for yambar-particles([#137][137] and [#175][175]). + + [137]: https://codeberg.org/dnkl/yambar/issues/137 + [175]: https://codeberg.org/dnkl/yambar/issues/172 ### Deprecated @@ -48,6 +88,8 @@ ### Security ### Contributors +* Horus + ## 1.8.0 diff --git a/particles/map.c b/particles/map.c index 2c610c8..c748bea 100644 --- a/particles/map.c +++ b/particles/map.c @@ -101,7 +101,7 @@ eval_map_condition(const struct map_condition* map_cond, const struct tag_set *t char *end; const long cond_value = strtol(map_cond->value, &end, 0); - if (errno==ERANGE) { + if (errno == ERANGE) { LOG_WARN("value %s is too large", map_cond->value); return false; } else if (*end != '\0') { @@ -117,7 +117,7 @@ eval_map_condition(const struct map_condition* map_cond, const struct tag_set *t char *end; const double cond_value = strtod(map_cond->value, &end); - if (errno==ERANGE) { + if (errno == ERANGE) { LOG_WARN("value %s is too large", map_cond->value); return false; } else if (*end != '\0') { @@ -205,10 +205,11 @@ map_condition_from_str(const char *str) cond->tag = strdup(trim(tag)); cond->value = NULL; - if (value != NULL){ + if (value != NULL) { value = trim(value); - if (value[0] == '"' && value[strlen(value) - 1] == '"'){ - value[strlen(value) - 1] = '\0'; + const size_t value_len = strlen(value); + if (value[0] == '"' && value[value_len - 1] == '"') { + value[value_len - 1] = '\0'; ++value; } cond->value = strdup(value); From 3b5845370c0b66bc0acf59497b86f2866aa24d2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Gibrowski=20Fa=C3=A9?= Date: Sun, 24 Apr 2022 11:27:32 -0300 Subject: [PATCH 358/611] doc: explain that order in map conditions matter --- doc/yambar-particles.5.scd | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/doc/yambar-particles.5.scd b/doc/yambar-particles.5.scd index 0ac7e72..dba606a 100644 --- a/doc/yambar-particles.5.scd +++ b/doc/yambar-particles.5.scd @@ -238,6 +238,25 @@ To match for empty strings, use ' "" ': In addition to explicit tag values, you can also specify a default/fallback particle. +Note that conditions are evaluated in the order they appear. *If +multiple conditions are true, the first one will be used*. This means +that in a configuration such as: + +``` +tx-bitrate > 1000: +tx-bitrate > 1000000: +``` + +the second condition would never run, since whenever the second +condition is true, the first is also true. The correct way of doing +this would be to invert the order of the conditions: + +``` +tx-bitrate > 1000000: +tx-bitrate > 1000: +``` + + ## CONFIGURATION [[ *Name* From 5fc092a874ab8e3b2ca5eebb91fe2202701fb9a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 24 Apr 2022 11:04:37 +0200 Subject: [PATCH 359/611] examples: dwl-tags: adapt parsing of output to recent DWL changes(?) Closes #178 --- examples/scripts/dwl-tags.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/scripts/dwl-tags.sh b/examples/scripts/dwl-tags.sh index 395a790..bc24ac7 100755 --- a/examples/scripts/dwl-tags.sh +++ b/examples/scripts/dwl-tags.sh @@ -127,7 +127,7 @@ while true; do inotifywait -qq --event modify "${fname}" # Get info from the file - output="$(tail -n4 "${fname}")" + output="$(tail -n6 "${fname}")" title="$(echo "${output}" | grep title | cut -d ' ' -f 3- )" #selmon="$(echo "${output}" | grep 'selmon')" layout="$(echo "${output}" | grep layout | cut -d ' ' -f 3- )" From a3a03340699edc9c3e1a053c9632d45060a75744 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 24 Apr 2022 11:06:27 +0200 Subject: [PATCH 360/611] changelog: dwl-tags updates --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f3d8642..67195a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -80,9 +80,11 @@ e.g. when handling mouse wheel events ([#169][169]). * cpu: don’t error out on systems where SMT has been disabled ([#172][172]). +* examples/dwl-tags: updated parsing of `output` name ([#178][178]). [169]: https://codeberg.org/dnkl/yambar/issues/169 [172]: https://codeberg.org/dnkl/yambar/issues/172 +[178]: https://codeberg.org/dnkl/yambar/issues/178 ### Security From c738f1c63d6e0a7fe09349d8e62f069fe5c34f55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 2 Jun 2022 17:24:42 +0200 Subject: [PATCH 361/611] =?UTF-8?q?module/river:=20add=20support=20for=20t?= =?UTF-8?q?he=20=E2=80=98mode=E2=80=99=20event?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Seat status v3 adds a new ‘mode’ event, that informs us of the current mode (as set by e.g. ‘riverctl enter-mode passthrough’) The mode is exposed as a tag (named “mode”) on river’s “title” particle: - river: title: map: default: {empty: {}} conditions: mode == passthrough: string: {text: " {mode} ", deco: {background: {color: ff0000ff}}} --- CHANGELOG.md | 4 ++++ doc/yambar-modules-river.5.scd | 18 ++++++++++++--- external/river-status-unstable-v1.xml | 12 ++++++++-- modules/river.c | 33 +++++++++++++++++++++++++-- 4 files changed, 60 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 67195a0..0562de4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,10 @@ * font-shaping: new inheritable configuration option, allowing you to configure whether strings should be _shaped_ using HarfBuzz, or not ([#159][159]). +* river: support for the new “mode” event present in version 3 of the + river status manager protocol, in the form of a new tag, _”mode”_, + in the `title` particle. + [153]: https://codeberg.org/dnkl/yambar/issues/153 [159]: https://codeberg.org/dnkl/yambar/issues/159 diff --git a/doc/yambar-modules-river.5.scd b/doc/yambar-modules-river.5.scd index 7ba2441..4b3c6d8 100644 --- a/doc/yambar-modules-river.5.scd +++ b/doc/yambar-modules-river.5.scd @@ -13,13 +13,14 @@ It has an interface similar to the i3/sway module. The configuration for the river module specifies one _title_ particle, which will be instantiated once for each seat, with tags representing -the seats' name and the title of the seats' currently focused view. +the seats' name, the title of the seats' currently focused view, and +its current river "mode". It also specifies a _content_ template particle, which is instantiated once for all 32 river tags. This means you probably want to use a *map* particle to hide unused river tags. -# TAGS +# TAGS (for the "content" particle) [[ *Name* :[ *Type* @@ -42,12 +43,23 @@ once for all 32 river tags. This means you probably want to use a | state : string : Set to *urgent* if _urgent_ is true, *focused* if _focused_ is true, *unfocused* if _visible_ is true, but _focused_ is false, or *invisible* if the river tag is not visible on any monitors. + + +# TAGS (for the "title" particle) + +[[ *Name* +:[ *Type* +:[ *Description* | seat : string : The name of the seat (*title* particle only, see CONFIGURATION) | title : string : The seat's focused view's title (*title* particle only, see CONFIGURATION) +| mode +: string +: The seat's current mode (entered with e.g. *riverctl enter-mode foobar*) + # CONFIGURATION @@ -76,7 +88,7 @@ once for all 32 river tags. This means you probably want to use a bar: left: - river: - title: {string: { text: "{seat} - {title}" }} + title: {string: { text: "{seat} - {title} ({mode})" }} content: map: conditions: diff --git a/external/river-status-unstable-v1.xml b/external/river-status-unstable-v1.xml index 13affaa..6a74256 100644 --- a/external/river-status-unstable-v1.xml +++ b/external/river-status-unstable-v1.xml @@ -16,7 +16,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - + A global factory for objects that receive status information specific to river. It could be used to implement, for example, a status bar. @@ -85,7 +85,7 @@ - + This interface allows clients to receive information about the current focus of a seat. Note that (un)focused_output events will only be sent @@ -121,5 +121,13 @@ + + + + Sent once on binding the interface and again whenever a new mode + is entered (e.g. with riverctl enter-mode foobar). + + + diff --git a/modules/river.c b/modules/river.c index f9854f2..23ac032 100644 --- a/modules/river.c +++ b/modules/river.c @@ -16,6 +16,8 @@ #include "river-status-unstable-v1.h" #include "xdg-output-unstable-v1.h" +#define min(x, y) ((x) < (y) ? (x) : (y)) + struct private; struct output { @@ -39,6 +41,7 @@ struct seat { uint32_t wl_name; char *name; + char *mode; char *title; struct output *output; }; @@ -158,8 +161,9 @@ content(struct module *mod) .tags = (struct tag *[]){ tag_new_string(mod, "seat", seat->name), tag_new_string(mod, "title", seat->title), + tag_new_string(mod, "mode", seat->mode), }, - .count = 2, + .count = 3, }; tag_parts[i++] = m->title->instantiate(m->title, &tags); @@ -199,6 +203,7 @@ seat_destroy(struct seat *seat) { free(seat->title); free(seat->name); + free(seat->mode); if (seat->status != NULL) zriver_seat_status_v1_destroy(seat->status); if (seat->wl_seat != NULL) @@ -435,10 +440,34 @@ focused_view(void *data, struct zriver_seat_status_v1 *zriver_seat_status_v1, } } +#if defined(ZRIVER_SEAT_STATUS_V1_MODE_SINCE_VERSION) +static void +mode(void *data, struct zriver_seat_status_v1 *zriver_seat_status_v1, + const char *name) +{ + struct seat *seat = data; + struct module *mod = seat->m->mod; + + mtx_lock(&mod->lock); + { + free(seat->mode); + seat->mode = strdup(name); + mtx_unlock(&mod->lock); + } + mod->bar->refresh(mod->bar); + + LOG_DBG("seat: %s, current mode: %s", seat->name, seat->mode); +} + +#endif + static const struct zriver_seat_status_v1_listener river_seat_status_listener = { .focused_output = &focused_output, .unfocused_output = &unfocused_output, .focused_view = &focused_view, +#if defined(ZRIVER_SEAT_STATUS_V1_MODE_SINCE_VERSION) + .mode = &mode, +#endif }; static void @@ -557,7 +586,7 @@ handle_global(void *data, struct wl_registry *registry, return; m->status_manager = wl_registry_bind( - registry, name, &zriver_status_manager_v1_interface, required); + registry, name, &zriver_status_manager_v1_interface, min(version, 3)); mtx_lock(&m->mod->lock); tll_foreach(m->outputs, it) From de4814d16e229e24cbe93b946b7dd19f1eba0322 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 2 Jun 2022 21:33:04 +0200 Subject: [PATCH 362/611] tag: use as_float() when kb/mb/gb formatting a float tag value --- CHANGELOG.md | 7 +++++-- tag.c | 5 ++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0562de4..dc20e43 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,9 @@ ### Changed * Minimum required meson version is now 0.58. +* Float tags are now treated as floats instead of integers when + formatted with the `kb`/`kib`/`mb`/`mib`/`gb`/`gib` string particle + formatters. * **BREAKING CHANGE**: overhaul of the `map` particle. Instead of specifying a `tag` and then an array of `values`, you must now simply use an array of `conditions`, that consist of: @@ -69,8 +72,8 @@ For a more thorough explanation, see the updated map section in the man page for yambar-particles([#137][137] and [#175][175]). - [137]: https://codeberg.org/dnkl/yambar/issues/137 - [175]: https://codeberg.org/dnkl/yambar/issues/172 +[137]: https://codeberg.org/dnkl/yambar/issues/137 +[175]: https://codeberg.org/dnkl/yambar/issues/172 ### Deprecated diff --git a/tag.c b/tag.c index 5c5684b..0428223 100644 --- a/tag.c +++ b/tag.c @@ -582,7 +582,10 @@ tags_expand_template(const char *template, const struct tag_set *tags) 1; char str[24]; - snprintf(str, sizeof(str), "%lu", tag->as_int(tag) / divider); + if (tag->type(tag) == TAG_TYPE_FLOAT) + snprintf(str, sizeof(str), "%f", tag->as_float(tag) / (double)divider); + else + snprintf(str, sizeof(str), "%lu", tag->as_int(tag) / divider); sbuf_append(&formatted, str); break; } From d9d5671e045c78d194ca1ba937d18daaa31c5e76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 2 Jun 2022 22:21:09 +0200 Subject: [PATCH 363/611] doc: tags: fix divisors for kb/mb/gb and kib/mib/gib They were inversed: kb/mb/gb uses 1000^n, while kib/mib/gib uses 1024^n. --- doc/yambar-tags.5.scd | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/yambar-tags.5.scd b/doc/yambar-tags.5.scd index 49a08ab..c4d5408 100644 --- a/doc/yambar-tags.5.scd +++ b/doc/yambar-tags.5.scd @@ -74,12 +74,12 @@ be used. : Range tags | kb, mb, gb : format -: Renders a tag's value (in decimal) divided by 1024, 1024^2 or - 1024^3. Note: no unit suffix is appended) +: Renders a tag's value (in decimal) divided by 1000, 1000^2 or + 1000^3. Note: no unit suffix is appended) : All tag types | kib, mib, gib : format -: Same as *kb*, *mb* and *gb*, but divide by 1000^n instead of 1024^n. +: Same as *kb*, *mb* and *gb*, but divide by 1024^n instead of 1000^n. : All tag types | min : selector From ca077447c23fee10032fe37ce5a5fc0930dca629 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 2 Jun 2022 22:25:53 +0200 Subject: [PATCH 364/611] module/network: tx/rx-bitrate is now in bits/s instead of Mb/s --- CHANGELOG.md | 3 +++ doc/yambar-modules-network.5.scd | 4 ++-- modules/network.c | 4 ++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dc20e43..87d0d2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,9 @@ * Float tags are now treated as floats instead of integers when formatted with the `kb`/`kib`/`mb`/`mib`/`gb`/`gib` string particle formatters. +* network: `tx-bitrate` and `rx-bitrate` are now in bits/s instead of + Mb/s. Use the `mb` string formatter to render these tags as before + (e.g. `string: {text: "{tx-bitrate:mb}"}`). * **BREAKING CHANGE**: overhaul of the `map` particle. Instead of specifying a `tag` and then an array of `values`, you must now simply use an array of `conditions`, that consist of: diff --git a/doc/yambar-modules-network.5.scd b/doc/yambar-modules-network.5.scd index 8ac46a1..6b5e835 100644 --- a/doc/yambar-modules-network.5.scd +++ b/doc/yambar-modules-network.5.scd @@ -47,10 +47,10 @@ address. : Signal strength, in dBm (Wi-Fi only) | rx-bitrate : int -: RX bitrate, in Mbit/s +: RX bitrate, in bits/s | tx-bitrate : int -: TX bitrate in Mbit/s +: TX bitrate in bits/s # CONFIGURATION diff --git a/modules/network.c b/modules/network.c index 34980fa..da3324d 100644 --- a/modules/network.c +++ b/modules/network.c @@ -143,8 +143,8 @@ content(struct module *mod) tag_new_string(mod, "ipv6", ipv6_str), tag_new_string(mod, "ssid", m->ssid), tag_new_int(mod, "signal", m->signal_strength_dbm), - tag_new_int(mod, "rx-bitrate", m->rx_bitrate / 1000 / 1000), - tag_new_int(mod, "tx-bitrate", m->tx_bitrate / 1000 / 1000), + tag_new_int(mod, "rx-bitrate", m->rx_bitrate), + tag_new_int(mod, "tx-bitrate", m->tx_bitrate), }, .count = 11, }; From f4ceaaad523854eb4c7876ad7fc6276eaa1b6ffe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 2 Jun 2022 21:27:06 +0200 Subject: [PATCH 365/611] module/sway-xkb: handle device being added again MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If a device is removed while the computer is hibernating, and then reconnected after waking it up, Sway sends an “added” event without first sending a “removed” event. Yambar used to assert that an “added” event didn’t refer to an already tracked device. This patch changes this, and simply ignores duplicate “added” events. Closes #177 --- modules/sway-xkb.c | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/modules/sway-xkb.c b/modules/sway-xkb.c index 6632ab7..6ee3e3a 100644 --- a/modules/sway-xkb.c +++ b/modules/sway-xkb.c @@ -207,21 +207,27 @@ handle_input_event(int type, const struct json_object *json, void *_mod) return true; } - if (is_removed || is_added) { - mtx_lock(&mod->lock); - assert((is_removed && input->exists) || - (is_added && !input->exists)); + if (is_removed) { + if (input->exists) { + mtx_lock(&mod->lock); + input->exists = false; + m->num_existing_inputs--; + m->dirty = true; + mtx_unlock(&mod->lock); + } + return true; + } - input->exists = !input->exists; - m->num_existing_inputs += is_added ? 1 : -1; - m->dirty = true; + if (is_added) { + if (!input->exists) { + mtx_lock(&mod->lock); + input->exists = true; + m->num_existing_inputs++; + m->dirty = true; + mtx_unlock(&mod->lock); + } - mtx_unlock(&mod->lock); - - if (is_removed) - return true; - - /* let is_added fall through, to update layout */ + /* “fallthrough”, to query current/active layout */ } /* Get current/active layout */ From 8a8a40bfb5ceed5a40f0e1db080734fec82eff32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 2 Jun 2022 22:30:09 +0200 Subject: [PATCH 366/611] =?UTF-8?q?changelog:=20sway-xkb:=20don=E2=80=99t?= =?UTF-8?q?=20crash=20on=20=E2=80=9Cadded=E2=80=9D=20event=20for=20already?= =?UTF-8?q?=20tracked=20device?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 87d0d2f..b27504a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -91,10 +91,13 @@ * cpu: don’t error out on systems where SMT has been disabled ([#172][172]). * examples/dwl-tags: updated parsing of `output` name ([#178][178]). +* sway-xkb: don’t crash when Sway sends an _”added”_ event for a + device yambar is already tracking ([#177][177]). [169]: https://codeberg.org/dnkl/yambar/issues/169 [172]: https://codeberg.org/dnkl/yambar/issues/172 [178]: https://codeberg.org/dnkl/yambar/issues/178 +[177]: https://codeberg.org/dnkl/yambar/issues/177 ### Security From a8994b9268fc3267a72cec9bf05a549f44b65c5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 3 Jun 2022 19:43:28 +0200 Subject: [PATCH 367/611] tag: for now, always use 2 decimals for floats MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the future, we’ll want to add a way to specify the number of decimals, as part of the formatter. --- tag.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tag.c b/tag.c index 0428223..fc09bdc 100644 --- a/tag.c +++ b/tag.c @@ -583,7 +583,7 @@ tags_expand_template(const char *template, const struct tag_set *tags) char str[24]; if (tag->type(tag) == TAG_TYPE_FLOAT) - snprintf(str, sizeof(str), "%f", tag->as_float(tag) / (double)divider); + snprintf(str, sizeof(str), "%.2f", tag->as_float(tag) / (double)divider); else snprintf(str, sizeof(str), "%lu", tag->as_int(tag) / divider); sbuf_append(&formatted, str); From 9353aa14fe8a2c9ba9fe77d9680cd67765148698 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 5 Jun 2022 00:33:30 +0200 Subject: [PATCH 368/611] =?UTF-8?q?bar:=20set=20clip=20region=20before=20c?= =?UTF-8?q?alling=20the=20particles=E2=80=99=20expose()=20methods?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This ensures particles that are “too wide” doesn’t try to render outside the bar, possibly overrunning both margins and borders. Or worse, crashes yambar. Closes #198 --- CHANGELOG.md | 3 +++ bar/bar.c | 12 ++++++++++++ 2 files changed, 15 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b27504a..88880fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -93,11 +93,14 @@ * examples/dwl-tags: updated parsing of `output` name ([#178][178]). * sway-xkb: don’t crash when Sway sends an _”added”_ event for a device yambar is already tracking ([#177][177]). +* Crash when a particle is “too wide”, and tries to render outside the + bar ([#198][198]). [169]: https://codeberg.org/dnkl/yambar/issues/169 [172]: https://codeberg.org/dnkl/yambar/issues/172 [178]: https://codeberg.org/dnkl/yambar/issues/178 [177]: https://codeberg.org/dnkl/yambar/issues/177 +[198]: https://codeberg.org/dnkl/yambar/issues/198 ### Security diff --git a/bar/bar.c b/bar/bar.c index 4f1895f..ab9a587 100644 --- a/bar/bar.c +++ b/bar/bar.c @@ -134,6 +134,18 @@ expose(const struct bar *_bar) int y = bar->border.top_width; int x = bar->border.left_width + bar->left_margin - bar->left_spacing; + pixman_region32_t clip; + pixman_region32_init_rect( + &clip, + bar->border.left_width + bar->left_margin, + bar->border.top_width, + (bar->width - + bar->left_margin - bar->right_margin - + bar->border.left_width - bar->border.right_width), + bar->height); + pixman_image_set_clip_region32(pix, &clip); + pixman_region32_fini(&clip); + for (size_t i = 0; i < bar->left.count; i++) { const struct exposable *e = bar->left.exps[i]; e->expose(e, pix, x + bar->left_spacing, y, bar->height); From eb5cb9869c0fe523eca1d137ee1af590cea7623a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 6 Jun 2022 14:30:22 +0200 Subject: [PATCH 369/611] module/network: log signal strength and TX/RX bitrates at debug level --- modules/network.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/network.c b/modules/network.c index da3324d..ef7366b 100644 --- a/modules/network.c +++ b/modules/network.c @@ -1062,10 +1062,10 @@ parse_genl_reply(struct module *mod, const struct nlmsghdr *hdr, size_t len) foreach_nlattr(mod, genl, msg_size, &handle_nl80211_new_station); } - LOG_INFO("%s: signal: %d dBm, RX=%u Mbit/s, TX=%u Mbit/s", - m->iface, m->signal_strength_dbm, - m->rx_bitrate / 1000 / 1000, - m->tx_bitrate / 1000 / 1000); + LOG_DBG("%s: signal: %d dBm, RX=%u Mbit/s, TX=%u Mbit/s", + m->iface, m->signal_strength_dbm, + m->rx_bitrate / 1000 / 1000, + m->tx_bitrate / 1000 / 1000); break; default: From 03e1c7dc13ade94e6116b71228f4533cb38ad766 Mon Sep 17 00:00:00 2001 From: Johannes Date: Sat, 30 Apr 2022 05:08:05 +0200 Subject: [PATCH 370/611] module/network: Add link stats Exports two new tags from network module, `ul-speed` and `dl-speed`. Because these work through polling, poll-interval must be set. Otherwise, these two tags always will be 0. --- CHANGELOG.md | 2 + doc/yambar-modules-network.5.scd | 9 +++- examples/configurations/laptop.conf | 6 ++- modules/network.c | 68 ++++++++++++++++++++++++++++- 4 files changed, 81 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 88880fd..5a1b61b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,8 @@ * river: support for the new “mode” event present in version 3 of the river status manager protocol, in the form of a new tag, _”mode”_, in the `title` particle. +* network: request link stats and expose under tags `dl-speed` and + `ul-speed` when `poll-interval` is set. [153]: https://codeberg.org/dnkl/yambar/issues/153 diff --git a/doc/yambar-modules-network.5.scd b/doc/yambar-modules-network.5.scd index 6b5e835..d8129ef 100644 --- a/doc/yambar-modules-network.5.scd +++ b/doc/yambar-modules-network.5.scd @@ -51,6 +51,12 @@ address. | tx-bitrate : int : TX bitrate in bits/s +| dl-speed +: int +: Download speed in bits/s +| ul-speed +: int +: Upload speed in bits/s # CONFIGURATION @@ -66,7 +72,8 @@ address. | poll-interval : int : no -: Periodically (in seconds) update the signal and rx+tx bitrate tags. +: Periodically (in seconds) update the signal, rx+tx bitrate, and + ul+dl speed tags. # EXAMPLES diff --git a/examples/configurations/laptop.conf b/examples/configurations/laptop.conf index 573516c..c02ba4a 100644 --- a/examples/configurations/laptop.conf +++ b/examples/configurations/laptop.conf @@ -185,6 +185,7 @@ bar: ipv4 == "": {string: {text: , font: *awesome, foreground: ffffff66}} - network: name: wlp2s0 + poll-interval: 1 content: map: default: {string: {text: , font: *awesome, foreground: ffffff66}} @@ -194,11 +195,12 @@ bar: map: default: - string: {text: , font: *awesome} - - string: {text: "{ssid}"} + - string: {text: "{ssid} {dl-speed:mb}/{ul-speed:mb} Mb/s"} + conditions: ipv4 == "": - string: {text: , font: *awesome, foreground: ffffff66} - - string: {text: "{ssid}", foreground: ffffff66} + - string: {text: "{ssid} {dl-speed:mb}/{ul-speed:mb} Mb/s", foreground: ffffff66} - alsa: card: hw:PCH mixer: Master diff --git a/modules/network.c b/modules/network.c index ef7366b..9ec5993 100644 --- a/modules/network.c +++ b/modules/network.c @@ -31,6 +31,11 @@ #define UNUSED __attribute__((unused)) +struct rt_stats_msg { + struct rtmsg rth; + struct rtnl_link_stats64 stats; +}; + struct af_addr { int family; union { @@ -68,6 +73,12 @@ struct private { int signal_strength_dbm; uint32_t rx_bitrate; uint32_t tx_bitrate; + + uint64_t ul_speed; + uint64_t ul_bits; + + uint64_t dl_speed; + uint64_t dl_bits; }; static void @@ -145,8 +156,10 @@ content(struct module *mod) tag_new_int(mod, "signal", m->signal_strength_dbm), tag_new_int(mod, "rx-bitrate", m->rx_bitrate), tag_new_int(mod, "tx-bitrate", m->tx_bitrate), + tag_new_float(mod, "dl-speed", m->dl_speed), + tag_new_float(mod, "ul-speed", m->ul_speed), }, - .count = 11, + .count = 13, }; mtx_unlock(&mod->lock); @@ -252,6 +265,36 @@ send_rt_request(struct private *m, int request) return true; } +static bool +send_rt_getstats_request(struct private *m) +{ + struct { + struct nlmsghdr hdr; + struct if_stats_msg rt; + } req = { + .hdr = { + .nlmsg_len = NLMSG_LENGTH(sizeof(req.rt)), + .nlmsg_type = RTM_GETSTATS, + .nlmsg_flags = NLM_F_REQUEST, + .nlmsg_seq = 1, + .nlmsg_pid = nl_pid_value(), + }, + + .rt = { + .ifindex = m->ifindex, + .filter_mask = IFLA_STATS_LINK_64, + .family = AF_UNSPEC, + }, + }; + + if (!send_nlmsg(m->rt_sock, &req, req.hdr.nlmsg_len)) { + LOG_ERRNO("%s: failed to send netlink RT getstats request (%d)", + m->iface, RTM_GETSTATS); + return false; + } + return true; +} + static bool send_ctrl_get_family_request(struct private *m) { @@ -929,6 +972,23 @@ netlink_receive_messages(int sock, void **reply, size_t *len) return true; } +static void +handle_stats(struct module *mod, struct rt_stats_msg *msg) +{ + struct private *m = mod->private; + uint64_t ul_bits = msg->stats.tx_bytes*8; + uint64_t dl_bits = msg->stats.rx_bytes*8; + + if (m->ul_bits != 0) { + m->ul_speed = (ul_bits - m->ul_bits) / m->poll_interval; + } + if (m->dl_bits != 0) { + m->dl_speed = (dl_bits - m->dl_bits) / m->poll_interval; + } + m->ul_bits = ul_bits; + m->dl_bits = dl_bits; +} + static bool parse_rt_reply(struct module *mod, const struct nlmsghdr *hdr, size_t len) { @@ -967,6 +1027,11 @@ parse_rt_reply(struct module *mod, const struct nlmsghdr *hdr, size_t len) handle_address(mod, hdr->nlmsg_type, msg, msg_len); break; } + case RTM_NEWSTATS: { + struct rt_stats_msg *msg = NLMSG_DATA(hdr); + handle_stats(mod, msg); + break; + } case NLMSG_ERROR:{ const struct nlmsgerr *err = NLMSG_DATA(hdr); @@ -1200,6 +1265,7 @@ run(struct module *mod) } send_nl80211_get_station(m); + send_rt_getstats_request(m); } } From b0e132beaffaed26df2034566c1f77b56e24cbae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 2 Jun 2022 21:26:39 +0200 Subject: [PATCH 371/611] =?UTF-8?q?module/i3:=20if=20a=20new=20workspace?= =?UTF-8?q?=20is=20created,=20but=20unfocused,=20assume=20it=E2=80=99s=20n?= =?UTF-8?q?ot=20empty?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If a window is created on an unfocused workspace, yambar did not update the empty tag correctly. At least not for persistent workspaces. This is because yambar relies on focus events to determine a workspace's "empty" state. Since the new window, on the new workspace, isn't focused, there's no focus event, and yambar thinks the workspace is still empty. This patch changes the logic slightly; a new workspace is considered non-empty if it isn't focused (and has a non-zero node count). Closes #191 --- modules/i3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/i3.c b/modules/i3.c index 22b6d4b..e036f97 100644 --- a/modules/i3.c +++ b/modules/i3.c @@ -139,7 +139,7 @@ workspace_from_json(const struct json_object *json, struct workspace *ws) .visible = json_object_get_boolean(visible), .focused = json_object_get_boolean(focused), .urgent = json_object_get_boolean(urgent), - .empty = is_empty, + .empty = is_empty && json_object_get_boolean(focused), .window = {.title = NULL, .pid = -1}, }; From 36a4250a96df33e2029396c9abefa8d246f44136 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 2 Jun 2022 22:32:53 +0200 Subject: [PATCH 372/611] changelog: i3: newly created, unfocused, workspaces are considered non-empty --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a1b61b..9286a76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,8 @@ * network: `tx-bitrate` and `rx-bitrate` are now in bits/s instead of Mb/s. Use the `mb` string formatter to render these tags as before (e.g. `string: {text: "{tx-bitrate:mb}"}`). +* i3: newly created, and **unfocused** workspaces are now considered + non-empty ([#191][191]) * **BREAKING CHANGE**: overhaul of the `map` particle. Instead of specifying a `tag` and then an array of `values`, you must now simply use an array of `conditions`, that consist of: @@ -79,6 +81,7 @@ [137]: https://codeberg.org/dnkl/yambar/issues/137 [175]: https://codeberg.org/dnkl/yambar/issues/172 +[191]: https://codeberg.org/dnkl/yambar/issues/191 ### Deprecated From 1b03bd6bc0adb8d7625b6ed8c07f2aba12f3af32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 13 Jun 2022 12:06:59 +0200 Subject: [PATCH 373/611] particle/string: simplify; no need to call c32len(wtext) twice --- particles/string.c | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/particles/string.c b/particles/string.c index f1e1faf..190a786 100644 --- a/particles/string.c +++ b/particles/string.c @@ -179,24 +179,20 @@ instantiate(const struct particle *particle, const struct tag_set *tags) size_t chars = c32len(wtext); /* Truncate, if necessary */ - if (p->max_len > 0) { - const size_t len = c32len(wtext); - if (len > p->max_len) { + if (p->max_len > 0 && chars > p->max_len) { + size_t end = p->max_len; + if (end >= 1) { + /* "allocate" room for three dots at the end */ + end -= 1; + } - size_t end = p->max_len; - if (end >= 1) { - /* "allocate" room for three dots at the end */ - end -= 1; - } - - if (p->max_len > 1) { - wtext[end] = U'…'; - wtext[end + 1] = U'\0'; - chars = end + 1; - } else { - wtext[end] = U'\0'; - chars = 0; - } + if (p->max_len > 1) { + wtext[end] = U'…'; + wtext[end + 1] = U'\0'; + chars = end + 1; + } else { + wtext[end] = U'\0'; + chars = 0; } } From 605a6a9edeebccd0edf2e1de109449f52b52a9bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 13 Jun 2022 12:07:24 +0200 Subject: [PATCH 374/611] =?UTF-8?q?particle/string:=20don=E2=80=99t=20try?= =?UTF-8?q?=20to=20call=20c32len()=20on=20a=20NULL=20string?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 2 ++ particles/string.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9286a76..d72d692 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -100,6 +100,8 @@ device yambar is already tracking ([#177][177]). * Crash when a particle is “too wide”, and tries to render outside the bar ([#198][198]). +* string: crash when failing to convert string to UTF-32. + [169]: https://codeberg.org/dnkl/yambar/issues/169 [172]: https://codeberg.org/dnkl/yambar/issues/172 diff --git a/particles/string.c b/particles/string.c index 190a786..e679600 100644 --- a/particles/string.c +++ b/particles/string.c @@ -176,7 +176,7 @@ instantiate(const struct particle *particle, const struct tag_set *tags) /* Not in cache - we need to rasterize it. First, convert to char32_t */ wtext = ambstoc32(text); - size_t chars = c32len(wtext); + size_t chars = wtext != NULL ? c32len(wtext) : 0; /* Truncate, if necessary */ if (p->max_len > 0 && chars > p->max_len) { From cb47c53de452b7c50aa63a1d4cafa72902af028a Mon Sep 17 00:00:00 2001 From: hiog Date: Thu, 16 Jun 2022 11:29:04 +0200 Subject: [PATCH 375/611] Update 'examples/configurations/laptop.conf' --- examples/configurations/laptop.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/configurations/laptop.conf b/examples/configurations/laptop.conf index c02ba4a..cb90111 100644 --- a/examples/configurations/laptop.conf +++ b/examples/configurations/laptop.conf @@ -282,6 +282,6 @@ bar: - label: content: string: - on-click: loginctl poweroff + on-click: systemctl poweroff text:  font: *awesome From a0c07d7836b05fe5b88d6cb9b926a8ca9d38beab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Gibrowski=20Fa=C3=A9?= Date: Wed, 27 Apr 2022 21:55:34 -0300 Subject: [PATCH 376/611] modules: creates disk-io-module This creates the disk-io-module, which displays io information read from `/proc/diskstats`. Details on `diskstats` can be found on: https://www.kernel.org/doc/Documentation/ABI/testing/procfs-diskstats --- CHANGELOG.md | 1 + doc/meson.build | 1 + doc/yambar-modules-disk-io.5.scd | 85 ++++++++ modules/disk-io.c | 355 +++++++++++++++++++++++++++++++ modules/meson.build | 1 + plugin.c | 2 + 6 files changed, 445 insertions(+) create mode 100644 doc/yambar-modules-disk-io.5.scd create mode 100644 modules/disk-io.c diff --git a/CHANGELOG.md b/CHANGELOG.md index d72d692..7d72922 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ in the `title` particle. * network: request link stats and expose under tags `dl-speed` and `ul-speed` when `poll-interval` is set. +* new module: disk-io. [153]: https://codeberg.org/dnkl/yambar/issues/153 diff --git a/doc/meson.build b/doc/meson.build index e882f52..81188bf 100644 --- a/doc/meson.build +++ b/doc/meson.build @@ -6,6 +6,7 @@ scdoc_prog = find_program(scdoc.get_variable('scdoc'), native: true) foreach man_src : ['yambar.1.scd', 'yambar.5.scd', 'yambar-decorations.5.scd', 'yambar-modules-alsa.5.scd', 'yambar-modules-backlight.5.scd', 'yambar-modules-battery.5.scd', 'yambar-modules-clock.5.scd', + 'yambar-modules-disk-io.5.scd', 'yambar-modules-foreign-toplevel.5.scd', 'yambar-modules-i3.5.scd', 'yambar-modules-label.5.scd', 'yambar-modules-mpd.5.scd', 'yambar-modules-network.5.scd', diff --git a/doc/yambar-modules-disk-io.5.scd b/doc/yambar-modules-disk-io.5.scd new file mode 100644 index 0000000..92ce449 --- /dev/null +++ b/doc/yambar-modules-disk-io.5.scd @@ -0,0 +1,85 @@ +yambar-modules-disk-io(5) + +# NAME +disk-io - This module keeps track of the amount of bytes being +read/written from/to disk. It can distinguish between all partitions +currently present in the machine. + +# TAGS + +[[ *Name* +:[ *Type* +:[ *Description* +| device +: string +: Name of the device being tracked (use the command *lsblk* to see these). + There is a special device, "Total", that reports the total stats + for the machine +| is_disk +: boolean +: whether or not the device is a disk (e.g. sda, sdb) or a partition + (e.g. sda1, sda2, ...). "Total" is advertised as a disk. +| read_speed +: int +: bytes read, in bytes/s +| write_speed +: int +: bytes written, in bytes/s +| ios_in_progress +: int +: number of ios that are happening at the time of polling + +# CONFIGURATION + +[[ *Name* +:[ *Type* +:[ *Req* +:[ *Description* +| interval +: int +: no +: Refresh interval of disk's stats in ms (default=500). + Cannot be less then 500 ms + +# EXAMPLES + +This reports the total amount of bytes being read and written every second, +formatting in b/s, kb/s, mb/s, or gb/s, as appropriate. + +``` +bar: + left: + - disk-io: + interval: 1000 + content: + map: + conditions: + device == Total: + list: + items: + - string: {text: "Total read: "} + - map: + default: {string: {text: "{read_speed} B/s"}} + conditions: + read_speed > 1073741824: + string: {text: "{read_speed:gib} GB/s"} + read_speed > 1048576: + string: {text: "{read_speed:mib} MB/s"} + read_speed > 1024: + string: {text: "{read_speed:kib} KB/s"} + - string: {text: " | "} + - string: {text: "Total written: "} + - map: + default: {string: {text: "{write_speed} B/s"}} + conditions: + write_speed > 1073741824: + string: {text: "{write_speed:gib} GB/s"} + write_speed > 1048576: + string: {text: "{write_speed:mib} MB/s"} + write_speed > 1024: + string: {text: "{write_speed:kib} KB/s"} +``` + +# SEE ALSO + +*yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5) diff --git a/modules/disk-io.c b/modules/disk-io.c new file mode 100644 index 0000000..ee6da8a --- /dev/null +++ b/modules/disk-io.c @@ -0,0 +1,355 @@ +#include +#include +#include +#include +#include +#include + +#include + +#include "../particles/dynlist.h" +#include "../bar/bar.h" +#include "../config-verify.h" +#include "../config.h" +#include "../log.h" +#include "../plugin.h" + +#define LOG_MODULE "disk-io" +#define LOG_ENABLE_DBG 0 +#define SMALLEST_INTERVAL 500 + +struct device_stats { + char *name; + bool is_disk; + + uint64_t prev_sectors_read; + uint64_t cur_sectors_read; + + uint64_t prev_sectors_written; + uint64_t cur_sectors_written; + + uint32_t ios_in_progress; + + bool exists; +}; + +struct private { + struct particle *label; + uint16_t interval; + tll(struct device_stats *) devices; +}; + +static bool +is_disk(char const *name) +{ + DIR *dir = opendir("/sys/block"); + if (dir == NULL) { + LOG_ERRNO("failed to read /sys/block directory"); + return false; + } + + struct dirent *entry; + + bool found = false; + while ((entry = readdir(dir)) != NULL) { + if (strcmp(name, entry->d_name) == 0) { + found = true; + break; + } + } + + closedir(dir); + return found; +} + +static struct device_stats* +new_device_stats(char const *name) +{ + struct device_stats *dev = malloc(sizeof(*dev)); + dev->name = strdup(name); + dev->is_disk = is_disk(name); + return dev; +} + +static void +free_device_stats(struct device_stats *dev) +{ + free(dev->name); + free(dev); +} + +static void +destroy(struct module *mod) +{ + struct private *m = mod->private; + m->label->destroy(m->label); + tll_foreach(m->devices, it) { + free_device_stats(it->item); + } + tll_free(m->devices); + free(m); + module_default_destroy(mod); +} + +static const char * +description(struct module *mod) +{ + return "disk-io"; +} + +static void +refresh_device_stats(struct private *m) +{ + FILE *fp = NULL; + char *line = NULL; + size_t len = 0; + ssize_t read; + + fp = fopen("/proc/diskstats", "r"); + if (NULL == fp) { + LOG_ERRNO("unable to open /proc/diskstats"); + return; + } + + /* + * Devices may be added or removed during the bar's lifetime, as external + * block devices are connected or disconnected from the machine. /proc/diskstats + * reports data only for the devices that are currently connected. + * + * This means that if we have a device that ISN'T in /proc/diskstats, it was + * disconnected, and we need to remove it from the list. + * + * On the other hand, if a device IS in /proc/diskstats, but not in our list, we + * must create a new device_stats struct and add it to the list. + * + * The 'exists' variable is what keep tracks of whether or not /proc/diskstats + * is still reporting the device (i.e., it is still connected). + */ + tll_foreach(m->devices, it) { + it->item->exists = false; + } + + while ((read = getline(&line, &len, fp)) != -1) { + /* + * For an explanation of the fields bellow, see + * https://www.kernel.org/doc/Documentation/ABI/testing/procfs-diskstats + */ + uint8_t major_number = 0; + uint8_t minor_number = 0; + char *device_name = NULL; + uint32_t completed_reads = 0; + uint32_t merged_reads = 0; + uint64_t sectors_read = 0; + uint32_t reading_time = 0; + uint32_t completed_writes = 0; + uint32_t merged_writes = 0; + uint64_t sectors_written = 0; + uint32_t writting_time = 0; + uint32_t ios_in_progress = 0; + uint32_t io_time = 0; + uint32_t io_weighted_time = 0; + uint32_t completed_discards = 0; + uint32_t merged_discards = 0; + uint32_t sectors_discarded = 0; + uint32_t discarding_time = 0; + uint32_t completed_flushes = 0; + uint32_t flushing_time = 0; + if (!sscanf(line, + " %" SCNu8 " %" SCNu8 " %ms %" SCNu32 " %" SCNu32 " %" SCNu64 " %" SCNu32 + " %" SCNu32 " %" SCNu32 " %" SCNu64 " %" SCNu32 " %" SCNu32 " %" SCNu32 + " %" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32 + " %" SCNu32, + &major_number, &minor_number, &device_name, &completed_reads, + &merged_reads, §ors_read, &reading_time, &completed_writes, + &merged_writes, §ors_written, &writting_time, &ios_in_progress, + &io_time, &io_weighted_time, &completed_discards, &merged_discards, + §ors_discarded, &discarding_time, &completed_flushes, &flushing_time)) + { + LOG_ERR("unable to parse /proc/diskstats line"); + free(device_name); + goto exit; + } + + bool found = false; + tll_foreach(m->devices, it) { + struct device_stats *dev = it->item; + if (strcmp(dev->name, device_name) == 0){ + dev->prev_sectors_read = dev->cur_sectors_read; + dev->prev_sectors_written = dev->cur_sectors_written; + dev->ios_in_progress = ios_in_progress; + dev->cur_sectors_read = sectors_read; + dev->cur_sectors_written = sectors_written; + dev->exists = true; + found = true; + break; + } + } + + if (!found) { + struct device_stats *new_dev = new_device_stats(device_name); + new_dev->ios_in_progress = ios_in_progress; + new_dev->prev_sectors_read = sectors_read; + new_dev->cur_sectors_read = sectors_read; + new_dev->prev_sectors_written = sectors_written; + new_dev->cur_sectors_written = sectors_written; + new_dev->exists = true; + tll_push_back(m->devices, new_dev); + } + + free(device_name); + } + + tll_foreach(m->devices, it) { + if (!it->item->exists){ + free_device_stats(it->item); + tll_remove(m->devices, it); + } + } +exit: + fclose(fp); + free(line); +} + +static struct exposable * +content(struct module *mod) +{ + const struct private *p = mod->private; + uint64_t total_bytes_read = 0; + uint64_t total_bytes_written = 0; + uint32_t total_ios_in_progress = 0; + mtx_lock(&mod->lock); + struct exposable *tag_parts[p->devices.length + 1]; + int i = 0; + tll_foreach(p->devices, it) { + struct device_stats *dev = it->item; + uint64_t bytes_read = (dev->cur_sectors_read - dev->prev_sectors_read) * 512; + uint64_t bytes_written = (dev->cur_sectors_written - dev->prev_sectors_written) * 512; + + if (dev->is_disk){ + total_bytes_read += bytes_read; + total_bytes_written += bytes_written; + total_ios_in_progress += dev->ios_in_progress; + } + + struct tag_set tags = { + .tags = (struct tag *[]) { + tag_new_string(mod, "device", dev->name), + tag_new_bool(mod, "is_disk", dev->is_disk), + tag_new_int(mod, "read_speed", (bytes_read * 1000) / p->interval), + tag_new_int(mod, "write_speed", (bytes_written * 1000) / p->interval), + tag_new_int(mod, "ios_in_progress", dev->ios_in_progress), + }, + .count = 5, + }; + tag_parts[i++] = p->label->instantiate(p->label, &tags); + tag_set_destroy(&tags); + } + struct tag_set tags = { + .tags = (struct tag *[]) { + tag_new_string(mod, "device", "Total"), + tag_new_bool(mod, "is_disk", true), + tag_new_int(mod, "read_speed", (total_bytes_read * 1000) / p->interval), + tag_new_int(mod, "write_speed", (total_bytes_written * 1000) / p->interval), + tag_new_int(mod, "ios_in_progress", total_ios_in_progress), + }, + .count = 5, + }; + tag_parts[i] = p->label->instantiate(p->label, &tags); + tag_set_destroy(&tags); + mtx_unlock(&mod->lock); + + return dynlist_exposable_new(tag_parts, p->devices.length + 1, 0, 0); +} + +static int +run(struct module *mod) +{ + const struct bar *bar = mod->bar; + bar->refresh(bar); + struct private *p = mod->private; + while (true) { + struct pollfd fds[] = {{.fd = mod->abort_fd, .events = POLLIN}}; + + int res = poll(fds, sizeof(fds) / sizeof(*fds), p->interval); + + if (res < 0) { + if (EINTR == errno) + continue; + LOG_ERRNO("unable to poll abort fd"); + return -1; + } + + if (fds[0].revents & POLLIN) + break; + + mtx_lock(&mod->lock); + refresh_device_stats(p); + mtx_unlock(&mod->lock); + bar->refresh(bar); + } + + return 0; +} + +static struct module * +disk_io_new(uint16_t interval, struct particle *label) +{ + struct private *p = calloc(1, sizeof(*p)); + p->label = label; + p->interval = interval; + + struct module *mod = module_common_new(); + mod->private = p; + mod->run = &run; + mod->destroy = &destroy; + mod->content = &content; + mod->description = &description; + return mod; +} + +static struct module * +from_conf(const struct yml_node *node, struct conf_inherit inherited) +{ + const struct yml_node *interval = yml_get_value(node, "interval"); + const struct yml_node *c = yml_get_value(node, "content"); + + return disk_io_new( + interval == NULL ? SMALLEST_INTERVAL : yml_value_as_int(interval), + conf_to_particle(c, inherited)); +} + +static bool +conf_verify_interval(keychain_t *chain, const struct yml_node *node) +{ + if (!conf_verify_unsigned(chain, node)) + return false; + + if (yml_value_as_int(node) < SMALLEST_INTERVAL) { + LOG_ERR( + "%s: interval value cannot be less than %d ms", + conf_err_prefix(chain, node), SMALLEST_INTERVAL); + return false; + } + + return true; +} + +static bool +verify_conf(keychain_t *chain, const struct yml_node *node) +{ + static const struct attr_info attrs[] = { + {"interval", false, &conf_verify_interval}, + MODULE_COMMON_ATTRS, + }; + + return conf_verify_dict(chain, node, attrs); +} + +const struct module_iface module_disk_io_iface = { + .verify_conf = &verify_conf, + .from_conf = &from_conf, +}; + +#if defined(CORE_PLUGINS_AS_SHARED_LIBRARIES) +extern const struct module_iface iface __attribute__((weak, alias("module_disk_io_iface"))); +#endif diff --git a/modules/meson.build b/modules/meson.build index 192b094..ada5ab6 100644 --- a/modules/meson.build +++ b/modules/meson.build @@ -18,6 +18,7 @@ mod_data = { 'battery': [[], [udev]], 'clock': [[], []], 'cpu': [[], []], + 'disk-io': [[], [dynlist]], 'mem': [[], []], 'i3': [['i3-common.c', 'i3-common.h'], [dynlist, json]], 'label': [[], []], diff --git a/plugin.c b/plugin.c index 43e8672..06304d8 100644 --- a/plugin.c +++ b/plugin.c @@ -36,6 +36,7 @@ EXTERN_MODULE(alsa); EXTERN_MODULE(backlight); EXTERN_MODULE(battery); EXTERN_MODULE(clock); +EXTERN_MODULE(disk_io); EXTERN_MODULE(foreign_toplevel); EXTERN_MODULE(i3); EXTERN_MODULE(label); @@ -118,6 +119,7 @@ init(void) REGISTER_CORE_MODULE(backlight, backlight); REGISTER_CORE_MODULE(battery, battery); REGISTER_CORE_MODULE(clock, clock); + REGISTER_CORE_MODULE(disk-io, disk_io); #if defined(HAVE_PLUGIN_foreign_toplevel) REGISTER_CORE_MODULE(foreign-toplevel, foreign_toplevel); #endif From 6c10eb2153a14ad12ee5d49c52669d4d0bc13466 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 16 Jun 2022 18:24:42 +0200 Subject: [PATCH 377/611] =?UTF-8?q?module/alsa:=20use=20channel=E2=80=99s?= =?UTF-8?q?=20dB=20range=20instead=20of=20raw=20volume,=20if=20available?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For channels that have a defined dB range, use that instead of the raw volume range when calculating the volume percent. Also use the same logic as alsamixer when calculating the percent from the dB values: assume a linear scale if the dB range is “small enough”, and otherwise normalize it against a logarithmic scale. With this, yambar’s “percent” value matches alsamixer’s exactly. The ‘volume’ tag remains unchanged - it always reflects the raw volume values. Instead, we add a new tag ‘dB’, that reflects the dB values. Closes #202 --- CHANGELOG.md | 6 +- doc/yambar-modules-alsa.5.scd | 9 +- examples/configurations/laptop.conf | 2 +- modules/alsa.c | 144 ++++++++++++++++++++++++---- 4 files changed, 141 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d72922..edaa31e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,10 +25,11 @@ * network: request link stats and expose under tags `dl-speed` and `ul-speed` when `poll-interval` is set. * new module: disk-io. - +* alsa: `dB` tag ([#202][202]) [153]: https://codeberg.org/dnkl/yambar/issues/153 [159]: https://codeberg.org/dnkl/yambar/issues/159 +[202]: https://codeberg.org/dnkl/yambar/issues/202 ### Changed @@ -42,6 +43,8 @@ (e.g. `string: {text: "{tx-bitrate:mb}"}`). * i3: newly created, and **unfocused** workspaces are now considered non-empty ([#191][191]) +* alsa: use dB instead of raw volume values, if possible, when + calculating the `percent` tag ([#202][202]) * **BREAKING CHANGE**: overhaul of the `map` particle. Instead of specifying a `tag` and then an array of `values`, you must now simply use an array of `conditions`, that consist of: @@ -83,6 +86,7 @@ [137]: https://codeberg.org/dnkl/yambar/issues/137 [175]: https://codeberg.org/dnkl/yambar/issues/172 [191]: https://codeberg.org/dnkl/yambar/issues/191 +[202]: https://codeberg.org/dnkl/yambar/issues/202 ### Deprecated diff --git a/doc/yambar-modules-alsa.5.scd b/doc/yambar-modules-alsa.5.scd index 58a4343..17f1a29 100644 --- a/doc/yambar-modules-alsa.5.scd +++ b/doc/yambar-modules-alsa.5.scd @@ -11,12 +11,17 @@ alsa - Monitors an alsa soundcard for volume and mute/unmute changes | online : bool : True when the ALSA device has successfully been opened +| dB +: range +: Volume level (in dB), with min and max as start and end range + values. | volume : range -: Volume level, with min and max as start and end range values +: Volume level (raw), with min and max as start and end range values | percent : range -: Volume level, as a percentage +: Volume level, as a percentage. This value is based on the *dB* tag + if available, otherwise the *volume* tag. | muted : bool : True if muted, otherwise false diff --git a/examples/configurations/laptop.conf b/examples/configurations/laptop.conf index cb90111..85f43ad 100644 --- a/examples/configurations/laptop.conf +++ b/examples/configurations/laptop.conf @@ -215,7 +215,7 @@ bar: muted: {string: {text: , font: *awesome, foreground: ffffff66}} ~muted: ramp: - tag: volume + tag: percent items: - string: {text: , font: *awesome} - string: {text: , font: *awesome} diff --git a/modules/alsa.c b/modules/alsa.c index f22b340..b3aaba8 100644 --- a/modules/alsa.c +++ b/modules/alsa.c @@ -23,7 +23,9 @@ struct channel { enum channel_type type; char *name; + bool use_db; long vol_cur; + long db_cur; bool muted; }; @@ -42,10 +44,18 @@ struct private { long playback_vol_min; long playback_vol_max; + bool has_playback_db; + long playback_db_min; + long playback_db_max; + bool has_capture_volume; long capture_vol_min; long capture_vol_max; + long has_capture_db; + long capture_db_min; + long capture_db_max; + const struct channel *volume_chan; const struct channel *muted_chan; }; @@ -94,30 +104,57 @@ content(struct module *mod) bool muted = muted_chan != NULL ? muted_chan->muted : false; long vol_min = 0, vol_max = 0, vol_cur = 0; + long db_min = 0, db_max = 0, db_cur = 0; + bool use_db = false; if (volume_chan != NULL) { if (volume_chan->type == CHANNEL_PLAYBACK) { + db_min = m->playback_db_min; + db_max = m->playback_db_max; vol_min = m->playback_vol_min; vol_max = m->playback_vol_max; } else { + db_min = m->capture_db_min; + db_max = m->capture_db_max; vol_min = m->capture_vol_min; vol_max = m->capture_vol_max; } vol_cur = volume_chan->vol_cur; + db_cur = volume_chan->db_cur; + use_db = volume_chan->use_db; } - int percent = vol_max - vol_min > 0 - ? round(100. * vol_cur / (vol_max - vol_min)) - : 0; + int percent; + + if (use_db) { + bool use_linear = db_max - db_min <= 24 * 100; + if (use_linear) { + percent = db_min - db_max > 0 + ? round(100. * (db_cur - db_min) / (db_max - db_min)) + : 0; + } else { + double normalized = pow(10, (double)(db_cur - db_max) / 6000.); + if (db_min != SND_CTL_TLV_DB_GAIN_MUTE) { + double min_norm = pow(10, (double)(db_min - db_max) / 6000.); + normalized = (normalized - min_norm) / (1. - min_norm); + } + percent = round(100. * normalized); + } + } else { + percent = vol_max - vol_min > 0 + ? round(100. * (vol_cur - vol_min) / (vol_max - vol_min)) + : 0; + } struct tag_set tags = { .tags = (struct tag *[]){ tag_new_bool(mod, "online", m->online), tag_new_int_range(mod, "volume", vol_cur, vol_min, vol_max), + tag_new_int_range(mod, "dB", db_cur, db_min, db_max), tag_new_int_range(mod, "percent", percent, 0, 100), tag_new_bool(mod, "muted", muted), }, - .count = 4, + .count = 5, }; mtx_unlock(&mod->lock); @@ -132,6 +169,8 @@ update_state(struct module *mod, snd_mixer_elem_t *elem) { struct private *m = mod->private; + mtx_lock(&mod->lock); + /* If volume level can be changed (i.e. this isn't just a switch; * e.g. a digital channel), get current channel levels */ tll_foreach(m->channels, it) { @@ -139,14 +178,57 @@ update_state(struct module *mod, snd_mixer_elem_t *elem) const bool has_volume = chan->type == CHANNEL_PLAYBACK ? m->has_playback_volume : m->has_capture_volume; + const bool has_db = chan->type == CHANNEL_PLAYBACK + ? m->has_playback_db : m->has_capture_db; + + if (!has_volume && !has_db) + continue; + + + if (has_db) { + chan->use_db = true; + + const long min = chan->type == CHANNEL_PLAYBACK + ? m->playback_db_min : m->capture_db_min; + const long max = chan->type == CHANNEL_PLAYBACK + ? m->playback_db_max : m->capture_db_max; + assert(min <= max); + + int r = chan->type == CHANNEL_PLAYBACK + ? snd_mixer_selem_get_playback_dB(elem, chan->id, &chan->db_cur) + : snd_mixer_selem_get_capture_dB(elem, chan->id, &chan->db_cur); + + if (r < 0) { + LOG_ERR("%s,%s: %s: failed to get current dB", + m->card, m->mixer, chan->name); + } + + if (chan->db_cur < min) { + LOG_WARN( + "%s,%s: %s: current dB is less than the indicated minimum: " + "%ld < %ld", m->card, m->mixer, chan->name, chan->db_cur, min); + chan->db_cur = min; + } + + if (chan->db_cur > max) { + LOG_WARN( + "%s,%s: %s: current dB is greater than the indicated maximum: " + "%ld > %ld", m->card, m->mixer, chan->name, chan->db_cur, max); + chan->db_cur = max; + } + + assert(chan->db_cur >= min); + assert(chan->db_cur <= max ); + + LOG_DBG("%s,%s: %s: dB: %ld", + m->card, m->mixer, chan->name, chan->db_cur); + } else + chan->use_db = false; + const long min = chan->type == CHANNEL_PLAYBACK ? m->playback_vol_min : m->capture_vol_min; const long max = chan->type == CHANNEL_PLAYBACK ? m->playback_vol_max : m->capture_vol_max; - - if (!has_volume) - continue; - assert(min <= max); int r = chan->type == CHANNEL_PLAYBACK @@ -199,10 +281,9 @@ update_state(struct module *mod, snd_mixer_elem_t *elem) LOG_DBG("%s,%s: %s: muted: %d", m->card, m->mixer, chan->name, !unmuted); } - mtx_lock(&mod->lock); m->online = true; - mtx_unlock(&mod->lock); + mtx_unlock(&mod->lock); mod->bar->refresh(mod->bar); } @@ -269,6 +350,16 @@ run_while_online(struct module *mod) } } + if (snd_mixer_selem_get_playback_dB_range( + elem, &m->playback_db_min, &m->playback_db_max) < 0) + { + LOG_WARN( + "%s,%s: failed to get playback dB range, " + "will use raw volume values instead", m->card, m->mixer); + m->has_playback_db = false; + } else + m->has_playback_db = true; + /* Get capture volume range */ m->has_capture_volume = snd_mixer_selem_has_capture_volume(elem) > 0; if (m->has_capture_volume) { @@ -290,6 +381,16 @@ run_while_online(struct module *mod) } } + if (snd_mixer_selem_get_capture_dB_range( + elem, &m->capture_db_min, &m->capture_db_max) < 0) + { + LOG_WARN( + "%s,%s: failed to get capture dB range, " + "will use raw volume values instead", m->card, m->mixer); + m->has_capture_db = false; + } else + m->has_capture_db = true; + /* Get available channels */ for (size_t i = 0; i < SND_MIXER_SCHN_LAST; i++) { bool is_playback = snd_mixer_selem_has_playback_channel(elem, i) == 1; @@ -361,13 +462,24 @@ run_while_online(struct module *mod) update_state(mod, elem); LOG_INFO( - "%s,%s: volume range=%ld-%ld, current=%ld%s (sources: volume=%s, muted=%s)", + "%s,%s: %s range=%ld-%ld, current=%ld%s (sources: volume=%s, muted=%s)", m->card, m->mixer, - m->volume_chan->type == CHANNEL_PLAYBACK - ? m->playback_vol_min : m->capture_vol_min, - m->volume_chan->type == CHANNEL_PLAYBACK - ? m->playback_vol_max : m->capture_vol_max, - m->volume_chan->vol_cur, + m->volume_chan->use_db ? "dB" : "volume", + (m->volume_chan->type == CHANNEL_PLAYBACK + ? (m->volume_chan->use_db + ? m->playback_db_min + : m->playback_vol_min) + : (m->volume_chan->use_db + ? m->capture_db_min + : m->capture_vol_min)), + (m->volume_chan->type == CHANNEL_PLAYBACK + ? (m->volume_chan->use_db + ? m->playback_db_max + : m->playback_vol_max) + : (m->volume_chan->use_db + ? m->capture_db_max + : m->capture_vol_max)), + m->volume_chan->use_db ? m->volume_chan->db_cur : m->volume_chan->vol_cur, m->muted_chan->muted ? " (muted)" : "", m->volume_chan->name, m->muted_chan->name); From 8d91cbd8a3f1b3bfa4e607c3306cbd62c9bca176 Mon Sep 17 00:00:00 2001 From: Baptiste Daroussin Date: Thu, 23 Jun 2022 10:30:49 +0200 Subject: [PATCH 378/611] modules: use portable function to count cpus --- modules/cpu.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/modules/cpu.c b/modules/cpu.c index b4067cd..4d2c3c8 100644 --- a/modules/cpu.c +++ b/modules/cpu.c @@ -9,8 +9,7 @@ #include #include #include - -#include +#include #define LOG_MODULE "cpu" #define LOG_ENABLE_DBG 0 @@ -57,7 +56,7 @@ description(struct module *mod) static uint32_t get_cpu_nb_cores() { - int nb_cores = get_nprocs(); + int nb_cores = sysconf(_SC_NPROCESSORS_ONLN); LOG_DBG("CPU count: %d", nb_cores); return nb_cores; From dd1280f49d6851e81ffeeaee2b7f2dd7c976ba1b Mon Sep 17 00:00:00 2001 From: Baptiste Daroussin Date: Thu, 23 Jun 2022 14:25:46 +0200 Subject: [PATCH 379/611] portability: add missing header for signal related functions --- modules/script.c | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/script.c b/modules/script.c index f701e99..117cc89 100644 --- a/modules/script.c +++ b/modules/script.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include From 138db05d70c0938593737f5255da41a9dfe20753 Mon Sep 17 00:00:00 2001 From: Baptiste Daroussin Date: Thu, 23 Jun 2022 14:35:36 +0200 Subject: [PATCH 380/611] portability: remove unused header --- modules/script.c | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/script.c b/modules/script.c index 117cc89..829932f 100644 --- a/modules/script.c +++ b/modules/script.c @@ -13,7 +13,6 @@ #include #include #include -#include #define LOG_MODULE "script" #define LOG_ENABLE_DBG 0 From d15d1f58f7ef96231d5c863240507834475a99f6 Mon Sep 17 00:00:00 2001 From: Baptiste Daroussin Date: Thu, 23 Jun 2022 14:49:47 +0200 Subject: [PATCH 381/611] meson: declare CFLAGS dependencies for xcb-stuff on FreeBSD we need append to the lookup path a non default path to find headers --- meson.build | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/meson.build b/meson.build index ef28c58..74915df 100644 --- a/meson.build +++ b/meson.build @@ -90,7 +90,11 @@ if backend_x11 c_args: xcb_errors.found() ? '-DHAVE_XCB_ERRORS' : [], pic: plugs_as_libs) - xcb_stuff = declare_dependency(link_with: xcb_stuff_lib) + xcb_stuff = declare_dependency( + link_with: xcb_stuff_lib, + dependencies: [xcb_aux, xcb_cursor, xcb_event, xcb_ewmh, xcb_randr, + xcb_render, xcb_errors], + ) install_headers('xcb.h', subdir: 'yambar') endif From b331473a6b4b9de2f35462236ece1db11f876071 Mon Sep 17 00:00:00 2001 From: Baptiste Daroussin Date: Thu, 23 Jun 2022 14:54:50 +0200 Subject: [PATCH 382/611] meson: add missing dependencies on wayland_client --- modules/meson.build | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/meson.build b/modules/meson.build index ada5ab6..d8fa9b5 100644 --- a/modules/meson.build +++ b/modules/meson.build @@ -59,7 +59,7 @@ if backend_wayland endforeach mod_data += { - 'river': [[wl_proto_src + wl_proto_headers + river_proto_src + river_proto_headers], [dynlist]], + 'river': [[wl_proto_src + wl_proto_headers + river_proto_src + river_proto_headers], [dynlist, wayland_client]], } ftop_proto_headers = [] @@ -81,7 +81,7 @@ if backend_wayland endforeach mod_data += { - 'foreign-toplevel': [[wl_proto_src + wl_proto_headers + ftop_proto_headers + ftop_proto_src], [dynlist]], + 'foreign-toplevel': [[wl_proto_src + wl_proto_headers + ftop_proto_headers + ftop_proto_src], [dynlist, wayland_client]], } endif From 084a9021b98b15648606bb0cada74cff0773af76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Gibrowski=20Fa=C3=A9?= Date: Wed, 13 Jul 2022 11:29:52 -0300 Subject: [PATCH 383/611] fix configurations in example scripts We forgot to update the configurations in the example scripts to the new map syntax based on conditions. This fixes that. Related to #201 --- examples/scripts/dwl-tags.sh | 42 ++++++++++++++++-------------------- examples/scripts/pacman.sh | 5 ++--- 2 files changed, 20 insertions(+), 27 deletions(-) diff --git a/examples/scripts/dwl-tags.sh b/examples/scripts/dwl-tags.sh index bc24ac7..7b70fff 100755 --- a/examples/scripts/dwl-tags.sh +++ b/examples/scripts/dwl-tags.sh @@ -31,39 +31,33 @@ # content: # - map: # margin: 4 -# tag: tag_0_occupied -# values: -# true: +# conditions: +# tag_0_occupied: # map: -# tag: tag_0_focused -# values: -# true: {string: {text: "{tag_0}", <<: *focused}} -# false: {string: {text: "{tag_0}", <<: *occupied}} -# false: +# conditions: +# tag_0_focused: {string: {text: "{tag_0}", <<: *focused}} +# ~tag_0_focused: {string: {text: "{tag_0}", <<: *occupied}} +# ~tag_0_occupied: # map: -# tag: tag_0_focused -# values: -# true: {string: {text: "{tag_0}", <<: *focused}} -# false: {string: {text: "{tag_0}", <<: *default}} +# conditions: +# tag_0_focused: {string: {text: "{tag_0}", <<: *focused}} +# ~tag_0_focused: {string: {text: "{tag_0}", <<: *default}} +# ... # ... -# ... # ... # - map: # margin: 4 -# tag: tag_8_occupied -# values: -# true: +# conditions: +# tag_8_occupied: # map: -# tag: tag_8_focused -# values: -# true: {string: {text: "{tag_8}", <<: *focused}} -# false: {string: {text: "{tag_8}", <<: *occupied}} -# false: +# conditions: +# tag_8_focused: {string: {text: "{tag_8}", <<: *focused}} +# ~tag_8_focused: {string: {text: "{tag_8}", <<: *occupied}} +# ~tag_8_occupied: # map: -# tag: tag_8_focused # values: -# true: {string: {text: "{tag_8}", <<: *focused}} -# false: {string: {text: "{tag_8}", <<: *default}} +# tag_8_focused: {string: {text: "{tag_8}", <<: *focused}} +# ~tag_8_focused: {string: {text: "{tag_8}", <<: *default}} # - list: # spacing: 3 # items: diff --git a/examples/scripts/pacman.sh b/examples/scripts/pacman.sh index a20fd6b..a04b7a9 100755 --- a/examples/scripts/pacman.sh +++ b/examples/scripts/pacman.sh @@ -24,10 +24,9 @@ # args: [] # content: # map: -# tag: pkg # default: { string: { text: "{pacman} + {aur} = {pkg}" } } -# values: -# 0: {string: {text: no updates}} +# conditions: +# pkg == 0: {string: {text: no updates}} declare interval aur_helper pacman_num aur_num pkg_num From 4257c80eeed87d7eab706328a96b2d86412e92ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 23 Aug 2022 16:42:56 +0200 Subject: [PATCH 384/611] ci (sr.ht): pull directly from git.sr.ht --- .builds/alpine-x64.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.builds/alpine-x64.yml b/.builds/alpine-x64.yml index fc657ce..931c21d 100644 --- a/.builds/alpine-x64.yml +++ b/.builds/alpine-x64.yml @@ -25,7 +25,7 @@ packages: - py3-pip sources: - - https://codeberg.org/dnkl/yambar + - https://git.sr.ht/~dnkl/yambar # triggers: # - action: email From 2759ba6349cf3e03ea509616f8ef6da6d613dacd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 28 Aug 2022 20:19:22 +0200 Subject: [PATCH 385/611] module/network: generate nl80211 sequence number from /dev/urandom --- modules/network.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/modules/network.c b/modules/network.c index 9ec5993..e196e14 100644 --- a/modules/network.c +++ b/modules/network.c @@ -11,6 +11,9 @@ #include #include +#include +#include + #include #include #include @@ -51,6 +54,7 @@ struct private { int genl_sock; int rt_sock; + int urandom_fd; struct { uint16_t family_id; @@ -90,6 +94,9 @@ destroy(struct module *mod) m->label->destroy(m->label); + if (m->urandom_fd >= 0) + close(m->urandom_fd); + tll_free(m->addrs); free(m->ssid); free(m->iface); @@ -411,7 +418,12 @@ send_nl80211_get_interface(struct private *m) LOG_DBG("%s: sending nl80211 get-interface request", m->iface); - uint32_t seq = time(NULL); + uint32_t seq; + if (read(m->urandom_fd, &seq, sizeof(seq)) != sizeof(seq)) { + LOG_ERRNO("failed to read from /dev/urandom"); + return false; + } + if (send_nl80211_request(m, NL80211_CMD_GET_INTERFACE, NLM_F_REQUEST, seq)) { m->nl80211.get_interface_seq_nr = seq; return true; @@ -1285,6 +1297,12 @@ run(struct module *mod) static struct module * network_new(const char *iface, struct particle *label, int poll_interval) { + int urandom_fd = open("/dev/urandom", O_RDONLY); + if (urandom_fd < 0) { + LOG_ERRNO("failed to open /dev/urandom"); + return NULL; + } + struct private *priv = calloc(1, sizeof(*priv)); priv->iface = strdup(iface); priv->label = label; @@ -1292,6 +1310,7 @@ network_new(const char *iface, struct particle *label, int poll_interval) priv->genl_sock = -1; priv->rt_sock = -1; + priv->urandom_fd = urandom_fd; priv->nl80211.family_id = -1; priv->get_addresses = true; priv->ifindex = -1; From aa09d88d8e52537fb81c2bbb8f1f373784b8adaf Mon Sep 17 00:00:00 2001 From: Midgard Date: Wed, 31 Aug 2022 15:12:14 +0200 Subject: [PATCH 386/611] =?UTF-8?q?doc:=20string=20particle=E2=80=99s=20?= =?UTF-8?q?=E2=80=9Cmax=E2=80=9D=20uses=20Unicode=20=E2=80=A6=20now?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit And fix a typo in the yambar-particles file. --- doc/yambar-particles.5.scd | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/yambar-particles.5.scd b/doc/yambar-particles.5.scd index dba606a..8e999fc 100644 --- a/doc/yambar-particles.5.scd +++ b/doc/yambar-particles.5.scd @@ -106,7 +106,7 @@ of free text mixed with tag specifiers. | text : string : yes -: Format string. Tags are spcified with _{tag_name}_. Some tag types +: Format string. Tags are specified with _{tag_name}_. Some tag types have suffixes that can be appended (e.g. _{tag_name:suffix}_). See *yambar-modules*(5)). | max @@ -114,9 +114,9 @@ of free text mixed with tag specifiers. : no : Sets the rendered string's maximum length. If the final string's length exceeds this, the rendered string will be truncated, and - "..." will be appended. Note that the trailing "..." are + "…" will be appended. Note that the trailing "…" is *included* in the maximum length. I.e. if you set _max_ to '5', you - will only get *2* characters from the string. + will only get *4* characters from the string. ## EXAMPLES From d002919bad93a8eb2e4b1fea15fa47bf555bc073 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 28 Aug 2022 20:36:12 +0200 Subject: [PATCH 387/611] module/network: generate nl80211 sequence number from /dev/urandom --- modules/network.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/modules/network.c b/modules/network.c index e196e14..011ac33 100644 --- a/modules/network.c +++ b/modules/network.c @@ -442,7 +442,12 @@ send_nl80211_get_station(struct private *m) LOG_DBG("%s: sending nl80211 get-station request", m->iface); - uint32_t seq = time(NULL); + uint32_t seq; + if (read(m->urandom_fd, &seq, sizeof(seq)) != sizeof(seq)) { + LOG_ERRNO("failed to read from /dev/urandom"); + return false; + } + if (send_nl80211_request( m, NL80211_CMD_GET_STATION, NLM_F_REQUEST | NLM_F_DUMP, seq)) { From 5da1cd4a38903646f34b4615c5ae3fa046f7da1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 1 Sep 2022 18:47:39 +0200 Subject: [PATCH 388/611] module/script: process *all* transactions received in a single read() When the script module received multiple transactions in a single batch, only the first were processed. This lead to multiple, unprocessed transactions stacking up in the receive buffer. Every time a new transaction was received, we popped the oldest transaction from the buffer, but never actually getting to the last one. This is perceived as "lag" by the user, where the bar displays outdated information. Closes #221 --- CHANGELOG.md | 4 +++- modules/script.c | 28 +++++++++++++++------------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index edaa31e..cafee73 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -106,13 +106,15 @@ * Crash when a particle is “too wide”, and tries to render outside the bar ([#198][198]). * string: crash when failing to convert string to UTF-32. - +* script: only first transaction processed when receiving multiple + transactions in a single batch ([#221][221]). [169]: https://codeberg.org/dnkl/yambar/issues/169 [172]: https://codeberg.org/dnkl/yambar/issues/172 [178]: https://codeberg.org/dnkl/yambar/issues/178 [177]: https://codeberg.org/dnkl/yambar/issues/177 [198]: https://codeberg.org/dnkl/yambar/issues/198 +[221]: https://codeberg.org/dnkl/yambar/issues/221 ### Security diff --git a/modules/script.c b/modules/script.c index 829932f..fd149d2 100644 --- a/modules/script.c +++ b/modules/script.c @@ -313,21 +313,23 @@ data_received(struct module *mod, const char *data, size_t len) memcpy(&m->recv_buf.data[m->recv_buf.idx], data, len); m->recv_buf.idx += len; - const char *eot = memmem(m->recv_buf.data, m->recv_buf.idx, "\n\n", 2); - if (eot == NULL) { - /* End of transaction not yet available */ - return true; + while (true) { + const char *eot = memmem(m->recv_buf.data, m->recv_buf.idx, "\n\n", 2); + if (eot == NULL) { + /* End of transaction not yet available */ + return true; + } + + const size_t transaction_size = eot - m->recv_buf.data + 1; + process_transaction(mod, transaction_size); + + assert(m->recv_buf.idx >= transaction_size + 1); + memmove(m->recv_buf.data, + &m->recv_buf.data[transaction_size + 1], + m->recv_buf.idx - (transaction_size + 1)); + m->recv_buf.idx -= transaction_size + 1; } - const size_t transaction_size = eot - m->recv_buf.data + 1; - process_transaction(mod, transaction_size); - - assert(m->recv_buf.idx >= transaction_size + 1); - memmove(m->recv_buf.data, - &m->recv_buf.data[transaction_size + 1], - m->recv_buf.idx - (transaction_size + 1)); - m->recv_buf.idx -= transaction_size + 1; - return true; } From d1a8029e6cfc6854b226f0d42a5e77baed441058 Mon Sep 17 00:00:00 2001 From: Midgard Date: Wed, 31 Aug 2022 14:45:46 +0200 Subject: [PATCH 389/611] =?UTF-8?q?module/mpd:=20add=20=E2=80=9Cfile?= =?UTF-8?q?=E2=80=9D=20tag?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 2 ++ doc/yambar-modules-mpd.5.scd | 3 +++ modules/mpd.c | 10 +++++++++- 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cafee73..06e8b58 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,10 +26,12 @@ `ul-speed` when `poll-interval` is set. * new module: disk-io. * alsa: `dB` tag ([#202][202]) +* mpd: `file` tag ([#219][219]). [153]: https://codeberg.org/dnkl/yambar/issues/153 [159]: https://codeberg.org/dnkl/yambar/issues/159 [202]: https://codeberg.org/dnkl/yambar/issues/202 +[219]: https://codeberg.org/dnkl/yambar/pulls/219 ### Changed diff --git a/doc/yambar-modules-mpd.5.scd b/doc/yambar-modules-mpd.5.scd index 93e776b..ccc5d85 100644 --- a/doc/yambar-modules-mpd.5.scd +++ b/doc/yambar-modules-mpd.5.scd @@ -32,6 +32,9 @@ mpd - This module provides MPD status such as currently playing artist/album/son | title : string : Title of currently playing song (also valid in *paused* state) +| file +: string +: Filename or URL of currently playing song (also valid in *paused* state) | pos : string : *%M:%S*-formatted string describing the song's current position diff --git a/modules/mpd.c b/modules/mpd.c index 27992d3..05edd7b 100644 --- a/modules/mpd.c +++ b/modules/mpd.c @@ -42,6 +42,7 @@ struct private { char *album; char *artist; char *title; + char *file; struct { uint64_t value; @@ -75,6 +76,7 @@ destroy(struct module *mod) free(m->album); free(m->artist); free(m->title); + free(m->file); assert(m->conn == NULL); m->label->destroy(m->label); @@ -173,13 +175,14 @@ content(struct module *mod) tag_new_string(mod, "album", m->album), tag_new_string(mod, "artist", m->artist), tag_new_string(mod, "title", m->title), + tag_new_string(mod, "file", m->file), tag_new_string(mod, "pos", pos), tag_new_string(mod, "end", end), tag_new_int(mod, "duration", m->duration), tag_new_int_realtime( mod, "elapsed", elapsed, 0, m->duration, realtime), }, - .count = 12, + .count = 13, }; mtx_unlock(&mod->lock); @@ -354,20 +357,24 @@ update_status(struct module *mod) free(m->album); m->album = NULL; free(m->artist); m->artist = NULL; free(m->title); m->title = NULL; + free(m->file); m->file = NULL; mtx_unlock(&mod->lock); } else { const char *album = mpd_song_get_tag(song, MPD_TAG_ALBUM, 0); const char *artist = mpd_song_get_tag(song, MPD_TAG_ARTIST, 0); const char *title = mpd_song_get_tag(song, MPD_TAG_TITLE, 0); + const char *file = mpd_song_get_uri(song); mtx_lock(&mod->lock); free(m->album); free(m->artist); free(m->title); + free(m->file); m->album = album != NULL ? strdup(album) : NULL; m->artist = artist != NULL ? strdup(artist) : NULL; m->title = title != NULL ? strdup(title) : NULL; + m->file = file != NULL ? strdup(file) : NULL; mtx_unlock(&mod->lock); mpd_song_free(song); @@ -397,6 +404,7 @@ run(struct module *mod) free(m->album); m->album = NULL; free(m->artist); m->artist = NULL; free(m->title); m->title = NULL; + free(m->file); m->file = NULL; m->state = MPD_STATE_UNKNOWN; m->elapsed.value = m->duration = 0; m->elapsed.when.tv_sec = m->elapsed.when.tv_nsec = 0; From 4143099e94c1144efecc5711985d8cf19e1e2f75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 10 Sep 2022 13:46:34 +0200 Subject: [PATCH 390/611] module/network: resurrect SSID MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Recent kernels, or possibly updated wireless drivers, no longer provide the SSID in `NL80211_CMD_NEW_STATION` responses. For yambar, this meant the SSID was always missing. This patch fixes this, by also issuing a NL80211_CMD_GET_SCAN command. The response to this (NL80211_CMD_SCAN_RESULTS) _may_ return many access points. Pick out the one that we’re associated with, and inspect its BSS_INFORMATION_ELEMENTS. This is a raw data structure containing, among other things, the SSID. I haven’t been able to find any documentation of the format, but could glean enough from https://git.kernel.org/pub/scm/linux/kernel/git/jberg/iw.git/tree/scan.c#n2313 to be able to parse out the SSID. Note that we get a “device or resource busy” error if we try to issue both the NL80211_CMD_GET_STATION and the NL80211_CMD_GET_SCAN commands at the same time. Therefore, we issue the GET_SCAN command after completing the GET_STATION command. Closes #226 --- CHANGELOG.md | 4 ++ modules/network.c | 146 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 147 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 06e8b58..1539093 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -110,6 +110,9 @@ * string: crash when failing to convert string to UTF-32. * script: only first transaction processed when receiving multiple transactions in a single batch ([#221][221]). +* network: missing SSID (recent kernels, or possibly wireless drivers, + no longer provide the SSID in the `NL80211_CMD_NEW_STATION` + response) ([#226][226]). [169]: https://codeberg.org/dnkl/yambar/issues/169 [172]: https://codeberg.org/dnkl/yambar/issues/172 @@ -117,6 +120,7 @@ [177]: https://codeberg.org/dnkl/yambar/issues/177 [198]: https://codeberg.org/dnkl/yambar/issues/198 [221]: https://codeberg.org/dnkl/yambar/issues/221 +[226]: https://codeberg.org/dnkl/yambar/issues/226 ### Security diff --git a/modules/network.c b/modules/network.c index 011ac33..710148e 100644 --- a/modules/network.c +++ b/modules/network.c @@ -60,6 +60,7 @@ struct private { uint16_t family_id; uint32_t get_interface_seq_nr; uint32_t get_station_seq_nr; + uint32_t get_scan_seq_nr; } nl80211; bool get_addresses; @@ -457,6 +458,32 @@ send_nl80211_get_station(struct private *m) return false; } +static bool +send_nl80211_get_scan(struct private *m) +{ + if (m->nl80211.get_scan_seq_nr > 0) { + LOG_ERR( + "%s: nl80211 get-scan request already in progress", m->iface); + return true; + } + + LOG_DBG("%s: sending nl80211 get-scan request", m->iface); + + uint32_t seq; + if (read(m->urandom_fd, &seq, sizeof(seq)) != sizeof(seq)) { + LOG_ERRNO("failed to read from /dev/urandom"); + return false; + } + + if (send_nl80211_request( + m, NL80211_CMD_GET_SCAN, NLM_F_REQUEST | NLM_F_DUMP, seq)) + { + m->nl80211.get_scan_seq_nr = seq; + return true; + } else + return false; +} + static bool find_my_ifindex(struct module *mod, const struct ifinfomsg *msg, size_t len) { @@ -810,6 +837,7 @@ handle_nl80211_new_interface(struct module *mod, uint16_t type, bool nested, case NL80211_ATTR_SSID: { const char *ssid = payload; + LOG_INFO("%s: SSID: %.*s", m->iface, (int)len, ssid); mtx_lock(&mod->lock); free(m->ssid); @@ -951,6 +979,102 @@ handle_nl80211_new_station(struct module *mod, uint16_t type, bool nested, return true; } +static bool +handle_ies(struct module *mod, const void *_ies, size_t len) +{ + struct private *m = mod->private; + const uint8_t *ies = _ies; + + while (len >= 2 && len - 2 >= ies[1]) { + switch (ies[0]) { + case 0: { /* SSID */ + const char *ssid = (const char *)&ies[2]; + const size_t ssid_len = ies[1]; + + LOG_INFO("%s: SSID: %.*s", m->iface, (int)ssid_len, ssid); + + mtx_lock(&mod->lock); + free(m->ssid); + m->ssid = strndup(ssid, ssid_len); + mtx_unlock(&mod->lock); + + mod->bar->refresh(mod->bar); + break; + } + } + len -= ies[1] + 2; + ies += ies[1] + 2; + } + + return true; +} + +struct scan_results_context { + bool associated; + + const void *ies; + size_t ies_size; +}; + +static bool +handle_nl80211_bss(struct module *mod, uint16_t type, bool nested, + const void *payload, size_t len, void *_ctx) +{ + struct private *m UNUSED = mod->private; + struct scan_results_context *ctx = _ctx; + + switch (type) { + case NL80211_BSS_STATUS: { + const uint32_t status = *(uint32_t *)payload; + + if (status == NL80211_BSS_STATUS_ASSOCIATED) { + ctx->associated = true; + + if (ctx->ies != NULL) { + /* Deferred handling of BSS_INFORMATION_ELEMENTS */ + return handle_ies(mod, ctx->ies, ctx->ies_size); + } + } + break; + } + + case NL80211_BSS_INFORMATION_ELEMENTS: + if (ctx->associated) + return handle_ies(mod, payload, len); + else { + /* + * We’re either not associated, or, we haven’t seen the + * BSS_STATUS attribute yet. + * + * Save a pointer to the IES payload, so that we can + * process it later, if we see a + * BSS_STATUS == BSS_STATUS_ASSOCIATED. + */ + ctx->ies = payload; + ctx->ies_size = len; + } + } + + return true; +} + +static bool +handle_nl80211_scan_results(struct module *mod, uint16_t type, bool nested, + const void *payload, size_t len) +{ + struct private *m UNUSED = mod->private; + + struct scan_results_context ctx = {0}; + + switch (type) { + case NL80211_ATTR_BSS: + foreach_nlattr_nested(mod, payload, len, &handle_nl80211_bss, &ctx); + break; + } + + return true; +} + /* * Reads at least one (possibly more) message. * @@ -1083,6 +1207,11 @@ parse_genl_reply(struct module *mod, const struct nlmsghdr *hdr, size_t len) /* Current request is now considered complete */ m->nl80211.get_station_seq_nr = 0; } + + else if (hdr->nlmsg_seq == m->nl80211.get_scan_seq_nr) { + /* Current request is now considered complete */ + m->nl80211.get_scan_seq_nr = 0; + } } else if (hdr->nlmsg_type == GENL_ID_CTRL) { @@ -1101,8 +1230,6 @@ parse_genl_reply(struct module *mod, const struct nlmsghdr *hdr, size_t len) LOG_DBG("%s: got interface information", m->iface); foreach_nlattr( mod, genl, msg_size, &handle_nl80211_new_interface); - - LOG_INFO("%s: SSID: %s", m->iface, m->ssid); } break; @@ -1148,6 +1275,18 @@ parse_genl_reply(struct module *mod, const struct nlmsghdr *hdr, size_t len) m->iface, m->signal_strength_dbm, m->rx_bitrate / 1000 / 1000, m->tx_bitrate / 1000 / 1000); + + /* Can’t issue both get-station and get-scan at the + * same time. So, always run a get-scan when a + * get-station is complete */ + send_nl80211_get_scan(m); + break; + + case NL80211_CMD_NEW_SCAN_RESULTS: + if (nl80211_is_for_us(mod, genl, msg_size)) { + LOG_DBG("%s: got scan results", m->iface); + foreach_nlattr(mod, genl, msg_size, &handle_nl80211_scan_results); + } break; default: @@ -1165,7 +1304,8 @@ parse_genl_reply(struct module *mod, const struct nlmsghdr *hdr, size_t len) else if (nl_errno == ENOENT) ; /* iface down? */ else - LOG_ERRNO_P(nl_errno, "%s: nl80211 reply", m->iface); + LOG_ERRNO_P(nl_errno, "%s: nl80211 reply (seq-nr: %u)", + m->iface, hdr->nlmsg_seq); } else { From 8d5deda4e40bbebe060283160c9bcdea35b1c656 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 3 Oct 2022 09:52:44 +0200 Subject: [PATCH 391/611] =?UTF-8?q?module/river:=20fix=20=E2=80=9Cuse=20of?= =?UTF-8?q?=20uninitialized=20variable=E2=80=9D=20warning?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There were “goto err” statements before “unlock_at_exit” had been initialized. --- modules/river.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/river.c b/modules/river.c index 23ac032..16e34f5 100644 --- a/modules/river.c +++ b/modules/river.c @@ -637,6 +637,7 @@ run(struct module *mod) int ret = 1; struct wl_display *display = NULL; struct wl_registry *registry = NULL; + bool unlock_at_exit = false; if ((display = wl_display_connect(NULL)) == NULL) { LOG_ERR("no Wayland compositor running?"); @@ -659,8 +660,8 @@ run(struct module *mod) wl_display_roundtrip(display); - bool unlock_at_exit = true; mtx_lock(&mod->lock); + unlock_at_exit = true; m->is_starting_up = false; From 6ed576c7198d694a1c6cc76507dcb8be05bb15cd Mon Sep 17 00:00:00 2001 From: Peter Rice Date: Sat, 24 Sep 2022 00:31:44 -0400 Subject: [PATCH 392/611] particle/on-click: support next/previous buttons --- CHANGELOG.md | 2 ++ bar/wayland.c | 2 ++ config-verify.c | 2 ++ config.c | 4 ++++ doc/yambar-particles.5.scd | 8 ++++++++ particle.c | 2 ++ particle.h | 2 ++ 7 files changed, 22 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1539093..532530a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,11 +27,13 @@ * new module: disk-io. * alsa: `dB` tag ([#202][202]) * mpd: `file` tag ([#219][219]). +* on-click: support `next`/`previous` mouse buttons ([#228][228]). [153]: https://codeberg.org/dnkl/yambar/issues/153 [159]: https://codeberg.org/dnkl/yambar/issues/159 [202]: https://codeberg.org/dnkl/yambar/issues/202 [219]: https://codeberg.org/dnkl/yambar/pulls/219 +[228]: https://codeberg.org/dnkl/yambar/pulls/228 ### Changed diff --git a/bar/wayland.c b/bar/wayland.c index a57fdfa..edbf0db 100644 --- a/bar/wayland.c +++ b/bar/wayland.c @@ -292,6 +292,8 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer, case BTN_LEFT: btn = MOUSE_BTN_LEFT; break; case BTN_MIDDLE: btn = MOUSE_BTN_MIDDLE; break; case BTN_RIGHT: btn = MOUSE_BTN_RIGHT; break; + case BTN_SIDE: btn = MOUSE_BTN_PREVIOUS; break; + case BTN_EXTRA: btn = MOUSE_BTN_NEXT; break; default: return; } diff --git a/config-verify.c b/config-verify.c index 68a50c8..a099ef7 100644 --- a/config-verify.c +++ b/config-verify.c @@ -188,6 +188,8 @@ conf_verify_on_click(keychain_t *chain, const struct yml_node *node) {"right", false, &conf_verify_string}, {"wheel-up", false, &conf_verify_string}, {"wheel-down", false, &conf_verify_string}, + {"previous", false, &conf_verify_string}, + {"next", false, &conf_verify_string}, {NULL, false, NULL}, }; diff --git a/config.c b/config.c index d2c11a6..3d32678 100644 --- a/config.c +++ b/config.c @@ -236,6 +236,10 @@ conf_to_particle(const struct yml_node *node, struct conf_inherit inherited) on_click_templates[MOUSE_BTN_WHEEL_UP] = template; else if (strcmp(key, "wheel-down") == 0) on_click_templates[MOUSE_BTN_WHEEL_DOWN] = template; + else if (strcmp(key, "previous") == 0) + on_click_templates[MOUSE_BTN_PREVIOUS] = template; + else if (strcmp(key, "next") == 0) + on_click_templates[MOUSE_BTN_NEXT] = template; else assert(false); } diff --git a/doc/yambar-particles.5.scd b/doc/yambar-particles.5.scd index 8e999fc..1b6059d 100644 --- a/doc/yambar-particles.5.scd +++ b/doc/yambar-particles.5.scd @@ -68,6 +68,14 @@ following attributes are supported by all particles: : string : no : Command to execute every time a 'wheel-down' event is triggered. +| on-click.previous +: string +: no +: Command to execute when the particle is clicked with the 'previous' button. +| on-click.next +: string +: no +: Command to execute when the particle is clicked with the 'next' button. | deco : decoration : no diff --git a/particle.c b/particle.c index 5f6b04d..fe1d138 100644 --- a/particle.c +++ b/particle.c @@ -165,6 +165,8 @@ exposable_default_on_mouse(struct exposable *exposable, struct bar *bar, [MOUSE_BTN_COUNT] = "count", [MOUSE_BTN_WHEEL_UP] = "wheel-up", [MOUSE_BTN_WHEEL_DOWN] = "wheel-down", + [MOUSE_BTN_PREVIOUS] = "previous", + [MOUSE_BTN_NEXT] = "next", }; LOG_DBG("on_mouse: exposable=%p, event=%s, btn=%s, x=%d, y=%d (on-click=%s)", exposable, event == ON_MOUSE_MOTION ? "motion" : "click", diff --git a/particle.h b/particle.h index bdf01f2..c92c5fc 100644 --- a/particle.h +++ b/particle.h @@ -20,6 +20,8 @@ enum mouse_button { MOUSE_BTN_RIGHT, MOUSE_BTN_WHEEL_UP, MOUSE_BTN_WHEEL_DOWN, + MOUSE_BTN_PREVIOUS, + MOUSE_BTN_NEXT, MOUSE_BTN_COUNT, }; From 028011a8169b7b447c6a91b4a32c302ac4199db4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 4 Oct 2022 21:09:43 +0200 Subject: [PATCH 393/611] =?UTF-8?q?module/sway-xkb:=20don=E2=80=99t=20add?= =?UTF-8?q?=20the=20=E2=80=9Csame=E2=80=9D=20device=20multiple=20times?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Not sure if Sway bug or not, but we’ve seen Sway presenting multiple input devices with the exact same ID (and nothing else differentiating them). This caused a crash in the sway-xkb module, since we didn’t check if we were already tracking the device, and thus bumped the “num_existing_inputs” variable multiple times for the same input object. This lead to a content() returning an array with uninitialized elements, and thus a crash. Closes #229 --- CHANGELOG.md | 3 +++ modules/sway-xkb.c | 10 +++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 532530a..ade384d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -115,6 +115,8 @@ * network: missing SSID (recent kernels, or possibly wireless drivers, no longer provide the SSID in the `NL80211_CMD_NEW_STATION` response) ([#226][226]). +* sway-xkb: crash when compositor presents multiple inputs with + identical IDs ([#229][229]). [169]: https://codeberg.org/dnkl/yambar/issues/169 [172]: https://codeberg.org/dnkl/yambar/issues/172 @@ -123,6 +125,7 @@ [198]: https://codeberg.org/dnkl/yambar/issues/198 [221]: https://codeberg.org/dnkl/yambar/issues/221 [226]: https://codeberg.org/dnkl/yambar/issues/226 +[229]: https://codeberg.org/dnkl/yambar/issues/229 ### Security diff --git a/modules/sway-xkb.c b/modules/sway-xkb.c index 6ee3e3a..3f2e965 100644 --- a/modules/sway-xkb.c +++ b/modules/sway-xkb.c @@ -67,6 +67,7 @@ content(struct module *mod) mtx_lock(&mod->lock); + assert(m->num_existing_inputs <= m->num_inputs); struct exposable *particles[max(m->num_existing_inputs, 1)]; for (size_t i = 0, j = 0; i < m->num_inputs; i++) { @@ -120,9 +121,12 @@ handle_input_reply(int type, const struct json_object *json, void *_mod) struct input *input = NULL; for (size_t i = 0; i < m->num_inputs; i++) { struct input *maybe_input = &m->inputs[i]; - if (strcmp(maybe_input->identifier, id) == 0) { + if (strcmp(maybe_input->identifier, id) == 0 && !maybe_input->exists) + { input = maybe_input; + LOG_DBG("adding: %s", id); + mtx_lock(&mod->lock); input->exists = true; m->num_existing_inputs++; @@ -209,6 +213,8 @@ handle_input_event(int type, const struct json_object *json, void *_mod) if (is_removed) { if (input->exists) { + LOG_DBG("removing: %s", id); + mtx_lock(&mod->lock); input->exists = false; m->num_existing_inputs--; @@ -220,6 +226,8 @@ handle_input_event(int type, const struct json_object *json, void *_mod) if (is_added) { if (!input->exists) { + LOG_DBG("adding: %s", id); + mtx_lock(&mod->lock); input->exists = true; m->num_existing_inputs++; From 8f1a123de2345394e2eabeb9c24f58c403bc2801 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 16 Oct 2022 16:17:08 +0200 Subject: [PATCH 394/611] module/network: only log SSID when different from current one This prevents log spamming with poll-interval set Closes #232 --- modules/network.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/modules/network.c b/modules/network.c index 710148e..1cb7a2e 100644 --- a/modules/network.c +++ b/modules/network.c @@ -837,7 +837,9 @@ handle_nl80211_new_interface(struct module *mod, uint16_t type, bool nested, case NL80211_ATTR_SSID: { const char *ssid = payload; - LOG_INFO("%s: SSID: %.*s", m->iface, (int)len, ssid); + + if (m->ssid == NULL || strncmp(m->ssid, ssid, len) != 0) + LOG_INFO("%s: SSID: %.*s", m->iface, (int)len, ssid); mtx_lock(&mod->lock); free(m->ssid); @@ -991,7 +993,8 @@ handle_ies(struct module *mod, const void *_ies, size_t len) const char *ssid = (const char *)&ies[2]; const size_t ssid_len = ies[1]; - LOG_INFO("%s: SSID: %.*s", m->iface, (int)ssid_len, ssid); + if (m->ssid == NULL || strncmp(m->ssid, ssid, ssid_len) != 0) + LOG_INFO("%s: SSID: %.*s", m->iface, (int)ssid_len, ssid); mtx_lock(&mod->lock); free(m->ssid); From 794b1ed633fb8a3d7d5edf5a415a5ab2f2566f7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 27 Oct 2022 15:59:09 +0200 Subject: [PATCH 395/611] module/river: fix broken debug log --- modules/river.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/river.c b/modules/river.c index 16e34f5..14053c3 100644 --- a/modules/river.c +++ b/modules/river.c @@ -133,7 +133,7 @@ content(struct module *mod) #if 0 LOG_DBG("tag: #%u, visible=%d, focused=%d, occupied=%d, state=%s", - i, visible, focused, occupied & (1u << i), state); + i, is_visible, is_focused, is_occupied & (1u << i), state); #endif struct tag_set tags = { From 8deac539eff21841e17320a798ddef74a3205004 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 27 Oct 2022 15:59:32 +0200 Subject: [PATCH 396/611] module/river: new workaround for river issue 69 River seat status events are not fired if the river interface is bound before the output globals are (despite zriver_status_manager_v1_get_river_seat_status() not taking an output as argument). See https://github.com/riverwm/river/issues/69 for details. Up until now, we had a workaround for this, where we deferred binding the seat status interface until after all globals have been processed. This did not help with runtime changes. For example, if a monitor is turned off/on (with e.g. wlr-randr), all future river seat status output events were lost, since the new output global was being bound *after* the river seat status object. This patch implements a new workaround, where we re-bind the river seat status interface every time an output global is added. --- modules/river.c | 69 +++++++++++++++++++++++-------------------------- 1 file changed, 32 insertions(+), 37 deletions(-) diff --git a/modules/river.c b/modules/river.c index 14053c3..a9dc8b4 100644 --- a/modules/river.c +++ b/modules/river.c @@ -54,7 +54,6 @@ struct private { struct particle *title; bool all_monitors; - bool is_starting_up; tll(struct output) outputs; tll(struct seat) seats; }; @@ -112,8 +111,7 @@ content(struct module *mod) } } - const size_t seat_count = m->title != NULL && !m->is_starting_up - ? tll_length(m->seats) : 0; + const size_t seat_count = m->title != NULL ? tll_length(m->seats) : 0; struct exposable *tag_parts[32 + seat_count]; for (unsigned i = 0; i < 32; i++) { @@ -152,7 +150,7 @@ content(struct module *mod) tag_set_destroy(&tags); } - if (m->title != NULL && !m->is_starting_up) { + if (m->title != NULL) { size_t i = 32; tll_foreach(m->seats, it) { const struct seat *seat = &it->item; @@ -189,6 +187,11 @@ verify_iface_version(const char *iface, uint32_t version, uint32_t wanted) static void output_destroy(struct output *output) { + tll_foreach(output->m->seats, it) { + struct seat *seat = &it->item; + if (seat->output == output) + seat->output = NULL; + } free(output->name); if (output->status != NULL) zriver_output_status_v1_destroy(output->status); @@ -323,14 +326,20 @@ static struct zxdg_output_v1_listener xdg_output_listener = { }; static void -instantiate_output(struct output *output) +update_output(struct output *output) { - if (output->m->is_starting_up) - return; - assert(output->wl_output != NULL); - if (output->m->status_manager != NULL && output->status == NULL) { + if (output->m->status_manager != NULL) { + /* + * Bind river output status, if we have already bound the status manager + */ + + if (output->status != NULL) { + zriver_output_status_v1_destroy(output->status); + output->status = NULL; + } + output->status = zriver_status_manager_v1_get_river_output_status( output->m->status_manager, output->wl_output); @@ -382,7 +391,7 @@ focused_output(void *data, struct zriver_seat_status_v1 *zriver_seat_status_v1, static void unfocused_output(void *data, struct zriver_seat_status_v1 *zriver_seat_status_v1, - struct wl_output *wl_output) + struct wl_output *wl_output) { struct seat *seat = data; struct private *m = seat->m; @@ -497,16 +506,18 @@ static const struct wl_seat_listener seat_listener = { }; static void -instantiate_seat(struct seat *seat) +update_seat(struct seat *seat) { assert(seat->wl_seat != NULL); - if (seat->m->is_starting_up) - return; - if (seat->m->status_manager == NULL) return; + if (seat->status != NULL) { + zriver_seat_status_v1_destroy(seat->status); + seat->status = NULL; + } + seat->status = zriver_status_manager_v1_get_river_seat_status( seat->m->status_manager, seat->wl_seat); @@ -542,7 +553,9 @@ handle_global(void *data, struct wl_registry *registry, mtx_lock(&m->mod->lock); tll_push_back(m->outputs, output); - instantiate_output(&tll_back(m->outputs)); + update_output(&tll_back(m->outputs)); + tll_foreach(m->seats, it) + update_seat(&it->item); mtx_unlock(&m->mod->lock); } @@ -556,7 +569,7 @@ handle_global(void *data, struct wl_registry *registry, mtx_lock(&m->mod->lock); tll_foreach(m->outputs, it) - instantiate_output(&it->item); + update_output(&it->item); mtx_unlock(&m->mod->lock); } @@ -576,7 +589,7 @@ handle_global(void *data, struct wl_registry *registry, struct seat *seat = &tll_back(m->seats); wl_seat_add_listener(wl_seat, &seat_listener, seat); - instantiate_seat(seat); + update_seat(seat); mtx_unlock(&m->mod->lock); } @@ -590,9 +603,9 @@ handle_global(void *data, struct wl_registry *registry, mtx_lock(&m->mod->lock); tll_foreach(m->outputs, it) - instantiate_output(&it->item); + update_output(&it->item); tll_foreach(m->seats, it) - instantiate_seat(&it->item); + update_seat(&it->item); mtx_unlock(&m->mod->lock); } } @@ -637,7 +650,6 @@ run(struct module *mod) int ret = 1; struct wl_display *display = NULL; struct wl_registry *registry = NULL; - bool unlock_at_exit = false; if ((display = wl_display_connect(NULL)) == NULL) { LOG_ERR("no Wayland compositor running?"); @@ -660,19 +672,6 @@ run(struct module *mod) wl_display_roundtrip(display); - mtx_lock(&mod->lock); - unlock_at_exit = true; - - m->is_starting_up = false; - - tll_foreach(m->outputs, it) - instantiate_output(&it->item); - tll_foreach(m->seats, it) - instantiate_seat(&it->item); - - unlock_at_exit = false; - mtx_unlock(&mod->lock); - while (true) { wl_display_flush(display); @@ -719,9 +718,6 @@ out: wl_registry_destroy(registry); if (display != NULL) wl_display_disconnect(display); - - if (unlock_at_exit) - mtx_unlock(&mod->lock); return ret; } @@ -732,7 +728,6 @@ river_new(struct particle *template, struct particle *title, bool all_monitors) m->template = template; m->title = title; m->all_monitors = all_monitors; - m->is_starting_up = true; struct module *mod = module_common_new(); mod->private = m; From 87854fa1015d8ac8aed5b167cc6d26a478910500 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Gibrowski=20Fa=C3=A9?= Date: Sun, 24 Jul 2022 15:10:09 -0300 Subject: [PATCH 397/611] float tag: let user specify number of decimals Closes #200 --- CHANGELOG.md | 2 ++ doc/yambar-tags.5.scd | 6 +++++- tag.c | 32 ++++++++++++++++++++++++++++---- 3 files changed, 35 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ade384d..e7e922a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ ### Added +* Support for specifying number of decimals when printing a float tag ([#200][200]). * Support for custom font fallbacks ([#153][153]). * overline: new decoration ([#153][153]). * i3/sway: boolean option `strip-workspace-numbers`. @@ -31,6 +32,7 @@ [153]: https://codeberg.org/dnkl/yambar/issues/153 [159]: https://codeberg.org/dnkl/yambar/issues/159 +[200]: https://codeberg.org/dnkl/yambar/issues/200 [202]: https://codeberg.org/dnkl/yambar/issues/202 [219]: https://codeberg.org/dnkl/yambar/pulls/219 [228]: https://codeberg.org/dnkl/yambar/pulls/228 diff --git a/doc/yambar-tags.5.scd b/doc/yambar-tags.5.scd index c4d5408..a8bb2ce 100644 --- a/doc/yambar-tags.5.scd +++ b/doc/yambar-tags.5.scd @@ -59,7 +59,11 @@ be used. [[ *Formatter* :[ *Kind* :[ *Description* -:[ *Applies to*] +:[ *Applies to* +| . +: format +: How many decimals to print +: Float tags | hex : format : Renders a tag's value in hex diff --git a/tag.c b/tag.c index fc09bdc..4e72c2e 100644 --- a/tag.c +++ b/tag.c @@ -2,7 +2,9 @@ #include #include #include +#include #include +#include #include #define LOG_MODULE "tag" @@ -425,6 +427,16 @@ sbuf_append(struct sbuf *s1, const char *s2) sbuf_append_at_most(s1, s2, strlen(s2)); } +bool +is_number(const char *str) { + while (*str != '\0') { + if (!isdigit(*str)) + return false; + ++str; + } + return true; +} + char * tags_expand_template(const char *template, const struct tag_set *tags) { @@ -456,7 +468,7 @@ tags_expand_template(const char *template, const struct tag_set *tags) strncpy(tag_name_and_arg, begin + 1, end - begin - 1); tag_name_and_arg[end - begin - 1] = '\0'; - static const size_t MAX_TAG_ARGS = 3; + static const size_t MAX_TAG_ARGS = 4; const char *tag_name = NULL; const char *tag_args[MAX_TAG_ARGS]; memset(tag_args, 0, sizeof(tag_args)); @@ -507,6 +519,9 @@ tags_expand_template(const char *template, const struct tag_set *tags) VALUE_UNIT, } kind = VALUE_VALUE; + int decimals = 2; + char *float_fmt_end; + for (size_t i = 0; i < MAX_TAG_ARGS; i++) { if (tag_args[i] == NULL) break; @@ -534,6 +549,8 @@ tags_expand_template(const char *template, const struct tag_set *tags) kind = VALUE_MAX; else if (strcmp(tag_args[i], "unit") == 0) kind = VALUE_UNIT; + else if (tag_args[i][0] == '.' && is_number(tag_args[i] + 1)) + decimals = strtol(tag_args[i] + 1, &float_fmt_end, 10); else LOG_WARN("invalid tag formatter: %s", tag_args[i]); } @@ -542,9 +559,16 @@ tags_expand_template(const char *template, const struct tag_set *tags) switch (kind) { case VALUE_VALUE: switch (format) { - case FMT_DEFAULT: - sbuf_append(&formatted, tag->as_string(tag)); + case FMT_DEFAULT: { + if (tag->type(tag) == TAG_TYPE_FLOAT){ + char str[24]; + snprintf(str, sizeof(str), "%.*f", decimals, tag->as_float(tag)); + sbuf_append(&formatted, str); + } else { + sbuf_append(&formatted, tag->as_string(tag)); + } break; + } case FMT_HEX: case FMT_OCT: { @@ -583,7 +607,7 @@ tags_expand_template(const char *template, const struct tag_set *tags) char str[24]; if (tag->type(tag) == TAG_TYPE_FLOAT) - snprintf(str, sizeof(str), "%.2f", tag->as_float(tag) / (double)divider); + snprintf(str, sizeof(str), "%.*f", decimals, tag->as_float(tag) / (double)divider); else snprintf(str, sizeof(str), "%lu", tag->as_int(tag) / divider); sbuf_append(&formatted, str); From 463b39b56dbbbb50f97dcecd0229c2f2391ad656 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 9 Dec 2022 15:25:29 +0100 Subject: [PATCH 398/611] changelog: line-wrap long line --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e7e922a..08ebdc4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,8 @@ ### Added -* Support for specifying number of decimals when printing a float tag ([#200][200]). +* Support for specifying number of decimals when printing a float tag + ([#200][200]). * Support for custom font fallbacks ([#153][153]). * overline: new decoration ([#153][153]). * i3/sway: boolean option `strip-workspace-numbers`. From 4a41d4296adac6a7ac8b820491cfedcf70def1cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Gibrowski=20Fa=C3=A9?= Date: Tue, 10 May 2022 22:04:26 -0300 Subject: [PATCH 399/611] Implement '&&' and '||' operators on map '-' is a valid character for tags. Commit 03e1c7d (module/network: Add link stats, 2022-04-30) introduced two new tags for the network module: `ul-speed` and `dl-speed`. These use the `-` character, that was previously never used in any tag. We had two options: either change those tags to use `_` instead, or just accept `-`s as a valid character. Going forward, I can see many people deciding to name their tags with `-` instead of `_`, so I believe it is better to just accept it once and for all. Note that `-` cannot be used as the first character of a tag (e.g. `-tag1`) since the `-` has a special meaning in `.yml` files. I don't believe this will happen often, however, and should be easy to both detect and correct if it does. --- .woodpecker.yml | 2 + CHANGELOG.md | 14 ++ doc/yambar-particles.5.scd | 39 +++- examples/configurations/laptop.conf | 8 +- examples/configurations/river-tags.conf | 18 +- particles/map.c | 264 +++++------------------- particles/map.h | 37 ++++ particles/map.l | 25 +++ particles/map.y | 125 +++++++++++ particles/meson.build | 24 ++- 10 files changed, 330 insertions(+), 226 deletions(-) create mode 100644 particles/map.h create mode 100644 particles/map.l create mode 100644 particles/map.y diff --git a/.woodpecker.yml b/.woodpecker.yml index 4ea84f1..aa7ce65 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -40,6 +40,7 @@ pipeline: - apk add json-c-dev libmpdclient-dev alsa-lib-dev - apk add ttf-dejavu - apk add git + - apk add flex bison # Debug - apk add gcovr @@ -97,6 +98,7 @@ pipeline: - apk add json-c-dev libmpdclient-dev alsa-lib-dev - apk add ttf-dejavu - apk add git + - apk add flex bison # Debug - mkdir -p bld/debug-x86 diff --git a/CHANGELOG.md b/CHANGELOG.md index 08ebdc4..a6ae5e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -87,6 +87,20 @@ ... ``` + Note that if `` contains any non-alphanumerical characters, + it **must** be surrounded by `""`: + + `State == "very confused!!!"` + + Finally, you can mix and match conditions using the boolean + operators `&&` and `||`: + + ``` + && + && ( || ) # parenthesis work + ~( && ) # '~' can be applied to any condition + ``` + For a more thorough explanation, see the updated map section in the man page for yambar-particles([#137][137] and [#175][175]). diff --git a/doc/yambar-particles.5.scd b/doc/yambar-particles.5.scd index 1b6059d..c04bf93 100644 --- a/doc/yambar-particles.5.scd +++ b/doc/yambar-particles.5.scd @@ -218,11 +218,15 @@ content: This particle maps the values of a specific tag to different particles based on conditions. A condition takes either the form of: +``` +``` Or, for boolean tags: +``` +``` Where is the tag you would like to map, is one of: @@ -233,15 +237,46 @@ Where is the tag you would like to map, is one of: :- <= :- < -and is the value you would like to compare it to. +and is the value you would like to compare it to. *If the +value contains any non-alphanumerical characters, you must +surround it with ' " ' *: -For boolean tags, negation is done with a preceding '~': +``` +"hello world" +"@#$%" +``` +Negation is done with a preceding '~': + +``` ~ +~ +``` To match for empty strings, use ' "" ': +``` == "" +``` + +Furthermore, you may use the boolean operators: + +[- && +:- || + +in order to create more complex conditions: + +``` + && +``` + +You may surround with parenthesis for clarity or +specifying precedence: + +``` +() + && ( || ) +``` In addition to explicit tag values, you can also specify a default/fallback particle. diff --git a/examples/configurations/laptop.conf b/examples/configurations/laptop.conf index 85f43ad..96383ca 100644 --- a/examples/configurations/laptop.conf +++ b/examples/configurations/laptop.conf @@ -178,11 +178,7 @@ bar: map: default: {string: {text: , font: *awesome, foreground: ffffff66}} conditions: - state == up: - map: - default: {string: {text: , font: *awesome}} - conditions: - ipv4 == "": {string: {text: , font: *awesome, foreground: ffffff66}} + state == up && ipv4 != "": {string: {text: , font: *awesome}} - network: name: wlp2s0 poll-interval: 1 @@ -257,7 +253,7 @@ bar: state == full: - string: {text: , foreground: 00ff00ff, font: *awesome} - string: {text: "{capacity}% full"} - state == not charging: + state == "not charging": - ramp: tag: capacity items: diff --git a/examples/configurations/river-tags.conf b/examples/configurations/river-tags.conf index 462a329..54289e6 100644 --- a/examples/configurations/river-tags.conf +++ b/examples/configurations/river-tags.conf @@ -41,16 +41,18 @@ bar: map: <<: *river_base deco: *bg_default - state == visible: + state == visible && ~occupied: map: - conditions: - ~occupied: {map: {<<: *river_base}} - occupied: {map: {<<: *river_base, deco: *bg_default}} + <<: *river_base + state == visible && occupied: + map: + <<: *river_base + deco: *bg_default state == unfocused: map: <<: *river_base - state == invisible: + state == invisible && ~occupied: {empty: {}} + state == invisible && occupied: map: - conditions: - ~occupied: {empty: {}} - occupied: {map: {<<: *river_base, deco: {underline: {size: 3, color: ea6962ff}}}} + <<: *river_base + deco: {underline: {size: 3, color: ea6962ff}} diff --git a/particles/map.c b/particles/map.c index c748bea..82a1ee0 100644 --- a/particles/map.c +++ b/particles/map.c @@ -11,38 +11,7 @@ #include "../plugin.h" #include "dynlist.h" -enum map_op{ - MAP_OP_EQ, - MAP_OP_NE, - MAP_OP_LE, - MAP_OP_LT, - MAP_OP_GE, - MAP_OP_GT, - /* The next two are for bool types */ - MAP_OP_SELF, - MAP_OP_NOT, -}; - -struct map_condition { - char *tag; - enum map_op op; - char *value; -}; - -static char * -trim(char *s) -{ - while (*s == ' ') - s++; - - char *end = s + strlen(s) - 1; - while (*end == ' ') { - *end = '\0'; - end--; - } - - return s; -} +#include "map.h" bool int_condition(const long tag_value, const long cond_value, enum map_op op) @@ -54,6 +23,7 @@ int_condition(const long tag_value, const long cond_value, enum map_op op) case MAP_OP_LT: return tag_value < cond_value; case MAP_OP_GE: return tag_value >= cond_value; case MAP_OP_GT: return tag_value > cond_value; + case MAP_OP_SELF: LOG_WARN("using int tag as bool"); default: return false; } } @@ -68,6 +38,7 @@ float_condition(const double tag_value, const double cond_value, enum map_op op) case MAP_OP_LT: return tag_value < cond_value; case MAP_OP_GE: return tag_value >= cond_value; case MAP_OP_GT: return tag_value > cond_value; + case MAP_OP_SELF: LOG_WARN("using float tag as bool"); default: return false; } } @@ -82,12 +53,13 @@ str_condition(const char* tag_value, const char* cond_value, enum map_op op) case MAP_OP_LT: return strcmp(tag_value, cond_value) < 0; case MAP_OP_GE: return strcmp(tag_value, cond_value) >= 0; case MAP_OP_GT: return strcmp(tag_value, cond_value) > 0; + case MAP_OP_SELF: LOG_WARN("using String tag as bool"); default: return false; } } bool -eval_map_condition(const struct map_condition* map_cond, const struct tag_set *tags) +eval_comparison(const struct map_condition* map_cond, const struct tag_set *tags) { const struct tag *tag = tag_for_name(tags, map_cond->tag); if (tag == NULL) { @@ -131,8 +103,6 @@ eval_map_condition(const struct map_condition* map_cond, const struct tag_set *t case TAG_TYPE_BOOL: if (map_cond->op == MAP_OP_SELF) return tag->as_bool(tag); - else if (map_cond->op == MAP_OP_NOT) - return !tag->as_bool(tag); else { LOG_WARN("boolean tag '%s' should be used directly", map_cond->tag); return false; @@ -145,86 +115,43 @@ eval_map_condition(const struct map_condition* map_cond, const struct tag_set *t return false; } -struct map_condition* -map_condition_from_str(const char *str) +bool +eval_map_condition(const struct map_condition* map_cond, const struct tag_set *tags) { - struct map_condition *cond = malloc(sizeof(*cond)); - - /* This strdup is just to discard the 'const' qualifier */ - char *str_cpy = strdup(str); - char *op_str = strpbrk(str_cpy, "=!<>~"); - - /* we've already checked things in the verify functions, so these should all work */ - char *tag = str_cpy; - char *value = NULL; - - if (op_str == NULL) { - cond->tag = strdup(trim(tag)); - cond->op = MAP_OP_SELF; - cond->value = NULL; - free(str_cpy); - return cond; + switch(map_cond->op) + { + case MAP_OP_NOT: return !eval_map_condition(map_cond->cond1, tags); + case MAP_OP_AND: return eval_map_condition(map_cond->cond1, tags) && eval_map_condition(map_cond->cond2, tags); + case MAP_OP_OR: return eval_map_condition(map_cond->cond1, tags) || eval_map_condition(map_cond->cond2, tags); + default: return eval_comparison(map_cond, tags); } - - switch (op_str[0]) { - case '=': - cond->op = MAP_OP_EQ; - value = op_str + 2; - break; - case '!': - cond->op = MAP_OP_NE; - value = op_str + 2; - break; - case '<': - if (op_str[1] == '=') { - cond->op = MAP_OP_LE; - value = op_str + 2; - } else { - cond->op = MAP_OP_LT; - value = op_str + 1; - } - break; - case '>': - if (op_str[1] == '=') { - cond->op = MAP_OP_GE; - value = op_str + 2; - } else { - cond->op = MAP_OP_GT; - value = op_str + 1; - } - break; - case '~': - tag = op_str + 1; - cond->op = MAP_OP_NOT; - break; - } - - /* NULL terminate the tag value */ - op_str[0] = '\0'; - - cond->tag = strdup(trim(tag)); - - cond->value = NULL; - if (value != NULL) { - value = trim(value); - const size_t value_len = strlen(value); - if (value[0] == '"' && value[value_len - 1] == '"') { - value[value_len - 1] = '\0'; - ++value; - } - cond->value = strdup(value); - } - - free(str_cpy); - return cond; } void -free_map_condition(struct map_condition* mc) +free_map_condition(struct map_condition* c) { - free(mc->tag); - free(mc->value); - free(mc); + switch (c->op) + { + case MAP_OP_EQ: + case MAP_OP_NE: + case MAP_OP_LE: + case MAP_OP_LT: + case MAP_OP_GE: + case MAP_OP_GT: + free(c->value); + /* FALLTHROUGH */ + case MAP_OP_SELF: + free(c->tag); + break; + case MAP_OP_AND: + case MAP_OP_OR: + free_map_condition(c->cond2); + /* FALLTHROUGH */ + case MAP_OP_NOT: + free_map_condition(c->cond1); + break; + } + free(c); } struct particle_map { @@ -379,101 +306,6 @@ map_new(struct particle *common, const struct particle_map particle_map[], return common; } -static bool -verify_map_condition_syntax(keychain_t *chain, const struct yml_node *node, - const char *line) -{ - /* We need this strdup to discard the 'const' qualifier */ - char *cond = strdup(line); - char *op_str = strpbrk(cond, " =!<>~"); - if (op_str == NULL) { - char *tag = trim(cond); - if (tag[0] == '\0') - goto syntax_fail_missing_tag; - - free(cond); - return true; - } - op_str = trim(op_str); - - char *tag = cond; - char *value = NULL; - - switch (op_str[0]) { - case '=': - if (op_str[1] != '=') - goto syntax_fail_invalid_op; - - value = op_str + 2; - break; - - case '!': - if (op_str[1] != '=') - goto syntax_fail_invalid_op; - - value = op_str + 2; - break; - - case '<': - if (op_str[1] == '=') - value = op_str + 2; - else - value = op_str + 1; - break; - - case '>': - if (op_str[1] == '=') - value = op_str + 2; - else - value = op_str + 1; - break; - - case '~': - tag = op_str + 1; - if (strpbrk(tag, " =!<>~") != NULL) - goto syntax_fail_bad_not; - value = NULL; - break; - default: - goto syntax_fail_invalid_op; - } - - /* NULL terminate the tag value */ - op_str[0] = '\0'; - - tag = trim(tag); - if (tag[0] == '\0') - goto syntax_fail_missing_tag; - - value = value != NULL ? trim(value) : NULL; - - if (value != NULL && value[0] == '\0') - goto syntax_fail_missing_value; - - free(cond); - return true; - -syntax_fail_invalid_op: - LOG_ERR("%s: \"%s\" invalid operator", conf_err_prefix(chain, node), line); - goto err; - -syntax_fail_missing_tag: - LOG_ERR("%s: \"%s\" missing tag", conf_err_prefix(chain, node), line); - goto err; - -syntax_fail_missing_value: - LOG_ERR("%s: \"%s\" missing value", conf_err_prefix(chain, node), line); - goto err; - -syntax_fail_bad_not: - LOG_ERR("%s: \"%s\" '~' cannot be used with other operators", - conf_err_prefix(chain, node), line); - goto err; - -err: - free(cond); - return false; -} static bool verify_map_conditions(keychain_t *chain, const struct yml_node *node) @@ -485,6 +317,7 @@ verify_map_conditions(keychain_t *chain, const struct yml_node *node) return false; } + bool result = true; for (struct yml_dict_iter it = yml_dict_iter(node); it.key != NULL; yml_dict_next(&it)) @@ -494,9 +327,16 @@ verify_map_conditions(keychain_t *chain, const struct yml_node *node) LOG_ERR("%s: key must be a string", conf_err_prefix(chain, it.key)); return false; } - - if (!verify_map_condition_syntax(chain, it.key, key)) - return false; + char *key_clone = strdup(key); + YY_BUFFER_STATE buffer = yy_scan_string(key_clone); + if (yyparse() != 0) { + LOG_ERR("%s: %s", conf_err_prefix(chain, it.key), MAP_PARSER_ERROR_MSG); + free(MAP_PARSER_ERROR_MSG); + result = false; + } else + free_map_condition(MAP_CONDITION_PARSE_RESULT); + yy_delete_buffer(buffer); + free(key_clone); if (!conf_verify_particle(chain_push(chain, key), it.value)) return false; @@ -504,7 +344,7 @@ verify_map_conditions(keychain_t *chain, const struct yml_node *node) chain_pop(chain); } - return true; + return result; } static struct particle * @@ -526,7 +366,13 @@ from_conf(const struct yml_node *node, struct particle *common) it.key != NULL; yml_dict_next(&it), idx++) { - particle_map[idx].condition = map_condition_from_str(yml_value_as_string(it.key)); + /* Note we can skip the error checking here */ + char *key_clone = strdup(yml_value_as_string(it.key)); + YY_BUFFER_STATE buffer = yy_scan_string(key_clone); + yyparse(); + particle_map[idx].condition = MAP_CONDITION_PARSE_RESULT; + yy_delete_buffer(buffer); + free(key_clone); particle_map[idx].particle = conf_to_particle(it.value, inherited); } diff --git a/particles/map.h b/particles/map.h new file mode 100644 index 0000000..a6d35b4 --- /dev/null +++ b/particles/map.h @@ -0,0 +1,37 @@ +#pragma once + +enum map_op { + MAP_OP_EQ, + MAP_OP_NE, + MAP_OP_LE, + MAP_OP_LT, + MAP_OP_GE, + MAP_OP_GT, + MAP_OP_SELF, + MAP_OP_NOT, + + MAP_OP_AND, + MAP_OP_OR, +}; + +struct map_condition { + union { + char *tag; + struct map_condition *cond1; + }; + enum map_op op; + union { + char *value; + struct map_condition *cond2; + }; +}; + +void free_map_condition(struct map_condition *c); + +typedef struct yy_buffer_state* YY_BUFFER_STATE; +YY_BUFFER_STATE yy_scan_string(const char *str); +int yyparse(); +void yy_delete_buffer(YY_BUFFER_STATE buffer); + +extern struct map_condition *MAP_CONDITION_PARSE_RESULT; +extern char *MAP_PARSER_ERROR_MSG; diff --git a/particles/map.l b/particles/map.l new file mode 100644 index 0000000..7a5ebc8 --- /dev/null +++ b/particles/map.l @@ -0,0 +1,25 @@ +%{ +#include +#include "map.h" +#include "map.tab.h" +%} + +%option warn nodefault nounput noinput noyywrap + +%% +[[:alnum:]_-]+ yylval.str = strdup(yytext); return WORD; +\".*\" yylval.str = strndup(yytext + 1, strlen(yytext) - 2); return STRING; +== yylval.op = MAP_OP_EQ; return CMP_OP; +!= yylval.op = MAP_OP_NE; return CMP_OP; +\<= yylval.op = MAP_OP_LE; return CMP_OP; +\< yylval.op = MAP_OP_LT; return CMP_OP; +>= yylval.op = MAP_OP_GE; return CMP_OP; +> yylval.op = MAP_OP_GT; return CMP_OP; +&& yylval.op = MAP_OP_AND; return BOOL_OP; +\|\| yylval.op = MAP_OP_OR; return BOOL_OP; +~ return NOT; +\( return L_PAR; +\) return R_PAR; +[ \t\n] ; +. yylval.str = strdup(yytext); return STRING; +%% diff --git a/particles/map.y b/particles/map.y new file mode 100644 index 0000000..ee426da --- /dev/null +++ b/particles/map.y @@ -0,0 +1,125 @@ +%{ +#include +#include + +#include "map.h" + +struct map_condition *MAP_CONDITION_PARSE_RESULT; +char *MAP_PARSER_ERROR_MSG; + +static const int NUM_TOKENS = 7; +int yylex(); +void yyerror(const char *str); +%} + +%define parse.lac full +%define parse.error custom + +%union { + char *str; + struct map_condition *condition; + enum map_op op; +} + +%token WORD STRING CMP_OP L_PAR R_PAR +%left BOOL_OP +%precedence NOT + +%destructor { free_map_condition($$); } condition +%destructor { free($$); } WORD +%destructor { free($$); } STRING + +%% +result: condition { MAP_CONDITION_PARSE_RESULT = $1; }; + +condition: + WORD { + $$ = malloc(sizeof(struct map_condition)); + $$->tag = $1; + $$->op = MAP_OP_SELF; + } + | + WORD CMP_OP WORD { + $$ = malloc(sizeof(struct map_condition)); + $$->tag = $1; + $$->op = $2; + $$->value = $3; + } + | + WORD CMP_OP STRING { + $$ = malloc(sizeof(struct map_condition)); + $$->tag = $1; + $$->op = $2; + $$->value = $3; + } + | + L_PAR condition R_PAR { $$ = $2; } + | + NOT condition { + $$ = malloc(sizeof(struct map_condition)); + $$->cond1 = $2; + $$->op = MAP_OP_NOT; + } + | + condition BOOL_OP condition { + $$ = malloc(sizeof(struct map_condition)); + $$->cond1 = $1; + $$->op = $2; + $$->cond2 = $3; + } + ; +%% + +void yyerror(const char *str) +{ + fprintf(stderr, "error: %s\n", str); +} + +static char const* +token_to_str(yysymbol_kind_t tkn) +{ + switch (tkn) { + case YYSYMBOL_CMP_OP: return "==, !=, <=, <, >=, >"; + case YYSYMBOL_BOOL_OP: return "||, &&"; + case YYSYMBOL_L_PAR: return "("; + case YYSYMBOL_R_PAR: return ")"; + case YYSYMBOL_NOT: return "~"; + default: return yysymbol_name(tkn); + } +} + +static int +yyreport_syntax_error (const yypcontext_t *ctx) +{ + int res = 0; + char *errmsg = malloc(1024); + errmsg[0] = '\0'; + + // Report the tokens expected at this point. + yysymbol_kind_t expected[NUM_TOKENS]; + int n = yypcontext_expected_tokens(ctx, expected, NUM_TOKENS); + if (n < 0) + res = n; // Forward errors to yyparse. + else { + for (int i = 0; i < n; ++i) { + strcat(errmsg, i == 0 ? "expected [" : ", "); + strcat(errmsg, token_to_str(expected[i])); + } + strcat(errmsg, "]"); + } + + // Report the unexpected token. + yysymbol_kind_t lookahead = yypcontext_token(ctx); + if (lookahead != YYSYMBOL_YYEMPTY) { + strcat(errmsg, ", found "); + if (!(lookahead == YYSYMBOL_STRING || lookahead == YYSYMBOL_WORD)) + strcat(errmsg, yysymbol_name(lookahead)); + else if (yylval.str != NULL) + strcat(errmsg, yylval.str); + else + strcat(errmsg, "nothing"); + } + + MAP_PARSER_ERROR_MSG = errmsg; + return res; +} diff --git a/particles/meson.build b/particles/meson.build index 6b31081..091f551 100644 --- a/particles/meson.build +++ b/particles/meson.build @@ -1,3 +1,25 @@ +flex = find_program('flex', required: true) +bison = find_program('bison', required: true) + +lgen = generator( + flex, + output : '@BASENAME@.yy.c', + arguments : ['-o', '@OUTPUT@', '@INPUT@'] +) +lfiles = lgen.process('map.l') + +pgen = generator( + bison, + output : ['@BASENAME@.tab.c', '@BASENAME@.tab.h'], + arguments : ['-Wall', + '-Wcounterexamples', + '--defines=@OUTPUT1@', + '--output=@OUTPUT0@', '@INPUT@'] +) +pfiles = pgen.process('map.y') + +map_parser = declare_dependency(sources: [pfiles, lfiles], include_directories: '.') + particle_sdk = declare_dependency(dependencies: [pixman, tllist, fcft]) dynlist_lib = build_target( @@ -14,7 +36,7 @@ dynlist = declare_dependency(link_with: dynlist_lib) deps = { 'empty': [], 'list': [], - 'map': [dynlist], + 'map': [dynlist, map_parser], 'progress-bar': [], 'ramp': [], 'string': [], From 3c3a881638c04dad83c6cfef912c4c417747b4f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 11 Dec 2022 18:32:52 +0100 Subject: [PATCH 400/611] ci (gitlab): add flex+bison --- .gitlab-ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index be21159..401fc0e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -16,6 +16,7 @@ before_script: - apk add json-c-dev libmpdclient-dev alsa-lib-dev - apk add ttf-dejavu - apk add git + - apk add flex bison versions: stage: info From ec86a7d2909daad0731bd1b469014fdb10fc514b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 11 Dec 2022 18:33:07 +0100 Subject: [PATCH 401/611] ci (sr.ht): add flex+bison --- .builds/alpine-x64.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.builds/alpine-x64.yml b/.builds/alpine-x64.yml index 931c21d..a070121 100644 --- a/.builds/alpine-x64.yml +++ b/.builds/alpine-x64.yml @@ -23,6 +23,8 @@ packages: - gcovr - python3 - py3-pip + - flex + - bison sources: - https://git.sr.ht/~dnkl/yambar From f9fa43845e5f100da5c567482f5d495b57e7ae01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 11 Dec 2022 18:44:24 +0100 Subject: [PATCH 402/611] =?UTF-8?q?changelog:=20=E2=80=98map=E2=80=99=20ch?= =?UTF-8?q?anges:=20add=20ref=20to=20182?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a6ae5e8..a688c90 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -102,10 +102,11 @@ ``` For a more thorough explanation, see the updated map section in the - man page for yambar-particles([#137][137] and [#175][175]). + man page for yambar-particles([#137][137], [#175][175] and [#][182]). [137]: https://codeberg.org/dnkl/yambar/issues/137 [175]: https://codeberg.org/dnkl/yambar/issues/172 +[182]: https://codeberg.org/dnkl/yambar/issues/182 [191]: https://codeberg.org/dnkl/yambar/issues/191 [202]: https://codeberg.org/dnkl/yambar/issues/202 From 54c70bb6ad22af660aa4b12a91a8ebaa1aebeff7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 12 Dec 2022 16:53:10 +0100 Subject: [PATCH 403/611] readme: add missing modules to list of modules --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 5251119..b95299f 100644 --- a/README.md +++ b/README.md @@ -78,8 +78,12 @@ Available modules: * backlight * battery * clock +* cpu +* disk-io +* foreign-toplevel * i3 (and Sway) * label +* mem * mpd * network * removables From dcf21f0b06d2cff94bd253cc4f6377f45c220e97 Mon Sep 17 00:00:00 2001 From: Willem van de Krol Date: Thu, 28 Jul 2022 17:20:30 +0200 Subject: [PATCH 404/611] modules: add pulse The pulse module shows information about PulseAudio sinks and sources. --- .builds/alpine-x64.yml | 1 + .gitlab-ci.yml | 2 +- .woodpecker.yml | 4 +- CHANGELOG.md | 4 +- PKGBUILD | 3 +- PKGBUILD.wayland-only | 1 + README.md | 1 + doc/meson.build | 1 + doc/yambar-modules-pulse.5.scd | 67 ++++ doc/yambar-modules.5.scd | 2 + meson.build | 8 +- meson_options.txt | 3 + modules/meson.build | 7 + modules/pulse.c | 550 +++++++++++++++++++++++++++++++++ plugin.c | 6 + 15 files changed, 653 insertions(+), 7 deletions(-) create mode 100644 doc/yambar-modules-pulse.5.scd create mode 100644 modules/pulse.c diff --git a/.builds/alpine-x64.yml b/.builds/alpine-x64.yml index a070121..926dc46 100644 --- a/.builds/alpine-x64.yml +++ b/.builds/alpine-x64.yml @@ -18,6 +18,7 @@ packages: - wlroots-dev - json-c-dev - libmpdclient-dev + - libpulse - alsa-lib-dev - ttf-dejavu - gcovr diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 401fc0e..155979d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -13,7 +13,7 @@ before_script: - apk add pixman-dev freetype-dev fontconfig-dev - apk add libxcb-dev xcb-util-wm-dev xcb-util-cursor-dev yaml-dev - apk add wayland-dev wayland-protocols wlroots-dev - - apk add json-c-dev libmpdclient-dev alsa-lib-dev + - apk add json-c-dev libmpdclient-dev alsa-lib-dev pulseaudio-dev - apk add ttf-dejavu - apk add git - apk add flex bison diff --git a/.woodpecker.yml b/.woodpecker.yml index aa7ce65..0a5117a 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -37,7 +37,7 @@ pipeline: - apk add pixman-dev freetype-dev fontconfig-dev - apk add libxcb-dev xcb-util-wm-dev xcb-util-cursor-dev yaml-dev - apk add wayland-dev wayland-protocols wlroots-dev - - apk add json-c-dev libmpdclient-dev alsa-lib-dev + - apk add json-c-dev libmpdclient-dev alsa-lib-dev pulseaudio-dev - apk add ttf-dejavu - apk add git - apk add flex bison @@ -95,7 +95,7 @@ pipeline: - apk add pixman-dev freetype-dev fontconfig-dev - apk add libxcb-dev xcb-util-wm-dev xcb-util-cursor-dev yaml-dev - apk add wayland-dev wayland-protocols wlroots-dev - - apk add json-c-dev libmpdclient-dev alsa-lib-dev + - apk add json-c-dev libmpdclient-dev alsa-lib-dev pulseaudio-dev - apk add ttf-dejavu - apk add git - apk add flex bison diff --git a/CHANGELOG.md b/CHANGELOG.md index a688c90..73ff4e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,7 +27,8 @@ * network: request link stats and expose under tags `dl-speed` and `ul-speed` when `poll-interval` is set. * new module: disk-io. -* alsa: `dB` tag ([#202][202]) +* new module: pulse ([#223][223]). +* alsa: `dB` tag ([#202][202]). * mpd: `file` tag ([#219][219]). * on-click: support `next`/`previous` mouse buttons ([#228][228]). @@ -36,6 +37,7 @@ [200]: https://codeberg.org/dnkl/yambar/issues/200 [202]: https://codeberg.org/dnkl/yambar/issues/202 [219]: https://codeberg.org/dnkl/yambar/pulls/219 +[223]: https://codeberg.org/dnkl/yambar/pulls/223 [228]: https://codeberg.org/dnkl/yambar/pulls/228 diff --git a/PKGBUILD b/PKGBUILD index 13ddc83..b675823 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -1,5 +1,5 @@ pkgname=yambar -pkgver=1.8.0 +pkgver=1.8.0.r77.ge9a6994 pkgrel=1 pkgdesc="Simplistic and highly configurable status panel for X and Wayland" arch=('x86_64' 'aarch64') @@ -15,6 +15,7 @@ depends=( 'libudev.so' 'json-c' 'libmpdclient' + 'libpulse' 'fcft>=3.0.0' 'fcft<4.0.0') optdepends=('xcb-util-errors: better X error messages') source=() diff --git a/PKGBUILD.wayland-only b/PKGBUILD.wayland-only index 5dc6cfd..e836093 100644 --- a/PKGBUILD.wayland-only +++ b/PKGBUILD.wayland-only @@ -16,6 +16,7 @@ depends=( 'libudev.so' 'json-c' 'libmpdclient' + 'libpulse' 'fcft>=3.0.0' 'fcft<4.0.0') source=() diff --git a/README.md b/README.md index b95299f..e4b497d 100644 --- a/README.md +++ b/README.md @@ -86,6 +86,7 @@ Available modules: * mem * mpd * network +* pulse * removables * river * script (see script [examples](examples/scripts)) diff --git a/doc/meson.build b/doc/meson.build index 81188bf..a956dbc 100644 --- a/doc/meson.build +++ b/doc/meson.build @@ -10,6 +10,7 @@ foreach man_src : ['yambar.1.scd', 'yambar.5.scd', 'yambar-decorations.5.scd', 'yambar-modules-foreign-toplevel.5.scd', 'yambar-modules-i3.5.scd', 'yambar-modules-label.5.scd', 'yambar-modules-mpd.5.scd', 'yambar-modules-network.5.scd', + 'yambar-modules-pulse.5.scd', 'yambar-modules-removables.5.scd', 'yambar-modules-river.5.scd', 'yambar-modules-script.5.scd', 'yambar-modules-sway-xkb.5.scd', 'yambar-modules-sway.5.scd', 'yambar-modules-xkb.5.scd', diff --git a/doc/yambar-modules-pulse.5.scd b/doc/yambar-modules-pulse.5.scd new file mode 100644 index 0000000..95df59a --- /dev/null +++ b/doc/yambar-modules-pulse.5.scd @@ -0,0 +1,67 @@ +yambar-modules-pulse(5) + +# NAME +pulse - Monitors a PulseAudio source and/or sink + +# TAGS + +[[ *Name* +:[ *Type* +:[ *Description* +| online +: bool +: True when connected to the PulseAudio server +| sink_online +: bool +: True when the sink is present +| source_online +: bool +: True when the source is present +| sink_percent +: range +: Sink volume level, as a percentage +| source_percent +: range +: Source volume level, as a percentage +| sink_muted +: bool +: True if the sink is muted, otherwise false +| source_muted +: bool +: True if the source is muted, otherwise false +| sink_port +: string +: Description of the active sink port +| source_port +: string +: Description of the active source port + +# CONFIGURATION + +[[ *Name* +:[ *Type* +:[ *Req* +:[ *Description* +| sink +: string +: no +: Name of sink to monitor (default: _@DEFAULT\_SINK@_). +| source +: string +: no +: Name of source to monitor (default: _@DEFAULT\_SOURCE@_). + +# EXAMPLES + +``` +bar: + left: + - pulse: + content: + string: {text: "{sink_percent}% ({sink_port})"} +``` + +# SEE ALSO + +*yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5) + diff --git a/doc/yambar-modules.5.scd b/doc/yambar-modules.5.scd index ef47f62..5767a52 100644 --- a/doc/yambar-modules.5.scd +++ b/doc/yambar-modules.5.scd @@ -150,6 +150,8 @@ Available modules have their own pages: *yambar-modules-network*(5) +*yambar-modules-pulse*(5) + *yambar-modules-removables*(5) *yambar-modules-river*(5) diff --git a/meson.build b/meson.build index 74915df..52f75bb 100644 --- a/meson.build +++ b/meson.build @@ -131,7 +131,8 @@ yambar = executable( version, dependencies: [bar, libepoll, libinotify, pixman, yaml, threads, dl, tllist, fcft] + decorations + particles + modules, - c_args: [plugin_mpd_enabled? '-DPLUGIN_ENABLED_MPD':[]], + c_args: [plugin_mpd_enabled? '-DPLUGIN_ENABLED_MPD':[], + plugin_pulse_enabled? '-DPLUGIN_ENABLED_PULSE':[]], build_rpath: '$ORIGIN/modules:$ORIGIN/decorations:$ORIGIN/particles', export_dynamic: true, install: true, @@ -168,7 +169,10 @@ summary( ) summary( - {'Music Player Daemon (MPD)': plugin_mpd_enabled}, + { + 'Music Player Daemon (MPD)': plugin_mpd_enabled, + 'PulseAudio': plugin_pulse_enabled, + }, section: 'Optional modules', bool_yn: true ) diff --git a/meson_options.txt b/meson_options.txt index 15dc3e9..3878096 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -8,3 +8,6 @@ option( option( 'plugin-mpd', type: 'feature', value: 'auto', description: 'Music Player Daemon (MPD) support') +option( + 'plugin-pulse', type: 'feature', value: 'auto', + description: 'PulseAudio support') diff --git a/modules/meson.build b/modules/meson.build index d8fa9b5..ddeb491 100644 --- a/modules/meson.build +++ b/modules/meson.build @@ -11,6 +11,9 @@ xcb_xkb = dependency('xcb-xkb', required: get_option('backend-x11')) mpd = dependency('libmpdclient', required: get_option('plugin-mpd')) plugin_mpd_enabled = mpd.found() +pulse = dependency('libpulse', required: get_option('plugin-pulse')) +plugin_pulse_enabled = pulse.found() + # Module name -> (source-list, dep-list) mod_data = { 'alsa': [[], [m, alsa]], @@ -32,6 +35,10 @@ if plugin_mpd_enabled mod_data += {'mpd': [[], [mpd]]} endif +if plugin_pulse_enabled + mod_data += {'pulse': [[], [pulse]]} +endif + if backend_x11 mod_data += { 'xkb': [[], [xcb_stuff, xcb_xkb]], diff --git a/modules/pulse.c b/modules/pulse.c new file mode 100644 index 0000000..c4955ac --- /dev/null +++ b/modules/pulse.c @@ -0,0 +1,550 @@ +#include +#include +#include +#include +#include + +#include +#include + +#include + +#define LOG_MODULE "pulse" +#define LOG_ENABLE_DBG 0 +#include "../bar/bar.h" +#include "../config-verify.h" +#include "../config.h" +#include "../log.h" +#include "../plugin.h" + +struct private { + char *sink_name; + char *source_name; + struct particle *label; + + bool online; + + bool sink_online; + pa_cvolume sink_volume; + bool sink_muted; + char *sink_port; + uint32_t sink_index; + + bool source_online; + pa_cvolume source_volume; + bool source_muted; + char *source_port; + uint32_t source_index; + + int refresh_timer_fd; + bool refresh_scheduled; + + pa_mainloop *mainloop; + pa_context *context; +}; + +static void +destroy(struct module *mod) +{ + struct private *priv = mod->private; + priv->label->destroy(priv->label); + free(priv->sink_name); + free(priv->source_name); + free(priv->sink_port); + free(priv->source_port); + free(priv); + module_default_destroy(mod); +} + +static const char * +description(struct module *mod) +{ + return "pulse"; +} + +static struct exposable * +content(struct module *mod) +{ + struct private *priv = mod->private; + + mtx_lock(&mod->lock); + + pa_volume_t sink_volume_max = pa_cvolume_max(&priv->sink_volume); + pa_volume_t source_volume_max = pa_cvolume_max(&priv->source_volume); + int sink_percent = round(100.0 * sink_volume_max / PA_VOLUME_NORM); + int source_percent = round(100.0 * source_volume_max / PA_VOLUME_NORM); + + struct tag_set tags = { + .tags = (struct tag *[]){ + tag_new_bool(mod, "online", priv->online), + + tag_new_bool(mod, "sink_online", priv->sink_online), + tag_new_int_range(mod, "sink_percent", sink_percent, 0, 100), + tag_new_bool(mod, "sink_muted", priv->sink_muted), + tag_new_string(mod, "sink_port", priv->sink_port), + + tag_new_bool(mod, "source_online", priv->source_online), + tag_new_int_range(mod, "source_percent", source_percent, 0, 100), + tag_new_bool(mod, "source_muted", priv->source_muted), + tag_new_string(mod, "source_port", priv->source_port), + }, + .count = 9, + }; + + mtx_unlock(&mod->lock); + + struct exposable *exposable = priv->label->instantiate(priv->label, &tags); + + tag_set_destroy(&tags); + return exposable; +} + +static const char * +context_error(pa_context *c) +{ + return pa_strerror(pa_context_errno(c)); +} + +static void +abort_event_cb(pa_mainloop_api *api, + pa_io_event *event, + int fd, + pa_io_event_flags_t flags, + void *userdata) +{ + struct module *mod = userdata; + struct private *priv = mod->private; + + pa_context_disconnect(priv->context); +} + +static void +refresh_timer_cb(pa_mainloop_api *api, + pa_io_event *event, + int fd, + pa_io_event_flags_t flags, + void *userdata) +{ + struct module *mod = userdata; + struct private *priv = mod->private; + + // Drain the refresh timer. + uint64_t n; + if (read(priv->refresh_timer_fd, &n, sizeof n) < 0) + LOG_ERRNO("failed to read from timerfd"); + + // Clear the refresh flag. + priv->refresh_scheduled = false; + + // Refresh the bar. + mod->bar->refresh(mod->bar); +} + +// Refresh the bar after a small delay. Without the delay, the bar +// would be refreshed multiple times per event (e.g., a volume change), +// and sometimes the active port would be reported incorrectly for a +// brief moment. (This behavior was observed with PipeWire 0.3.61.) +static void +schedule_refresh(struct module *mod) +{ + struct private *priv = mod->private; + + // Do nothing if a refresh has already been scheduled. + if (priv->refresh_scheduled) + return; + + // Start the refresh timer. + struct itimerspec t = { + .it_interval = { .tv_sec = 0, .tv_nsec = 0 }, + .it_value = { .tv_sec = 0, .tv_nsec = 50000000 }, + }; + timerfd_settime(priv->refresh_timer_fd, 0, &t, NULL); + + // Set the refresh flag. + priv->refresh_scheduled = true; +} + +static void +set_server_online(struct module *mod) +{ + struct private *priv = mod->private; + + mtx_lock(&mod->lock); + priv->online = true; + mtx_unlock(&mod->lock); + + schedule_refresh(mod); +} + +static void +set_server_offline(struct module *mod) +{ + struct private *priv = mod->private; + + mtx_lock(&mod->lock); + priv->online = false; + priv->sink_online = false; + priv->source_online = false; + mtx_unlock(&mod->lock); + + schedule_refresh(mod); +} + +static void +set_sink_info(struct module *mod, const pa_sink_info *sink_info) +{ + struct private *priv = mod->private; + + mtx_lock(&mod->lock); + + free(priv->sink_port); + + priv->sink_online = true; + priv->sink_index = sink_info->index; + priv->sink_volume = sink_info->volume; + priv->sink_muted = sink_info->mute; + priv->sink_port = sink_info->active_port != NULL + ? strdup(sink_info->active_port->description) + : NULL; + + mtx_unlock(&mod->lock); + + schedule_refresh(mod); +} + +static void +set_sink_offline(struct module *mod) +{ + struct private *priv = mod->private; + + mtx_lock(&mod->lock); + priv->sink_online = false; + mtx_unlock(&mod->lock); + + schedule_refresh(mod); +} + +static void +set_source_info(struct module *mod, const pa_source_info *source_info) +{ + struct private *priv = mod->private; + + mtx_lock(&mod->lock); + + free(priv->source_port); + + priv->source_online = true; + priv->source_index = source_info->index; + priv->source_volume = source_info->volume; + priv->source_muted = source_info->mute; + priv->source_port = source_info->active_port != NULL + ? strdup(source_info->active_port->description) + : NULL; + + mtx_unlock(&mod->lock); + + schedule_refresh(mod); +} + +static void +set_source_offline(struct module *mod) +{ + struct private *priv = mod->private; + + mtx_lock(&mod->lock); + priv->source_online = false; + mtx_unlock(&mod->lock); + + schedule_refresh(mod); +} + +static void +sink_info_cb(pa_context *c, const pa_sink_info *i, int eol, void *userdata) +{ + struct module *mod = userdata; + + if (eol < 0) { + LOG_ERR("failed to get sink info: %s", context_error(c)); + set_sink_offline(mod); + } else if (eol == 0) { + set_sink_info(mod, i); + } +} + +static void +source_info_cb(pa_context *c, const pa_source_info *i, int eol, void *userdata) +{ + struct module *mod = userdata; + + if (eol < 0) { + LOG_ERR("failed to get source info: %s", context_error(c)); + set_source_offline(mod); + } else if (eol == 0) { + set_source_info(mod, i); + } +} + +static void +server_info_cb(pa_context *c, const pa_server_info *i, void *userdata) +{ + LOG_INFO("%s, version %s", i->server_name, i->server_version); +} + +static void +get_sink_info_by_name(pa_context *c, const char *name, void *userdata) +{ + pa_operation *o = + pa_context_get_sink_info_by_name(c, name, sink_info_cb, userdata); + pa_operation_unref(o); +} + +static void +get_source_info_by_name(pa_context *c, const char *name, void *userdata) +{ + pa_operation *o = + pa_context_get_source_info_by_name(c, name, source_info_cb, userdata); + pa_operation_unref(o); +} + +static void +get_sink_info_by_index(pa_context *c, uint32_t index, void *userdata) +{ + pa_operation *o = + pa_context_get_sink_info_by_index(c, index, sink_info_cb, userdata); + pa_operation_unref(o); +} + +static void +get_source_info_by_index(pa_context *c, uint32_t index, void *userdata) +{ + pa_operation *o = + pa_context_get_source_info_by_index(c, index, source_info_cb, userdata); + pa_operation_unref(o); +} + +static void +get_server_info(pa_context *c, void *userdata) +{ + pa_operation *o = pa_context_get_server_info(c, server_info_cb, userdata); + pa_operation_unref(o); +} + +static void +subscribe(pa_context *c, void *userdata) +{ + pa_subscription_mask_t mask = PA_SUBSCRIPTION_MASK_SERVER + | PA_SUBSCRIPTION_MASK_SINK + | PA_SUBSCRIPTION_MASK_SOURCE; + pa_operation *o = pa_context_subscribe(c, mask, NULL, userdata); + pa_operation_unref(o); +} + +static pa_context * +connect_to_server(struct module *mod); + +static void +context_state_change_cb(pa_context *c, void *userdata) +{ + struct module *mod = userdata; + struct private *priv = mod->private; + + pa_context_state_t state = pa_context_get_state(c); + switch (state) { + case PA_CONTEXT_UNCONNECTED: + case PA_CONTEXT_CONNECTING: + case PA_CONTEXT_AUTHORIZING: + case PA_CONTEXT_SETTING_NAME: + break; + + case PA_CONTEXT_READY: + set_server_online(mod); + subscribe(c, mod); + get_server_info(c, mod); + get_sink_info_by_name(c, priv->sink_name, mod); + get_source_info_by_name(c, priv->source_name, mod); + break; + + case PA_CONTEXT_FAILED: + LOG_WARN("connection lost"); + set_server_offline(mod); + pa_context_unref(priv->context); + priv->context = connect_to_server(mod); + break; + + case PA_CONTEXT_TERMINATED: + LOG_DBG("connection terminated"); + set_server_offline(mod); + pa_mainloop_quit(priv->mainloop, 0); + break; + } +} + +static void +subscription_event_cb(pa_context *c, + pa_subscription_event_type_t event_type, + uint32_t index, + void *userdata) +{ + struct module *mod = userdata; + struct private *priv = mod->private; + + int facility = event_type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK; + int type = event_type & PA_SUBSCRIPTION_EVENT_TYPE_MASK; + + switch (facility) { + case PA_SUBSCRIPTION_EVENT_SERVER: + get_sink_info_by_name(c, priv->sink_name, mod); + get_source_info_by_name(c, priv->source_name, mod); + break; + + case PA_SUBSCRIPTION_EVENT_SINK: + if (index == priv->sink_index) { + if (type == PA_SUBSCRIPTION_EVENT_CHANGE) + get_sink_info_by_index(c, index, mod); + else if (type == PA_SUBSCRIPTION_EVENT_REMOVE) + set_sink_offline(mod); + } + break; + + case PA_SUBSCRIPTION_EVENT_SOURCE: + if (index == priv->source_index) { + if (type == PA_SUBSCRIPTION_EVENT_CHANGE) + get_source_info_by_index(c, index, mod); + else if (type == PA_SUBSCRIPTION_EVENT_REMOVE) + set_source_offline(mod); + } + break; + } +} + +static pa_context * +connect_to_server(struct module *mod) +{ + struct private *priv = mod->private; + + // Create connection context. + pa_mainloop_api *api = pa_mainloop_get_api(priv->mainloop); + pa_context *c = pa_context_new(api, "yambar"); + if (c == NULL) { + LOG_ERR("failed to create PulseAudio connection context"); + return NULL; + } + + // Register callback functions. + pa_context_set_state_callback(c, context_state_change_cb, mod); + pa_context_set_subscribe_callback(c, subscription_event_cb, mod); + + // Connect to server. + pa_context_flags_t flags = PA_CONTEXT_NOFAIL + | PA_CONTEXT_NOAUTOSPAWN; + if (pa_context_connect(c, NULL, flags, NULL) < 0) { + LOG_ERR("failed to connect to PulseAudio server: %s", context_error(c)); + pa_context_unref(c); + return NULL; + } + + return c; +} + +static int +run(struct module *mod) +{ + struct private *priv = mod->private; + int ret = -1; + + // Create main loop. + priv->mainloop = pa_mainloop_new(); + if (priv->mainloop == NULL) { + LOG_ERR("failed to create PulseAudio main loop"); + return -1; + } + + // Create refresh timer. + priv->refresh_timer_fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK); + if (priv->refresh_timer_fd < 0) { + LOG_ERRNO("failed to create timerfd"); + pa_mainloop_free(priv->mainloop); + return -1; + } + + // Connect to server. + priv->context = connect_to_server(mod); + if (priv->context == NULL) { + pa_mainloop_free(priv->mainloop); + close(priv->refresh_timer_fd); + return -1; + } + + // Poll refresh timer and abort event. + pa_mainloop_api *api = pa_mainloop_get_api(priv->mainloop); + api->io_new(api, priv->refresh_timer_fd, PA_IO_EVENT_INPUT, + refresh_timer_cb, mod); + api->io_new(api, mod->abort_fd, PA_IO_EVENT_INPUT | PA_IO_EVENT_HANGUP, + abort_event_cb, mod); + + // Run main loop. + if (pa_mainloop_run(priv->mainloop, &ret) < 0) { + LOG_ERR("PulseAudio main loop error"); + ret = -1; + } + + // Clean up. + pa_context_unref(priv->context); + pa_mainloop_free(priv->mainloop); + close(priv->refresh_timer_fd); + + return ret; +} + +static struct module * +pulse_new(const char *sink_name, + const char *source_name, + struct particle *label) +{ + struct private *priv = calloc(1, sizeof *priv); + priv->label = label; + priv->sink_name = strdup(sink_name); + priv->source_name = strdup(source_name); + + struct module *mod = module_common_new(); + mod->private = priv; + mod->run = &run; + mod->destroy = &destroy; + mod->content = &content; + mod->description = &description; + return mod; +} + +static struct module * +from_conf(const struct yml_node *node, struct conf_inherit inherited) +{ + const struct yml_node *sink = yml_get_value(node, "sink"); + const struct yml_node *source = yml_get_value(node, "source"); + const struct yml_node *content = yml_get_value(node, "content"); + + return pulse_new( + sink != NULL ? yml_value_as_string(sink) : "@DEFAULT_SINK@", + source != NULL ? yml_value_as_string(source) : "@DEFAULT_SOURCE@", + conf_to_particle(content, inherited)); +} + +static bool +verify_conf(keychain_t *chain, const struct yml_node *node) +{ + static const struct attr_info attrs[] = { + {"sink", false, &conf_verify_string}, + {"source", false, &conf_verify_string}, + MODULE_COMMON_ATTRS, + }; + + return conf_verify_dict(chain, node, attrs); +} + +const struct module_iface module_pulse_iface = { + .verify_conf = &verify_conf, + .from_conf = &from_conf, +}; + +#if defined(CORE_PLUGINS_AS_SHARED_LIBRARIES) +extern const struct module_iface iface __attribute__((weak, alias("module_pulse_iface"))); +#endif diff --git a/plugin.c b/plugin.c index 06304d8..542cfaa 100644 --- a/plugin.c +++ b/plugin.c @@ -44,6 +44,9 @@ EXTERN_MODULE(label); EXTERN_MODULE(mpd); #endif EXTERN_MODULE(network); +#if defined(PLUGIN_ENABLED_PULSE) +EXTERN_MODULE(pulse); +#endif EXTERN_MODULE(removables); EXTERN_MODULE(river); EXTERN_MODULE(sway_xkb); @@ -129,6 +132,9 @@ init(void) REGISTER_CORE_MODULE(mpd, mpd); #endif REGISTER_CORE_MODULE(network, network); +#if defined(PLUGIN_ENABLED_PULSE) + REGISTER_CORE_MODULE(pulse, pulse); +#endif REGISTER_CORE_MODULE(removables, removables); #if defined(HAVE_PLUGIN_river) REGISTER_CORE_MODULE(river, river); From 19a9f099e2efae8b81f91cbe2c1c045277247087 Mon Sep 17 00:00:00 2001 From: Ogromny Date: Tue, 13 Dec 2022 10:10:06 +0100 Subject: [PATCH 405/611] modules/pipewire: new module --- CHANGELOG.md | 2 + doc/meson.build | 1 + doc/yambar-modules-pipewire.5.scd | 83 +++ meson.build | 8 +- meson_options.txt | 3 + modules/meson.build | 5 + modules/pipewire.c | 969 ++++++++++++++++++++++++++++++ plugin.c | 6 + 8 files changed, 1075 insertions(+), 2 deletions(-) create mode 100644 doc/yambar-modules-pipewire.5.scd create mode 100644 modules/pipewire.c diff --git a/CHANGELOG.md b/CHANGELOG.md index 73ff4e0..9ff593b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ * new module: pulse ([#223][223]). * alsa: `dB` tag ([#202][202]). * mpd: `file` tag ([#219][219]). +* pipewire: add a new module for pipewire ([#224][224]) * on-click: support `next`/`previous` mouse buttons ([#228][228]). [153]: https://codeberg.org/dnkl/yambar/issues/153 @@ -38,6 +39,7 @@ [202]: https://codeberg.org/dnkl/yambar/issues/202 [219]: https://codeberg.org/dnkl/yambar/pulls/219 [223]: https://codeberg.org/dnkl/yambar/pulls/223 +[224]: https://codeberg.org/dnkl/yambar/pulls/224 [228]: https://codeberg.org/dnkl/yambar/pulls/228 diff --git a/doc/meson.build b/doc/meson.build index a956dbc..0d62cb5 100644 --- a/doc/meson.build +++ b/doc/meson.build @@ -11,6 +11,7 @@ foreach man_src : ['yambar.1.scd', 'yambar.5.scd', 'yambar-decorations.5.scd', 'yambar-modules-i3.5.scd', 'yambar-modules-label.5.scd', 'yambar-modules-mpd.5.scd', 'yambar-modules-network.5.scd', 'yambar-modules-pulse.5.scd', + 'yambar-modules-pipewire.5.scd', 'yambar-modules-removables.5.scd', 'yambar-modules-river.5.scd', 'yambar-modules-script.5.scd', 'yambar-modules-sway-xkb.5.scd', 'yambar-modules-sway.5.scd', 'yambar-modules-xkb.5.scd', diff --git a/doc/yambar-modules-pipewire.5.scd b/doc/yambar-modules-pipewire.5.scd new file mode 100644 index 0000000..4e0f587 --- /dev/null +++ b/doc/yambar-modules-pipewire.5.scd @@ -0,0 +1,83 @@ +yambar-modules-pipewire(5) + +# NAME +pipewire - Monitors pipewire for volume, mute/unmute, device change + +# TAGS + +[[ *Name* +:[ *Type* +:[ *Description* +| type +: string +: Either "source" (capture) or "sink" (speaker) +| name +: string +: Current device name +| description +: string +: Current device description +| form_factor +: string +: Current device form factor (headset, speaker, mic, etc) +| bus +: string +: Current device bus (bluetooth, alsa, etc) +| icon +: string +: Current device icon name +| muted +: bool +: True if muted, otherwise false +| linear_volume +: range +: Linear volume in percentage (with 0 as min and 100 as max) +| cubic_volume +: range +: Cubic volume (used by pulseaudio) in percentage (with 0 as min and 100 as max) + + +# CONFIGURATION + +No additional attributes supported, only the generic ones (see +*GENERIC CONFIGURATION* in *yambar-modules*(5)) + + +# EXAMPLES + +``` +bar: + left: + - pipewire: + anchors: + volume: &volume + conditions: + muted: {string: {text: "{linear_volume}%", foreground: ff0000ff}} + ~muted: {string: {text: "{linear_volume}%"}} + content: + list: + items: + - map: + conditions: + type == "sink": + map: + conditions: + icon == "audio-headset-bluetooth": + string: {text: "🎧 "} + default: + - ramp: + tag: linear_volume + items: + - string: {text: "🔈 "} + - string: {text: "🔉 "} + - string: {text: "🔊 "} + type == "source": + - string: {text: "🎙 "} + - map: + <<: *volume +``` + +# SEE ALSO + +*yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5) + diff --git a/meson.build b/meson.build index 52f75bb..d480929 100644 --- a/meson.build +++ b/meson.build @@ -131,8 +131,11 @@ yambar = executable( version, dependencies: [bar, libepoll, libinotify, pixman, yaml, threads, dl, tllist, fcft] + decorations + particles + modules, - c_args: [plugin_mpd_enabled? '-DPLUGIN_ENABLED_MPD':[], - plugin_pulse_enabled? '-DPLUGIN_ENABLED_PULSE':[]], + c_args: [ + plugin_mpd_enabled? '-DPLUGIN_ENABLED_MPD':[], + plugin_pulse_enabled? '-DPLUGIN_ENABLED_PULSE':[], + plugin_pipewire_enabled? '-DPLUGIN_ENABLED_PIPEWIRE':[], + ], build_rpath: '$ORIGIN/modules:$ORIGIN/decorations:$ORIGIN/particles', export_dynamic: true, install: true, @@ -172,6 +175,7 @@ summary( { 'Music Player Daemon (MPD)': plugin_mpd_enabled, 'PulseAudio': plugin_pulse_enabled, + 'Pipewire': plugin_pipewire_enabled }, section: 'Optional modules', bool_yn: true diff --git a/meson_options.txt b/meson_options.txt index 3878096..bcfce60 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -11,3 +11,6 @@ option( option( 'plugin-pulse', type: 'feature', value: 'auto', description: 'PulseAudio support') +option( + 'plugin-pipewire', type: 'feature', value: 'auto', + description: 'Pipewire support') diff --git a/modules/meson.build b/modules/meson.build index ddeb491..ddfd747 100644 --- a/modules/meson.build +++ b/modules/meson.build @@ -10,6 +10,8 @@ xcb_xkb = dependency('xcb-xkb', required: get_option('backend-x11')) # Optional deps mpd = dependency('libmpdclient', required: get_option('plugin-mpd')) plugin_mpd_enabled = mpd.found() +pipewire = dependency('libpipewire-0.3', required: get_option('plugin-pipewire')) +plugin_pipewire_enabled = pipewire.found() pulse = dependency('libpulse', required: get_option('plugin-pulse')) plugin_pulse_enabled = pulse.found() @@ -34,6 +36,9 @@ mod_data = { if plugin_mpd_enabled mod_data += {'mpd': [[], [mpd]]} endif +if plugin_pipewire_enabled + mod_data += {'pipewire': [[], [pipewire, dynlist, json]]} +endif if plugin_pulse_enabled mod_data += {'pulse': [[], [pulse]]} diff --git a/modules/pipewire.c b/modules/pipewire.c new file mode 100644 index 0000000..e369bd2 --- /dev/null +++ b/modules/pipewire.c @@ -0,0 +1,969 @@ +#include "spa/utils/list.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LOG_MODULE "pipewire" +#define LOG_ENABLE_DBG 1 +#include "../config-verify.h" +#include "../config.h" +#include "../log.h" +#include "../module.h" +#include "../particle.h" +#include "../particles/dynlist.h" +#include "../plugin.h" +#include "../yml.h" + +#define ARRAY_LENGTH(x) (sizeof((x)) / sizeof((x)[0])) +/* clang-format off */ +#define X_FREE_SET(t, v) do { free((t)); (t) = (v); } while (0) +/* clang-format on */ +#define X_STRDUP(s) ((s) != NULL ? strdup((s)) : NULL) + +struct output_informations { + /* internal */ + uint32_t device_id; + uint32_t card_profile_device_id; + + /* informations */ + bool muted; + uint8_t linear_volume; /* classic volume */ + uint8_t cubic_volume; /* volume a la pulseaudio */ + char *name; + char *description; + char *form_factor; /* headset, headphone, speaker, ..., can be null */ + char *bus; /* alsa, bluetooth, etc */ + char *icon; +}; +static struct output_informations const output_informations_null; + +struct data; +struct private +{ + struct particle *label; + struct data *data; + + /* pipewire related */ + struct output_informations sink_informations; + struct output_informations source_informations; +}; + +/* This struct is needed because when param event occur, the function + * `node_events_param` will receive the corresponding event about the node + * but there's no simple way of knowing from which node the event come from */ +struct node_data { + struct data *data; + /* otherwise is_source */ + bool is_sink; +}; + +/* struct data */ +struct node; +struct data { + /* yambar module */ + struct module *module; + + char *target_sink; + char *target_source; + + struct node *binded_sink; + struct node *binded_source; + + struct node_data node_data_sink; + struct node_data node_data_source; + + /* proxies */ + void *metadata; + void *node_sink; + void *node_source; + + /* main struct */ + struct pw_main_loop *loop; + struct pw_context *context; + struct pw_core *core; + struct pw_registry *registry; + + /* listener */ + struct spa_hook registry_listener; + struct spa_hook core_listener; + struct spa_hook metadata_listener; + struct spa_hook node_sink_listener; + struct spa_hook node_source_listener; + + /* list */ + struct spa_list node_list; + struct spa_list device_list; + + int sync; +}; + +/* struct Route */ +struct route { + struct device *device; + + struct spa_list link; + + enum spa_direction direction; /* direction */ + int profile_device_id; /* device */ + char *form_factor; /* info.type */ + char *icon_name; /* info.icon-name */ +}; + +static void +route_free(struct route *route) +{ + free(route->form_factor); + free(route->icon_name); + spa_list_remove(&route->link); + free(route); +} + +/* struct Device */ +struct device { + struct data *data; + + struct spa_list link; + uint32_t id; + struct spa_list routes; + + void *proxy; + struct spa_hook listener; +}; + +static void +device_free(struct device *device, struct data *data) +{ + struct route *route = NULL; + spa_list_consume(route, &device->routes, link) route_free(route); + + spa_hook_remove(&device->listener); + pw_proxy_destroy((struct pw_proxy *)device->proxy); + + spa_list_remove(&device->link); + free(device); +} + +static struct route * +route_find_or_create(struct device *device, uint32_t profile_device_id) +{ + struct route *route = NULL; + spa_list_for_each(route, &device->routes, link) + { + if (route->profile_device_id == profile_device_id) + return route; + } + + /* route not found, let's create it */ + route = calloc(1, sizeof(struct route)); + assert(route != NULL); + route->device = device; + route->profile_device_id = profile_device_id; + spa_list_append(&device->routes, &route->link); + return route; +} + +struct node { + struct spa_list link; + uint32_t id; + char *name; +}; + +/* struct node */ +static struct route * +node_find_route(struct data *data, bool is_sink) +{ + struct private *private = data->module->private; + struct output_informations *output_informations = NULL; + + if (is_sink) { + if (data->node_sink == NULL) + return NULL; + output_informations = &private->sink_informations; + } else { + if (data->node_source == NULL) + return NULL; + output_informations = &private->source_informations; + } + + struct device *device = NULL; + spa_list_for_each(device, &data->device_list, link) + { + if (device->id != output_informations->device_id) + continue; + + struct route *route = NULL; + spa_list_for_each(route, &device->routes, link) + { + if (route->profile_device_id == output_informations->card_profile_device_id) + return route; + } + } + + return NULL; +} + +static void +node_unhook_binded_node(struct data *data, bool is_sink) +{ + struct node **target_node = NULL; + struct spa_hook *target_listener = NULL; + void **target_proxy = NULL; + + if (is_sink) { + target_node = &data->binded_sink; + target_listener = &data->node_sink_listener; + target_proxy = &data->node_sink; + } else { + target_node = &data->binded_source; + target_listener = &data->node_source_listener; + target_proxy = &data->node_source; + } + + if (*target_node == NULL) + return; + + spa_hook_remove(target_listener); + pw_proxy_destroy(*target_proxy); + + *target_node = NULL; + *target_proxy = NULL; +} + +static void +node_free(struct node *node, struct data *data) +{ + if (data->binded_sink == node) + node_unhook_binded_node(data, true); + else if (data->binded_source == node) + node_unhook_binded_node(data, false); + + spa_list_remove(&node->link); + free(node->name); + free(node); +} + +/* Device events */ +static void +device_events_info(void *userdata, const struct pw_device_info *info) +{ + struct device *device = userdata; + + /* We only want the "Route" param, which is in Params */ + if (!(info->change_mask & PW_DEVICE_CHANGE_MASK_PARAMS)) + return; + + for (size_t i = 0; i < info->n_params; ++i) { + if (info->params[i].id == SPA_PARAM_Route) { + pw_device_enum_params(device->proxy, 0, info->params[i].id, 0, -1, NULL); + break; + } + } +} + +static void +device_events_param(void *userdata, int seq, uint32_t id, uint32_t index, uint32_t next, const struct spa_pod *param) +{ + /* We should only receive ParamRoute */ + assert(spa_pod_is_object_type(param, SPA_TYPE_OBJECT_ParamRoute)); + + struct route data = {0}; + struct spa_pod_prop const *prop = NULL; + + /* device must be present otherwise I can't do anything with the data */ + prop = spa_pod_find_prop(param, NULL, SPA_PARAM_ROUTE_device); + if (prop == NULL) + return; + spa_pod_get_int(&prop->value, &data.profile_device_id); + + /* same for direction, required too */ + prop = spa_pod_find_prop(param, NULL, SPA_PARAM_ROUTE_direction); + if (prop == NULL) + return; + char const *direction = NULL; + spa_pod_get_string(&prop->value, &direction); + if (spa_streq(direction, "Output")) + data.direction = SPA_DIRECTION_OUTPUT; + else + data.direction = SPA_DIRECTION_INPUT; + + /* same for info, it's required */ + prop = spa_pod_find_prop(param, NULL, SPA_PARAM_ROUTE_info); + if (prop == NULL) + return; + + struct spa_pod *iter = NULL; + char const *header = NULL; + SPA_POD_STRUCT_FOREACH(&prop->value, iter) + { + /* no previous header */ + if (header == NULL) { + /* headers are always string */ + if (spa_pod_is_string(iter)) + spa_pod_get_string(iter, &header); + /* otherwise it's the first iteration (number of elements in the struct) */ + continue; + } + + /* Values needed: + * - (string) device.icon_name [icon_name] + * - (string) port.type [form_factor] */ + if (spa_pod_is_string(iter)) { + if (spa_streq(header, "device.icon_name")) + spa_pod_get_string(iter, (char const **)&data.icon_name); + else if (spa_streq(header, "port.type")) { + spa_pod_get_string(iter, (char const **)&data.form_factor); + } + } + + header = NULL; + } + + struct device *device = userdata; + + struct route *route = route_find_or_create(device, data.profile_device_id); + X_FREE_SET(route->form_factor, X_STRDUP(data.form_factor)); + X_FREE_SET(route->icon_name, X_STRDUP(data.icon_name)); + route->direction = data.direction; + + /* set missing informations if possible */ + struct private *private = device->data->module->private; + struct node *binded_node = NULL; + struct output_informations *output_informations = NULL; + + if (route->direction == SPA_DIRECTION_INPUT) { + binded_node = private->data->binded_source; + output_informations = &private->source_informations; + } else { + binded_node = private->data->binded_sink; + output_informations = &private->sink_informations; + } + + /* Node not binded */ + if (binded_node == NULL) + return; + + /* Node's device is the the same as route's device */ + if (output_informations->device_id != route->device->id) + return; + + /* Route is not the Node's device route */ + if (output_informations->card_profile_device_id != route->profile_device_id) + return; + + /* Update missing informations */ + X_FREE_SET(output_informations->form_factor, X_STRDUP(route->form_factor)); + X_FREE_SET(output_informations->icon, X_STRDUP(route->icon_name)); + + device->data->module->bar->refresh(device->data->module->bar); +} + +static struct pw_device_events const device_events = { + PW_VERSION_DEVICE_EVENTS, + .info = device_events_info, + .param = device_events_param, +}; + +/* Node events */ +static void +node_events_info(void *userdata, struct pw_node_info const *info) +{ + struct node_data *node_data = userdata; + struct data *data = node_data->data; + struct private *private = data->module->private; + + if (info->change_mask & PW_NODE_CHANGE_MASK_PARAMS) { + /* We only need the Props param, so let's try to find it */ + for (size_t i = 0; i < info->n_params; ++i) { + if (info->params[i].id == SPA_PARAM_Props) { + void *target_node = (node_data->is_sink ? data->node_sink : data->node_source); + /* Found it, will emit a param event, the parem will then be handled + * in node_events_param */ + pw_node_enum_params(target_node, 0, info->params[i].id, 0, -1, NULL); + break; + } + } + } + + if (info->change_mask & PW_NODE_CHANGE_MASK_PROPS) { + struct output_informations *output_informations + = (node_data->is_sink ? &private->sink_informations : &private->source_informations); + struct spa_dict_item const *item = NULL; + + item = spa_dict_lookup_item(info->props, "node.name"); + if (item != NULL) + X_FREE_SET(output_informations->name, X_STRDUP(item->value)); + + item = spa_dict_lookup_item(info->props, "node.description"); + if (item != NULL) + X_FREE_SET(output_informations->description, X_STRDUP(item->value)); + + item = spa_dict_lookup_item(info->props, "device.id"); + if (item != NULL) { + uint32_t value = 0; + spa_atou32(item->value, &value, 10); + output_informations->device_id = value; + } + + item = spa_dict_lookup_item(info->props, "card.profile.device"); + if (item != NULL) { + uint32_t value = 0; + spa_atou32(item->value, &value, 10); + output_informations->card_profile_device_id = value; + } + + /* Device's informations has an more important priority than node's informations */ + /* icon_name */ + struct route *route = node_find_route(data, node_data->is_sink); + if (route != NULL && route->icon_name != NULL) + output_informations->icon = X_STRDUP(route->icon_name); + else { + item = spa_dict_lookup_item(info->props, "device.icon-name"); + if (item != NULL) + X_FREE_SET(output_informations->icon, X_STRDUP(item->value)); + } + /* form_factor */ + if (route != NULL && route->form_factor != NULL) + output_informations->form_factor = X_STRDUP(route->form_factor); + else { + item = spa_dict_lookup_item(info->props, "device.form-factor"); + if (item != NULL) + X_FREE_SET(output_informations->form_factor, X_STRDUP(item->value)); + } + + item = spa_dict_lookup_item(info->props, "device.bus"); + if (item != NULL) + X_FREE_SET(output_informations->bus, X_STRDUP(item->value)); + + data->module->bar->refresh(data->module->bar); + } +} + +static void +node_events_param(void *userdata, __attribute__((unused)) int seq, __attribute__((unused)) uint32_t id, + __attribute__((unused)) uint32_t index, __attribute__((unused)) uint32_t next, + const struct spa_pod *param) +{ + struct node_data *node_data = userdata; + struct data *data = node_data->data; + struct private *private = data->module->private; + + struct output_informations *output_informations + = (node_data->is_sink ? &private->sink_informations : &private->source_informations); + struct spa_pod_prop const *prop = NULL; + + prop = spa_pod_find_prop(param, NULL, SPA_PROP_mute); + if (prop != NULL) { + bool value = false; + spa_pod_get_bool(&prop->value, &value); + output_informations->muted = value; + } + + prop = spa_pod_find_prop(param, NULL, SPA_PROP_channelVolumes); + if (prop != NULL) { + uint32_t n_values = 0; + float *values = spa_pod_get_array(&prop->value, &n_values); + float total = 0.0f; + + /* Array can be empty some times, assume that values have not changed */ + if (n_values != 0) { + for (uint32_t i = 0; i < n_values; ++i) + total += values[i]; + + float base_volume = total / n_values; + output_informations->linear_volume = ceilf(base_volume * 100); + output_informations->cubic_volume = ceilf(cbrtf(base_volume) * 100); + } + } + + data->module->bar->refresh(data->module->bar); +} + +static struct pw_node_events const node_events = { + PW_VERSION_NODE_EVENTS, + .info = node_events_info, + .param = node_events_param, +}; + +/* Metadata events */ +static int +metadata_property(void *userdata, __attribute__((unused)) uint32_t id, char const *key, + __attribute__((unused)) char const *type, char const *value) +{ + struct data *data = userdata; + bool is_sink = false; // true for source mode + char **target_name = NULL; + + /* We only want default.audio.sink or default.audio.source */ + if (spa_streq(key, "default.audio.sink")) { + is_sink = true; + target_name = &data->target_sink; + } else if (spa_streq(key, "default.audio.source")) { + is_sink = false; /* just to be explicit */ + target_name = &data->target_source; + } else + return 0; + + /* Value is NULL when the profile is set to `off`. */ + if (value == NULL) { + node_unhook_binded_node(data, is_sink); + free(*target_name); + *target_name = NULL; + data->module->bar->refresh(data->module->bar); + return 0; + } + + struct json_object *json = json_tokener_parse(value); + struct json_object_iterator json_it = json_object_iter_begin(json); + struct json_object_iterator json_it_end = json_object_iter_end(json); + + while (!json_object_iter_equal(&json_it, &json_it_end)) { + char const *key = json_object_iter_peek_name(&json_it); + if (!spa_streq(key, "name")) { + json_object_iter_next(&json_it); + continue; + } + + /* Found name */ + struct json_object *value = json_object_iter_peek_value(&json_it); + assert(json_object_is_type(value, json_type_string)); + + char const *name = json_object_get_string(value); + /* `auto_null` is the same as `value == NULL` see lines above. */ + if (spa_streq(name, "auto_null")) { + node_unhook_binded_node(data, is_sink); + free(*target_name); + *target_name = NULL; + data->module->bar->refresh(data->module->bar); + break; + } + + /* target_name is the same */ + if (spa_streq(name, *target_name)) + break; + + /* Unhook the binded_node */ + node_unhook_binded_node(data, is_sink); + + /* Update the target */ + free(*target_name); + *target_name = strdup(name); + + /* Sync the core, core_events_done will then try to bind the good node */ + data->sync = pw_core_sync(data->core, PW_ID_CORE, data->sync); + break; + } + + json_object_put(json); + + return 0; +} + +static struct pw_metadata_events const metadata_events = { + PW_VERSION_METADATA_EVENTS, + .property = metadata_property, +}; + +/* Registry events */ +static void +registry_event_global(void *userdata, uint32_t id, __attribute__((unused)) uint32_t permissions, char const *type, + __attribute__((unused)) uint32_t version, struct spa_dict const *props) +{ + struct data *data = userdata; + + /* New device */ + if (spa_streq(type, PW_TYPE_INTERFACE_Device)) { + struct device *device = calloc(1, sizeof(struct device)); + assert(device != NULL); + device->data = data; + device->id = id; + spa_list_init(&device->routes); + device->proxy = pw_registry_bind(data->registry, id, type, PW_VERSION_DEVICE, 0); + assert(device->proxy != NULL); + pw_device_add_listener(device->proxy, &device->listener, &device_events, device); + + spa_list_append(&data->device_list, &device->link); + } + /* New node */ + else if (spa_streq(type, PW_TYPE_INTERFACE_Node)) { + /* Fill a new node */ + struct node *node = calloc(1, sizeof(struct node)); + assert(node != NULL); + node->id = id; + node->name = strdup(spa_dict_lookup(props, PW_KEY_NODE_NAME)); + + /* Store it */ + spa_list_append(&data->node_list, &node->link); + } + /* New metadata */ + else if (spa_streq(type, PW_TYPE_INTERFACE_Metadata)) { + /* A metadata has already been bind */ + if (data->metadata != NULL) + return; + + /* Target only metadata which has "default" key */ + char const *name = spa_dict_lookup(props, PW_KEY_METADATA_NAME); + if (name == NULL || !spa_streq(name, "default")) + return; + + /* Bind metadata */ + data->metadata = pw_registry_bind(data->registry, id, type, PW_VERSION_METADATA, 0); + assert(data->metadata != NULL); + pw_metadata_add_listener(data->metadata, &data->metadata_listener, &metadata_events, data); + } + + /* `core_events_done` will then try to bind the good node */ + data->sync = pw_core_sync(data->core, PW_ID_CORE, data->sync); +} + +static void +registry_event_global_remove(void *userdata, uint32_t id) +{ + struct data *data = userdata; + + /* Try to find a node with the same `id` */ + struct node *node = NULL, *node_temp = NULL; + spa_list_for_each_safe(node, node_temp, &data->node_list, link) + { + if (node->id == id) { + node_free(node, data); + return; + } + } + + /* No node with this `id` maybe it's a device */ + struct device *device = NULL, *device_temp = NULL; + spa_list_for_each_safe(device, device_temp, &data->device_list, link) + { + if (device->id == id) { + device_free(device, data); + return; + } + } +} + +static struct pw_registry_events const registry_events = { + PW_VERSION_REGISTRY_EVENTS, + .global = registry_event_global, + .global_remove = registry_event_global_remove, +}; + +static void +try_to_bind_node(struct node_data *node_data, char const *target_name, struct node **target_node, void **target_proxy, + struct spa_hook *target_listener) +{ + /* profile deactived */ + if (target_name == NULL) + return; + + struct data *data = node_data->data; + + struct node *node = NULL; + spa_list_for_each(node, &data->node_list, link) + { + if (!spa_streq(target_name, node->name)) + continue; + + /* Found good node */ + + *target_node = node; + *target_proxy = pw_registry_bind(data->registry, node->id, PW_TYPE_INTERFACE_Node, PW_VERSION_NODE, 0); + assert(*target_proxy != NULL); + pw_node_add_listener(*target_proxy, target_listener, &node_events, node_data); + break; + } +} + +/* Core events */ +static void +core_events_done(void *userdata, uint32_t id, int seq) +{ + struct data *data = userdata; + + if (id != PW_ID_CORE) + return; + + /* Not our seq */ + if (data->sync != seq) + return; + + /* Sync ended, try to bind the node which has the targeted sink or the targeted source */ + + /* Node sink not binded and target_sink is set */ + if (data->binded_sink == NULL && data->target_sink != NULL) + try_to_bind_node(&data->node_data_sink, data->target_sink, &data->binded_sink, &data->node_sink, + &data->node_sink_listener); + + /* Node source not binded and target_source is set */ + if (data->binded_source == NULL && data->target_source != NULL) + try_to_bind_node(&data->node_data_source, data->target_source, &data->binded_source, &data->node_source, + &data->node_source_listener); +} + +static void +core_events_error(void *userdata, uint32_t id, int seq, int res, char const *message) +{ + pw_log_error("error id:%u seq:%d res:%d (%s): %s", id, seq, res, spa_strerror(res), message); + + if (id == PW_ID_CORE && res == -EPIPE) { + struct data *data = userdata; + pw_main_loop_quit(data->loop); + } +} + +static struct pw_core_events const core_events = { + PW_VERSION_CORE_EVENTS, + .done = core_events_done, + .error = core_events_error, +}; + +/* init, deinit */ +static struct data * +pipewire_init(struct module *module) +{ + pw_init(NULL, NULL); + + /* Data */ + struct data *data = calloc(1, sizeof(struct data)); + assert(data != NULL); + + spa_list_init(&data->node_list); + spa_list_init(&data->device_list); + + /* Main loop */ + data->loop = pw_main_loop_new(NULL); + assert(data->loop != NULL); + + /* Context */ + data->context = pw_context_new(pw_main_loop_get_loop(data->loop), NULL, 0); + assert(data->context != NULL); + + /* Core */ + data->core = pw_context_connect(data->context, NULL, 0); + assert(data->core); + pw_core_add_listener(data->core, &data->core_listener, &core_events, data); + + /* Registry */ + data->registry = pw_core_get_registry(data->core, PW_VERSION_REGISTRY, 0); + assert(data->registry); + pw_registry_add_listener(data->registry, &data->registry_listener, ®istry_events, data); + + /* Sync */ + data->sync = pw_core_sync(data->core, PW_ID_CORE, data->sync); + + data->module = module; + + /* node_events_param_data */ + data->node_data_sink.data = data; + data->node_data_sink.is_sink = true; + data->node_data_source.data = data; + data->node_data_source.is_sink = false; + + return data; +} + +static void +pipewire_deinit(struct data *data) +{ + struct node *node = NULL; + spa_list_consume(node, &data->node_list, link) node_free(node, data); + + struct device *device = NULL; + spa_list_consume(device, &data->device_list, link) device_free(device, data); + + if (data->metadata) + pw_proxy_destroy((struct pw_proxy *)data->metadata); + spa_hook_remove(&data->registry_listener); + pw_proxy_destroy((struct pw_proxy *)data->registry); + spa_hook_remove(&data->core_listener); + spa_hook_remove(&data->metadata_listener); + pw_core_disconnect(data->core); + pw_context_destroy(data->context); + pw_main_loop_destroy(data->loop); + free(data->target_sink); + free(data->target_source); + pw_deinit(); +} + +static void +destroy(struct module *module) +{ + struct private *private = module->private; + + pipewire_deinit(private->data); + private->label->destroy(private->label); + + /* sink */ + free(private->sink_informations.name); + free(private->sink_informations.description); + free(private->sink_informations.icon); + free(private->sink_informations.form_factor); + free(private->sink_informations.bus); + /* source */ + free(private->source_informations.name); + free(private->source_informations.description); + free(private->source_informations.icon); + free(private->source_informations.form_factor); + free(private->source_informations.bus); + + free(private); + module_default_destroy(module); +} + +static char const * +description(struct module *module) +{ + return "pipewire"; +} + +static struct exposable * +content(struct module *module) +{ + struct private *private = module->private; + + mtx_lock(&module->lock); + + struct exposable *exposables[2]; + size_t exposables_length = ARRAY_LENGTH(exposables); + + struct output_informations const *output_informations = NULL; + + /* sink */ + output_informations + = (private->data->target_sink == NULL ? &output_informations_null : &private->sink_informations); + + struct tag_set sink_tag_set = (struct tag_set){ + .tags = (struct tag *[]){ + tag_new_string(module, "type", "sink"), + tag_new_string(module, "name", output_informations->name), + tag_new_string(module, "description", output_informations->description), + tag_new_string(module, "icon", output_informations->icon), + tag_new_string(module, "form_factor", output_informations->form_factor), + tag_new_string(module, "bus", output_informations->bus), + tag_new_bool(module, "muted", output_informations->muted), + tag_new_int_range(module, "linear_volume", output_informations->linear_volume, 0, 100), + tag_new_int_range(module, "cubic_volume", output_informations->cubic_volume, 0, 100), + }, + .count = 9, + }; + + /* source */ + output_informations + = (private->data->target_source == NULL ? &output_informations_null : &private->source_informations); + + struct tag_set source_tag_set = (struct tag_set){ + .tags = (struct tag *[]){ + tag_new_string(module, "type", "source"), + tag_new_string(module, "name", output_informations->name), + tag_new_string(module, "description", output_informations->description), + tag_new_string(module, "icon", output_informations->icon), + tag_new_string(module, "form_factor", output_informations->form_factor), + tag_new_string(module, "bus", output_informations->bus), + tag_new_bool(module, "muted", output_informations->muted), + tag_new_int_range(module, "linear_volume", output_informations->linear_volume, 0, 100), + tag_new_int_range(module, "cubic_volume", output_informations->cubic_volume, 0, 100), + }, + .count = 9, + }; + + exposables[0] = private->label->instantiate(private->label, &sink_tag_set); + exposables[1] = private->label->instantiate(private->label, &source_tag_set); + + tag_set_destroy(&sink_tag_set); + tag_set_destroy(&source_tag_set); + + mtx_unlock(&module->lock); + + return dynlist_exposable_new(exposables, exposables_length, 0, 0); +} + +static int +run(struct module *module) +{ + struct private *private = module->private; + struct pw_loop *pw_loop = pw_main_loop_get_loop(private->data->loop); + struct pollfd pollfds[] = { + /* abort_fd */ + (struct pollfd){.fd = module->abort_fd, .events = POLLIN}, + /* pipewire */ + (struct pollfd){.fd = pw_loop_get_fd(pw_loop), .events = POLLIN}, + }; + + while (true) { + if (poll(pollfds, ARRAY_LENGTH(pollfds), -1) == -1) { + if (errno == EINTR) + continue; + + LOG_ERRNO("Unable to poll: %s", strerror(errno)); + break; + } + + /* abort_fd */ + if (pollfds[0].revents & POLLIN) + break; + + /* pipewire */ + if (!(pollfds[1].revents & POLLIN)) + /* issue happened */ + break; + + int result = pw_loop_iterate(pw_loop, 0); + if (result < 0) { + LOG_ERRNO("Unable to iterate pipewire loop: %s", spa_strerror(result)); + break; + } + } + + return 0; +} + +static struct module * +pipewire_new(struct particle *label) +{ + struct private *private = calloc(1, sizeof(struct private)); + assert(private != NULL); + private->label = label; + + struct module *module = module_common_new(); + module->private = private; + module->run = &run; + module->destroy = &destroy; + module->content = &content; + module->description = &description; + + private->data = pipewire_init(module); + + return module; +} + +static struct module * +from_conf(struct yml_node const *node, struct conf_inherit inherited) +{ + struct yml_node const *content = yml_get_value(node, "content"); + return pipewire_new(conf_to_particle(content, inherited)); +} + +static bool +verify_conf(keychain_t *keychain, struct yml_node const *node) +{ + static struct attr_info const attrs[] = { + MODULE_COMMON_ATTRS, + }; + return conf_verify_dict(keychain, node, attrs); +} + +struct module_iface const module_pipewire_iface = { + .from_conf = &from_conf, + .verify_conf = &verify_conf, +}; + +#if defined(CORE_PLUGINS_AS_SHARED_LIBRARIES) +extern struct module_iface const iface __attribute__((weak, alias("module_pipewire_iface"))); +#endif diff --git a/plugin.c b/plugin.c index 542cfaa..a17f1ae 100644 --- a/plugin.c +++ b/plugin.c @@ -47,6 +47,9 @@ EXTERN_MODULE(network); #if defined(PLUGIN_ENABLED_PULSE) EXTERN_MODULE(pulse); #endif +#if defined(PLUGIN_ENABLED_PIPEWIRE) +EXTERN_MODULE(pipewire); +#endif EXTERN_MODULE(removables); EXTERN_MODULE(river); EXTERN_MODULE(sway_xkb); @@ -134,6 +137,9 @@ init(void) REGISTER_CORE_MODULE(network, network); #if defined(PLUGIN_ENABLED_PULSE) REGISTER_CORE_MODULE(pulse, pulse); +#endif +#if defined(PLUGIN_ENABLED_PIPEWIRE) + REGISTER_CORE_MODULE(pipewire, pipewire); #endif REGISTER_CORE_MODULE(removables, removables); #if defined(HAVE_PLUGIN_river) From 302e0d5cc64e269e52e6ded66e539fd2c63b0947 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 13 Dec 2022 10:42:53 +0100 Subject: [PATCH 406/611] =?UTF-8?q?ci=20(sr.ht):=20install=20=E2=80=98dev?= =?UTF-8?q?=E2=80=99=20version=20of=20=E2=80=9Clibpulse=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .builds/alpine-x64.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.builds/alpine-x64.yml b/.builds/alpine-x64.yml index 926dc46..24b2c80 100644 --- a/.builds/alpine-x64.yml +++ b/.builds/alpine-x64.yml @@ -18,7 +18,7 @@ packages: - wlroots-dev - json-c-dev - libmpdclient-dev - - libpulse + - libpulse-dev - alsa-lib-dev - ttf-dejavu - gcovr From bd607d769753b913dcc0e54366794adc7074611f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 13 Dec 2022 10:44:44 +0100 Subject: [PATCH 407/611] ci: install pipewire-dev; should ensure we build the pipewire plugin --- .builds/alpine-x64.yml | 1 + .gitlab-ci.yml | 2 +- .woodpecker.yml | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.builds/alpine-x64.yml b/.builds/alpine-x64.yml index 24b2c80..2e00bde 100644 --- a/.builds/alpine-x64.yml +++ b/.builds/alpine-x64.yml @@ -20,6 +20,7 @@ packages: - libmpdclient-dev - libpulse-dev - alsa-lib-dev + - pipewire-dev - ttf-dejavu - gcovr - python3 diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 155979d..dd1db7b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -13,7 +13,7 @@ before_script: - apk add pixman-dev freetype-dev fontconfig-dev - apk add libxcb-dev xcb-util-wm-dev xcb-util-cursor-dev yaml-dev - apk add wayland-dev wayland-protocols wlroots-dev - - apk add json-c-dev libmpdclient-dev alsa-lib-dev pulseaudio-dev + - apk add json-c-dev libmpdclient-dev alsa-lib-dev pulseaudio-dev pipewire-dev - apk add ttf-dejavu - apk add git - apk add flex bison diff --git a/.woodpecker.yml b/.woodpecker.yml index 0a5117a..02fd5f6 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -37,7 +37,7 @@ pipeline: - apk add pixman-dev freetype-dev fontconfig-dev - apk add libxcb-dev xcb-util-wm-dev xcb-util-cursor-dev yaml-dev - apk add wayland-dev wayland-protocols wlroots-dev - - apk add json-c-dev libmpdclient-dev alsa-lib-dev pulseaudio-dev + - apk add json-c-dev libmpdclient-dev alsa-lib-dev pulseaudio-dev pipewire-dev - apk add ttf-dejavu - apk add git - apk add flex bison From 6027b2728bceceab3b2287eb592c64f7ad6f0f9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 13 Dec 2022 10:47:07 +0100 Subject: [PATCH 408/611] =?UTF-8?q?ci=20(sr.ht):=20it=E2=80=99s=20pulseaud?= =?UTF-8?q?io-dev,=20not=20libpulse-dev?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .builds/alpine-x64.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.builds/alpine-x64.yml b/.builds/alpine-x64.yml index 2e00bde..e4ad80c 100644 --- a/.builds/alpine-x64.yml +++ b/.builds/alpine-x64.yml @@ -18,8 +18,8 @@ packages: - wlroots-dev - json-c-dev - libmpdclient-dev - - libpulse-dev - alsa-lib-dev + - pulseaudio-dev - pipewire-dev - ttf-dejavu - gcovr From f5cfc103d0b54ffc55e6e9bd0a06b6f903232f83 Mon Sep 17 00:00:00 2001 From: Ogromny Date: Tue, 13 Dec 2022 15:18:41 +0100 Subject: [PATCH 409/611] modules/dwl: new module --- CHANGELOG.md | 2 + doc/meson.build | 2 +- doc/yambar-modules-dwl.5.scd | 95 +++++++ meson.build | 4 +- meson_options.txt | 3 + modules/dwl.c | 487 +++++++++++++++++++++++++++++++++++ modules/meson.build | 6 + plugin.c | 6 + yml.c | 23 +- yml.h | 2 + 10 files changed, 625 insertions(+), 5 deletions(-) create mode 100644 doc/yambar-modules-dwl.5.scd create mode 100644 modules/dwl.c diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ff593b..38a0b93 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,11 +32,13 @@ * mpd: `file` tag ([#219][219]). * pipewire: add a new module for pipewire ([#224][224]) * on-click: support `next`/`previous` mouse buttons ([#228][228]). +* dwl: add a new module for DWL ([#218][218]) [153]: https://codeberg.org/dnkl/yambar/issues/153 [159]: https://codeberg.org/dnkl/yambar/issues/159 [200]: https://codeberg.org/dnkl/yambar/issues/200 [202]: https://codeberg.org/dnkl/yambar/issues/202 +[218]: https://codeberg.org/dnkl/yambar/pulls/218 [219]: https://codeberg.org/dnkl/yambar/pulls/219 [223]: https://codeberg.org/dnkl/yambar/pulls/223 [224]: https://codeberg.org/dnkl/yambar/pulls/224 diff --git a/doc/meson.build b/doc/meson.build index 0d62cb5..e561ef3 100644 --- a/doc/meson.build +++ b/doc/meson.build @@ -6,7 +6,7 @@ scdoc_prog = find_program(scdoc.get_variable('scdoc'), native: true) foreach man_src : ['yambar.1.scd', 'yambar.5.scd', 'yambar-decorations.5.scd', 'yambar-modules-alsa.5.scd', 'yambar-modules-backlight.5.scd', 'yambar-modules-battery.5.scd', 'yambar-modules-clock.5.scd', - 'yambar-modules-disk-io.5.scd', + 'yambar-modules-disk-io.5.scd', 'yambar-modules-dwl.5.scd', 'yambar-modules-foreign-toplevel.5.scd', 'yambar-modules-i3.5.scd', 'yambar-modules-label.5.scd', 'yambar-modules-mpd.5.scd', 'yambar-modules-network.5.scd', diff --git a/doc/yambar-modules-dwl.5.scd b/doc/yambar-modules-dwl.5.scd new file mode 100644 index 0000000..4e41691 --- /dev/null +++ b/doc/yambar-modules-dwl.5.scd @@ -0,0 +1,95 @@ +yambar-modules-dwl(5) + +# NAME +dwl - This module provides information about dwl tags, and information. + +# DESCRIPTION + +This module provides a map of each tags present in dwl. + +Each tags has its _id_, its status (_selected_, _empty_, _urgent_) +and the global data like _title_, _fullscreen_, _floating_, +_selmon_, and _layout_). The tags start a 1. For needs where +you only want information about the global data and not the _tags_, +there is a tag with the id _0_ that contains only the global data. + +This module will track *only* the monitor where yambar was launched on. +If you have a multi monitor setup, please launch yambar on each of your +monitors. + +Please, be aware that only *one instance* of this module is supported. +Running multiple instances at the same time may result in +*undefined behavior*. + +# TAGS + +[[ *Name* +:[ *Type* +:[ *Description* +| id +: int +: Dwl tag id. +| selected +: bool +: True if the tag is currently selected. +| empty +: bool +: True if there are no windows in the tag. +| urgent +: bool +: True if the tag has the urgent flag set. +| title +: string +: The currently focused window's title. +| fullscreen +: bool +: True if there is a fullscreen window in the current tag. +| floating +: bool +: True if there is a floating window in the current tag. +| selmon +: bool +: True if the monitor is actually focused. +| layout +: string +: The actual layout name of the tag. + +# CONFIGURATION + +[[ *Name* +:[ *Type* +:[ *Req* +:[ *Description* +| number-of-tags +: int +: yes +: The number of defined tags in the dwl `config.def.h`. +| dwl-info-filename +: string +: yes +: The filepath to the log emitted by dwl when running. + +# EXAMPLES + +``` +bar: + left: + - dwl: + number-of-tags: 9 + dwl-info-filename: "/home/ogromny/dwl_info" + content: + list: + items: + - map: + conditions: + selected: {string: {text: "-> {id}"}} + ~empty: {string: {text: "{id}"}} + urgent: {string: {text: "=> {id} <="}} + # default tag + id == 0: {string: {text: "{layout} {title}"}} +``` + +# SEE ALSO + +*yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5) + diff --git a/meson.build b/meson.build index d480929..5c14fd4 100644 --- a/meson.build +++ b/meson.build @@ -135,6 +135,7 @@ yambar = executable( plugin_mpd_enabled? '-DPLUGIN_ENABLED_MPD':[], plugin_pulse_enabled? '-DPLUGIN_ENABLED_PULSE':[], plugin_pipewire_enabled? '-DPLUGIN_ENABLED_PIPEWIRE':[], + plugin_dwl_enabled? '-DPLUGIN_ENABLED_DWL':[], ], build_rpath: '$ORIGIN/modules:$ORIGIN/decorations:$ORIGIN/particles', export_dynamic: true, @@ -175,7 +176,8 @@ summary( { 'Music Player Daemon (MPD)': plugin_mpd_enabled, 'PulseAudio': plugin_pulse_enabled, - 'Pipewire': plugin_pipewire_enabled + 'Pipewire': plugin_pipewire_enabled, + 'DWL (dwm for wayland)': plugin_dwl_enabled, }, section: 'Optional modules', bool_yn: true diff --git a/meson_options.txt b/meson_options.txt index bcfce60..bd77f77 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -14,3 +14,6 @@ option( option( 'plugin-pipewire', type: 'feature', value: 'auto', description: 'Pipewire support') +option( + 'plugin-dwl', type: 'feature', value: 'auto', + description: 'DWL (dwm for wayland) support') diff --git a/modules/dwl.c b/modules/dwl.c new file mode 100644 index 0000000..6c8ccff --- /dev/null +++ b/modules/dwl.c @@ -0,0 +1,487 @@ +#include +#include +#include +#include +#include + +#define ARR_LEN(x) (sizeof((x)) / sizeof((x)[0])) + +#include "../config-verify.h" +#include "../config.h" +#include "../log.h" +#include "../module.h" +#include "../particles/dynlist.h" +#include "../plugin.h" + +#define LOG_MODULE "dwl" +#define LOG_ENABLE_DBG 0 + +struct dwl_tag { + int id; + bool selected; + bool empty; + bool urgent; +}; + +struct private +{ + struct particle *label; + + char const *monitor; + + unsigned int number_of_tags; + char *dwl_info_filename; + + /* dwl data */ + char *title; + bool fullscreen; + bool floating; + bool selmon; + tll(struct dwl_tag *) tags; + char *layout; +}; + +enum LINE_MODE { + LINE_MODE_0, + LINE_MODE_TITLE, + LINE_MODE_FULLSCREEN, + LINE_MODE_FLOATING, + LINE_MODE_SELMON, + LINE_MODE_TAGS, + LINE_MODE_LAYOUT, +}; + +static void +destroy(struct module *module) +{ + struct private *private = module->private; + private->label->destroy(private->label); + + tll_free_and_free(private->tags, free); + free(private->dwl_info_filename); + free(private->title); + free(private->layout); + free(private); + + module_default_destroy(module); +} + +static char const * +description(struct module *module) +{ + return "dwl"; +} + +static struct exposable * +content(struct module *module) +{ + struct private const *private = module->private; + mtx_lock(&module->lock); + + size_t i = 0; + /* + 1 for `default` tag */ + struct exposable *exposable[tll_length(private->tags) + 1]; + tll_foreach(private->tags, it) + { + struct tag_set tags = { + .tags = (struct tag*[]){ + tag_new_string(module, "title", private->title), + tag_new_bool(module, "fullscreen", private->fullscreen), + tag_new_bool(module, "floating", private->floating), + tag_new_bool(module, "selmon", private->selmon), + tag_new_string(module, "layout", private->layout), + tag_new_int(module, "id", it->item->id), + tag_new_bool(module, "selected", it->item->selected), + tag_new_bool(module, "empty", it->item->empty), + tag_new_bool(module, "urgent", it->item->urgent), + }, + .count = 9, + }; + exposable[i++] = private->label->instantiate(private->label, &tags); + tag_set_destroy(&tags); + } + + /* default tag (used for title, layout, etc) */ + struct tag_set tags = { + .tags = (struct tag*[]){ + tag_new_string(module, "title", private->title), + tag_new_bool(module, "fullscreen", private->fullscreen), + tag_new_bool(module, "floating", private->floating), + tag_new_bool(module, "selmon", private->selmon), + tag_new_string(module, "layout", private->layout), + tag_new_int(module, "id", 0), + tag_new_bool(module, "selected", false), + tag_new_bool(module, "empty", true), + tag_new_bool(module, "urgent", false), + }, + .count = 9, + }; + exposable[i++] = private->label->instantiate(private->label, &tags); + tag_set_destroy(&tags); + + mtx_unlock(&module->lock); + return dynlist_exposable_new(exposable, i, 0, 0); +} + +static struct dwl_tag * +dwl_tag_find_or_create(struct private *private, uint32_t id) +{ + tll_foreach(private->tags, it) + { + if (it->item->id == id) + return it->item; + } + + /* No need to order the tag, `print_status` from dwl already orders tags */ + struct dwl_tag *dwl_tag = calloc(1, sizeof(struct dwl_tag)); + dwl_tag->id = id; + tll_push_back(private->tags, dwl_tag); + return dwl_tag; +} + +static void +process_line(char *line, struct module *module) +{ + struct private *private = module->private; + enum LINE_MODE line_mode = LINE_MODE_0; + + /* Remove \n */ + line[strcspn(line, "\n")] = '\0'; + + /* Split line by space */ + size_t index = 1; + char *save_pointer = NULL; + char *string = strtok_r(line, " ", &save_pointer); + while (string != NULL) { + /* dwl logs are formatted like this + * $1 -> monitor + * $2 -> action + * $3 -> arg1 + * $4 -> arg2 + * ... */ + + /* monitor */ + if (index == 1) { + /* Not our monitor */ + if (strcmp(string, private->monitor) != 0) + break; + } + /* action */ + else if (index == 2) { + if (strcmp(string, "title") == 0) + line_mode = LINE_MODE_TITLE; + else if (strcmp(string, "fullscreen") == 0) + line_mode = LINE_MODE_FULLSCREEN; + else if (strcmp(string, "floating") == 0) + line_mode = LINE_MODE_FLOATING; + else if (strcmp(string, "selmon") == 0) + line_mode = LINE_MODE_SELMON; + else if (strcmp(string, "tags") == 0) + line_mode = LINE_MODE_TAGS; + else if (strcmp(string, "layout") == 0) + line_mode = LINE_MODE_LAYOUT; + else { + LOG_WARN("UNKNOWN action, please open an issue on https://codeberg.org/dnkl/yambar"); + return; + } + } + /* args */ + else { + if (line_mode == LINE_MODE_TAGS) { + static uint32_t occupied, selected, client_tags, urgent; + static uint32_t *target = NULL; + + /* dwl tags action log are formatted like this + * $3 -> occupied + * $4 -> tags + * $5 -> clientTags (not needed) + * $6 -> urgent */ + if (index == 3) + target = &occupied; + else if (index == 4) + target = &selected; + else if (index == 5) + target = &client_tags; + else if (index == 6) + target = &urgent; + + /* No need to check error IMHO */ + *target = strtoul(string, NULL, 10); + + /* Populate informations */ + if (index == 6) { + for (size_t id = 1; id <= private->number_of_tags; ++id) { + uint32_t mask = 1 << (id - 1); + + struct dwl_tag *dwl_tag = dwl_tag_find_or_create(private, id); + dwl_tag->selected = mask & selected; + dwl_tag->empty = !(mask & occupied); + dwl_tag->urgent = mask & urgent; + } + } + } else + switch (line_mode) { + case LINE_MODE_TITLE: + free(private->title); + private->title = strdup(string); + break; + case LINE_MODE_FULLSCREEN: + private->fullscreen = (strcmp(string, "0") != 0); + break; + case LINE_MODE_FLOATING: + private->floating = (strcmp(string, "0") != 0); + break; + case LINE_MODE_SELMON: + private->selmon = (strcmp(string, "0") != 0); + break; + case LINE_MODE_LAYOUT: + free(private->layout); + private->layout = strdup(string); + break; + default:; + assert(false); /* unreachable */ + } + } + + string = strtok_r(NULL, " ", &save_pointer); + ++index; + } +} + +static int +file_read_content(FILE *file, struct module *module) +{ + static char buffer[1024]; + + errno = 0; + while (fgets(buffer, ARR_LEN(buffer), file) != NULL) + process_line(buffer, module); + + fseek(file, 0, SEEK_END); + + /* Check whether error has been */ + if (ferror(file) != 0) { + LOG_ERRNO("unable to read file's content."); + return 1; + } + + return 0; +} + +static void +file_seek_to_last_n_lines(FILE *file, int number_of_lines) +{ + if (number_of_lines == 0 || file == NULL) + return; + + fseek(file, 0, SEEK_END); + + long position = ftell(file); + while (position > 0) { + /* Cannot go less than position 0 */ + if (fseek(file, --position, SEEK_SET) == EINVAL) + break; + + if (fgetc(file) == '\n') + if (number_of_lines-- == 0) + break; + } +} + +static int +run_init(int *inotify_fd, int *inotify_wd, FILE **file, char *dwl_info_filename) +{ + *inotify_fd = inotify_init(); + if (*inotify_fd == -1) { + LOG_ERRNO("unable to create inotify fd."); + return -1; + } + + *inotify_wd = inotify_add_watch(*inotify_fd, dwl_info_filename, IN_MODIFY); + if (*inotify_wd == -1) { + close(*inotify_fd); + LOG_ERRNO("unable to add watch to inotify fd."); + return 1; + } + + *file = fopen(dwl_info_filename, "r"); + if (*file == NULL) { + inotify_rm_watch(*inotify_fd, *inotify_wd); + close(*inotify_fd); + LOG_ERRNO("unable to open file."); + return 1; + } + + return 0; +} + +static int +run_clean(int inotify_fd, int inotify_wd, FILE *file) +{ + if (inotify_fd != -1) { + if (inotify_wd != -1) + inotify_rm_watch(inotify_fd, inotify_wd); + close(inotify_fd); + } + + if (file != NULL) { + if (fclose(file) == EOF) { + LOG_ERRNO("unable to close file."); + return 1; + } + } + + return 0; +}; + +static int +run(struct module *module) +{ + struct private *private = module->private; + + /* Ugly, but I didn't find better way for waiting + * the monitor's name to be set */ + do { + private->monitor = module->bar->output_name(module->bar); + usleep(50); + } while (private->monitor == NULL); + + int inotify_fd = -1, inotify_wd = -1; + FILE *file = NULL; + if (run_init(&inotify_fd, &inotify_wd, &file, private->dwl_info_filename) != 0) + return 1; + + /* Dwl output is 6 lines per monitor, so let's assume that nobody has + * more than 5 monitors (6 * 5 = 30) */ + mtx_lock(&module->lock); + file_seek_to_last_n_lines(file, 30); + if (file_read_content(file, module) != 0) { + mtx_unlock(&module->lock); + return run_clean(inotify_fd, inotify_wd, file); + } + mtx_unlock(&module->lock); + + module->bar->refresh(module->bar); + + while (true) { + struct pollfd fds[] = { + (struct pollfd){.fd = module->abort_fd, .events = POLLIN}, + (struct pollfd){.fd = inotify_fd, .events = POLLIN}, + }; + + if (poll(fds, ARR_LEN(fds), -1) == -1) { + if (errno == EINTR) + continue; + + LOG_ERRNO("unable to poll."); + break; + } + + if (fds[0].revents & POLLIN) + break; + + /* fds[1] (inotify_fd) must be POLLIN otherwise issue happen'd */ + if (!(fds[1].revents & POLLIN)) { + LOG_ERR("expected POLLIN revent"); + break; + } + + /* Block until event */ + static char buffer[1024]; + ssize_t length = read(inotify_fd, buffer, ARR_LEN(buffer)); + + if (length == 0) + break; + + if (length == -1) { + if (errno == EAGAIN) + continue; + + LOG_ERRNO("unable to read %s", private->dwl_info_filename); + break; + } + + mtx_lock(&module->lock); + if (file_read_content(file, module) != 0) { + mtx_unlock(&module->lock); + break; + } + mtx_unlock(&module->lock); + + module->bar->refresh(module->bar); + } + + return run_clean(inotify_fd, inotify_wd, file); +} + +static struct module * +dwl_new(struct particle *label, int number_of_tags, char const *dwl_info_filename) +{ + struct private *private = calloc(1, sizeof(struct private)); + private->label = label; + private->number_of_tags = number_of_tags; + private->dwl_info_filename = strdup(dwl_info_filename); + + struct module *module = module_common_new(); + module->private = private; + module->run = &run; + module->destroy = &destroy; + module->content = &content; + module->description = &description; + + return module; +} + +static struct module * +from_conf(struct yml_node const *node, struct conf_inherit inherited) +{ + struct yml_node const *content = yml_get_value(node, "content"); + struct yml_node const *number_of_tags = yml_get_value(node, "number-of-tags"); + struct yml_node const *dwl_info_filename = yml_get_value(node, "dwl-info-filename"); + + return dwl_new(conf_to_particle(content, inherited), yml_value_as_int(number_of_tags), + yml_value_as_string(dwl_info_filename)); +} + +static bool +verify_conf(keychain_t *keychain, struct yml_node const *node) +{ + + static struct attr_info const attrs[] = { + {"number-of-tags", true, &conf_verify_unsigned}, + {"dwl-info-filename", true, &conf_verify_string}, + MODULE_COMMON_ATTRS, + }; + + if (!conf_verify_dict(keychain, node, attrs)) + return false; + + /* No need to check whether is `number_of_tags` is a int + * because `conf_verify_unsigned` already did it */ + struct yml_node const *key = yml_get_key(node, "number-of-tags"); + struct yml_node const *value = yml_get_value(node, "number-of-tags"); + if (yml_value_as_int(value) == 0) { + LOG_ERR("%s: %s must not be 0", conf_err_prefix(keychain, key), yml_value_as_string(key)); + return false; + } + + /* No need to check whether is `dwl_info_filename` is a string + * because `conf_verify_string` already did it */ + key = yml_get_key(node, "dwl-info-filename"); + value = yml_get_value(node, "dwl-info-filename"); + if (strlen(yml_value_as_string(value)) == 0) { + LOG_ERR("%s: %s must not be empty", conf_err_prefix(keychain, key), yml_value_as_string(key)); + return false; + } + + return true; +} + +struct module_iface const module_dwl_iface = { + .verify_conf = &verify_conf, + .from_conf = &from_conf, +}; + +#if defined(CORE_PLUGINS_AS_SHARED_LIBRARIES) +extern struct module_iface const iface __attribute__((weak, alias("module_dwl_iface"))); +#endif diff --git a/modules/meson.build b/modules/meson.build index ddfd747..56a4757 100644 --- a/modules/meson.build +++ b/modules/meson.build @@ -16,6 +16,8 @@ plugin_pipewire_enabled = pipewire.found() pulse = dependency('libpulse', required: get_option('plugin-pulse')) plugin_pulse_enabled = pulse.found() +plugin_dwl_enabled = get_option('plugin-dwl').allowed() + # Module name -> (source-list, dep-list) mod_data = { 'alsa': [[], [m, alsa]], @@ -33,6 +35,10 @@ mod_data = { 'sway-xkb': [['i3-common.c', 'i3-common.h'], [dynlist, json]], } +if plugin_dwl_enabled + mod_data += {'dwl': [[], [dynlist]]} +endif + if plugin_mpd_enabled mod_data += {'mpd': [[], [mpd]]} endif diff --git a/plugin.c b/plugin.c index a17f1ae..6320434 100644 --- a/plugin.c +++ b/plugin.c @@ -37,6 +37,9 @@ EXTERN_MODULE(backlight); EXTERN_MODULE(battery); EXTERN_MODULE(clock); EXTERN_MODULE(disk_io); +#if defined(PLUGIN_ENABLED_DWL) +EXTERN_MODULE(dwl); +#endif EXTERN_MODULE(foreign_toplevel); EXTERN_MODULE(i3); EXTERN_MODULE(label); @@ -126,6 +129,9 @@ init(void) REGISTER_CORE_MODULE(battery, battery); REGISTER_CORE_MODULE(clock, clock); REGISTER_CORE_MODULE(disk-io, disk_io); +#if defined(PLUGIN_ENABLED_DWL) + REGISTER_CORE_MODULE(dwl, dwl); +#endif #if defined(HAVE_PLUGIN_foreign_toplevel) REGISTER_CORE_MODULE(foreign-toplevel, foreign_toplevel); #endif diff --git a/yml.c b/yml.c index 6afa8d6..b3f3d42 100644 --- a/yml.c +++ b/yml.c @@ -640,9 +640,11 @@ yml_is_list(const struct yml_node *node) return node->type == LIST; } - const struct yml_node * -yml_get_value(const struct yml_node *node, const char *_path) +static struct yml_node const * +yml_get_(struct yml_node const *node, char const *_path, bool value) { + /* value: true for value, false for key */ + if (node != NULL && node->type == ROOT) node = node->root.root; @@ -662,7 +664,11 @@ yml_get_value(const struct yml_node *node, const char *_path) if (strcmp(it->item.key->scalar.value, part) == 0) { if (next_part == NULL) { free(path); - return it->item.value; + + if (value) + return it->item.value; + else + return it->item.key; } node = it->item.value; @@ -675,6 +681,17 @@ yml_get_value(const struct yml_node *node, const char *_path) return NULL; } +const struct yml_node * +yml_get_value(const struct yml_node *node, const char *_path) +{ + return yml_get_(node, _path, true); +} + +struct yml_node const * +yml_get_key(struct yml_node const *node, char const *_path) { + return yml_get_(node, _path, false); +} + struct yml_list_iter yml_list_iter(const struct yml_node *list) { diff --git a/yml.h b/yml.h index 476d469..0e5eca4 100644 --- a/yml.h +++ b/yml.h @@ -13,6 +13,8 @@ bool yml_is_list(const struct yml_node *node); const struct yml_node *yml_get_value( const struct yml_node *node, const char *path); +const struct yml_node *yml_get_key( + struct yml_node const *node, char const *path); struct yml_list_iter { const struct yml_node *node; From 4631e75e28a2171e46ace6ed5147ffdb49750e67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 13 Dec 2022 15:58:59 +0100 Subject: [PATCH 410/611] meson: require version >= 0.59 Required by feature_option.allowed() --- CHANGELOG.md | 2 +- meson.build | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 38a0b93..a1f345f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,7 +47,7 @@ ### Changed -* Minimum required meson version is now 0.58. +* Minimum required meson version is now 0.59. * Float tags are now treated as floats instead of integers when formatted with the `kb`/`kib`/`mb`/`mib`/`gb`/`gib` string particle formatters. diff --git a/meson.build b/meson.build index 5c14fd4..27ba00b 100644 --- a/meson.build +++ b/meson.build @@ -1,7 +1,7 @@ project('yambar', 'c', version: '1.8.0', license: 'MIT', - meson_version: '>=0.58.0', + meson_version: '>=0.59.0', default_options: ['c_std=c18', 'warning_level=1', 'werror=true', From 7ddd009a5caaed24ed8c04f2ddce00808c138ba7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 13 Dec 2022 15:59:40 +0100 Subject: [PATCH 411/611] meson: sort plugin list in summary output --- meson.build | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/meson.build b/meson.build index 27ba00b..d2ae70c 100644 --- a/meson.build +++ b/meson.build @@ -174,10 +174,10 @@ summary( summary( { - 'Music Player Daemon (MPD)': plugin_mpd_enabled, - 'PulseAudio': plugin_pulse_enabled, - 'Pipewire': plugin_pipewire_enabled, 'DWL (dwm for wayland)': plugin_dwl_enabled, + 'Music Player Daemon (MPD)': plugin_mpd_enabled, + 'Pipewire': plugin_pipewire_enabled, + 'PulseAudio': plugin_pulse_enabled, }, section: 'Optional modules', bool_yn: true From 6fa9c47c0b38329cfece82deb624fb8d59c4bdaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 13 Dec 2022 16:00:07 +0100 Subject: [PATCH 412/611] =?UTF-8?q?meson:=20summary:=20dwl:=20Wayland=20wi?= =?UTF-8?q?th=20upper=20case=20=E2=80=98W=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index d2ae70c..a26f682 100644 --- a/meson.build +++ b/meson.build @@ -174,7 +174,7 @@ summary( summary( { - 'DWL (dwm for wayland)': plugin_dwl_enabled, + 'DWL (dwm for Wayland)': plugin_dwl_enabled, 'Music Player Daemon (MPD)': plugin_mpd_enabled, 'Pipewire': plugin_pipewire_enabled, 'PulseAudio': plugin_pulse_enabled, From bbd2394601918653f8f21cad57f5694f9b3da26a Mon Sep 17 00:00:00 2001 From: Timur Celik Date: Mon, 5 Jul 2021 17:38:04 +0200 Subject: [PATCH 413/611] modules: Implement workspace rename event A renamed workspace caused yambar to abort in a failed assertion, because workspace lookup was done by name and the `rename` event was not implemented. To resolve this issue this patch implements the `rename` event and as a necessity changes workspace_lookup() to use ids instead of names. --- modules/i3.c | 75 ++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 52 insertions(+), 23 deletions(-) diff --git a/modules/i3.c b/modules/i3.c index e036f97..3e33f95 100644 --- a/modules/i3.c +++ b/modules/i3.c @@ -29,6 +29,7 @@ struct ws_content { }; struct workspace { + int id; char *name; int name_as_int; /* -1 if name is not a decimal number */ bool persistent; @@ -103,8 +104,9 @@ static bool workspace_from_json(const struct json_object *json, struct workspace *ws) { /* Always present */ - struct json_object *name, *output; - if (!json_object_object_get_ex(json, "name", &name) || + struct json_object *id, *name, *output; + if (!json_object_object_get_ex(json, "id", &id) || + !json_object_object_get_ex(json, "name", &name) || !json_object_object_get_ex(json, "output", &output)) { LOG_ERR("workspace reply/event without 'name' and/or 'output' " @@ -132,6 +134,7 @@ workspace_from_json(const struct json_object *json, struct workspace *ws) int name_as_int = workspace_name_as_int(name_as_string); *ws = (struct workspace) { + .id = json_object_get_int(id), .name = strdup(name_as_string), .name_as_int = name_as_int, .persistent = false, @@ -222,12 +225,12 @@ workspace_add(struct private *m, struct workspace ws) } static void -workspace_del(struct private *m, const char *name) +workspace_del(struct private *m, int id) { tll_foreach(m->workspaces, it) { struct workspace *ws = &it->item; - if (strcmp(ws->name, name) != 0) + if (ws->id != id) continue; workspace_free(ws); @@ -237,11 +240,11 @@ workspace_del(struct private *m, const char *name) } static struct workspace * -workspace_lookup(struct private *m, const char *name) +workspace_lookup(struct private *m, int id) { tll_foreach(m->workspaces, it) { struct workspace *ws = &it->item; - if (strcmp(ws->name, name) == 0) + if (ws->id == id) return ws; } return NULL; @@ -280,12 +283,12 @@ handle_subscribe_reply(int type, const struct json_object *json, void *_m) static bool workspace_update_or_add(struct private *m, const struct json_object *ws_json) { - struct json_object *name; - if (!json_object_object_get_ex(ws_json, "name", &name)) + struct json_object *_id; + if (!json_object_object_get_ex(ws_json, "id", &_id)) return false; - const char *name_as_string = json_object_get_string(name); - struct workspace *already_exists = workspace_lookup(m, name_as_string); + const int id = json_object_get_int(_id); + struct workspace *already_exists = workspace_lookup(m, id); if (already_exists != NULL) { bool persistent = already_exists->persistent; @@ -350,6 +353,7 @@ handle_workspace_event(int type, const struct json_object *json, void *_mod) bool is_init = strcmp(change_str, "init") == 0; bool is_empty = strcmp(change_str, "empty") == 0; bool is_focused = strcmp(change_str, "focus") == 0; + bool is_rename = strcmp(change_str, "rename") == 0; bool is_urgent = strcmp(change_str, "urgent") == 0; bool is_reload = strcmp(change_str, "reload") == 0; @@ -358,15 +362,15 @@ handle_workspace_event(int type, const struct json_object *json, void *_mod) return true; } - struct json_object *current, *_current_name; + struct json_object *current, *_current_id; if (!json_object_object_get_ex(json, "current", ¤t) || - !json_object_object_get_ex(current, "name", &_current_name)) + !json_object_object_get_ex(current, "id", &_current_id)) { - LOG_ERR("workspace event without 'current' and/or 'name' properties"); + LOG_ERR("workspace event without 'current' and/or 'id' properties"); return false; } - const char *current_name = json_object_get_string(_current_name); + int current_id = json_object_get_int(_current_id); mtx_lock(&mod->lock); @@ -376,23 +380,22 @@ handle_workspace_event(int type, const struct json_object *json, void *_mod) } else if (is_empty) { - struct workspace *ws = workspace_lookup(m, current_name); + struct workspace *ws = workspace_lookup(m, current_id); assert(ws != NULL); if (!ws->persistent) - workspace_del(m, current_name); + workspace_del(m, current_id); else { workspace_free(ws); - ws->name = strdup(current_name); ws->empty = true; assert(ws->persistent); } } else if (is_focused) { - struct json_object *old, *_old_name, *urgent; + struct json_object *old, *_old_id, *urgent; if (!json_object_object_get_ex(json, "old", &old) || - !json_object_object_get_ex(old, "name", &_old_name) || + !json_object_object_get_ex(old, "id", &_old_id) || !json_object_object_get_ex(current, "urgent", &urgent)) { LOG_ERR("workspace 'focused' event without 'old', 'name' and/or 'urgent' property"); @@ -400,7 +403,7 @@ handle_workspace_event(int type, const struct json_object *json, void *_mod) return false; } - struct workspace *w = workspace_lookup(m, current_name); + struct workspace *w = workspace_lookup(m, current_id); assert(w != NULL); LOG_DBG("w: %s", w->name); @@ -417,12 +420,38 @@ handle_workspace_event(int type, const struct json_object *json, void *_mod) w->visible = true; /* Old workspace is no longer focused */ - const char *old_name = json_object_get_string(_old_name); - struct workspace *old_w = workspace_lookup(m, old_name); + int old_id = json_object_get_int(_old_id); + struct workspace *old_w = workspace_lookup(m, old_id); if (old_w != NULL) old_w->focused = false; } + else if (is_rename) { + struct workspace *w = workspace_lookup(m, current_id); + assert(w != NULL); + + struct json_object *_current_name; + if (!json_object_object_get_ex(current, "name", &_current_name)) { + LOG_ERR("workspace 'rename' event without 'name' property"); + mtx_unlock(&mod->lock); + return false; + } + + free(w->name); + w->name = strdup(json_object_get_string(_current_name)); + w->name_as_int = workspace_name_as_int(w->name); + + /* Re-add the workspace to ensure correct sorting */ + struct workspace ws = *w; + tll_foreach(m->workspaces, it) { + if (it->item.id == current_id) { + tll_remove(m->workspaces, it); + break; + } + } + workspace_add(m, ws); + } + else if (is_urgent) { struct json_object *urgent; if (!json_object_object_get_ex(current, "urgent", &urgent)) { @@ -431,7 +460,7 @@ handle_workspace_event(int type, const struct json_object *json, void *_mod) return false; } - struct workspace *w = workspace_lookup(m, current_name); + struct workspace *w = workspace_lookup(m, current_id); w->urgent = json_object_get_boolean(urgent); } From 8f89545b32fa3ba1bba8accdaff9abc7c28b978c Mon Sep 17 00:00:00 2001 From: Timur Celik Date: Tue, 6 Jul 2021 11:05:17 +0200 Subject: [PATCH 414/611] modules: Warn for all unknown workspace events --- modules/i3.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/modules/i3.c b/modules/i3.c index 3e33f95..f970b0a 100644 --- a/modules/i3.c +++ b/modules/i3.c @@ -355,12 +355,6 @@ handle_workspace_event(int type, const struct json_object *json, void *_mod) bool is_focused = strcmp(change_str, "focus") == 0; bool is_rename = strcmp(change_str, "rename") == 0; bool is_urgent = strcmp(change_str, "urgent") == 0; - bool is_reload = strcmp(change_str, "reload") == 0; - - if (is_reload) { - LOG_WARN("unimplemented: 'reload' event"); - return true; - } struct json_object *current, *_current_id; if (!json_object_object_get_ex(json, "current", ¤t) || @@ -464,6 +458,10 @@ handle_workspace_event(int type, const struct json_object *json, void *_mod) w->urgent = json_object_get_boolean(urgent); } + else { + LOG_WARN("unimplemented workspace event '%s'", change_str); + } + m->dirty = true; mtx_unlock(&mod->lock); return true; From 24a3b90a01068819297055ed277cf8d78b9130ee Mon Sep 17 00:00:00 2001 From: Timur Celik Date: Tue, 6 Jul 2021 12:06:49 +0200 Subject: [PATCH 415/611] modules: Implement workspace move event Implementing the move event required to pass the IPC socket to `i3_ipc_callback_t`, because we won't get notified about any visibility changes of other workspaces. That's why we query all workspaces again after a focused workspace was moved. --- modules/i3-common.c | 2 +- modules/i3-common.h | 2 +- modules/i3.c | 36 ++++++++++++++++++++++++++++++------ modules/sway-xkb.c | 4 ++-- 4 files changed, 34 insertions(+), 10 deletions(-) diff --git a/modules/i3-common.c b/modules/i3-common.c index 589bfb8..a0769f2 100644 --- a/modules/i3-common.c +++ b/modules/i3-common.c @@ -309,7 +309,7 @@ i3_receive_loop(int abort_fd, int sock, } if (pkt_handler != NULL) - err = !pkt_handler(hdr->type, json, data); + err = !pkt_handler(sock, hdr->type, json, data); else LOG_DBG("no handler for reply/event %d; ignoring", hdr->type); diff --git a/modules/i3-common.h b/modules/i3-common.h index e3d94d1..0cbfa3e 100644 --- a/modules/i3-common.h +++ b/modules/i3-common.h @@ -11,7 +11,7 @@ bool i3_get_socket_address(struct sockaddr_un *addr); bool i3_send_pkg(int sock, int cmd, char *data); -typedef bool (*i3_ipc_callback_t)(int type, const struct json_object *json, void *data); +typedef bool (*i3_ipc_callback_t)(int sock, int type, const struct json_object *json, void *data); struct i3_ipc_callbacks { void (*burst_done)(void *data); diff --git a/modules/i3.c b/modules/i3.c index f970b0a..160913b 100644 --- a/modules/i3.c +++ b/modules/i3.c @@ -251,7 +251,7 @@ workspace_lookup(struct private *m, int id) } static bool -handle_get_version_reply(int type, const struct json_object *json, void *_m) +handle_get_version_reply(int sock, int type, const struct json_object *json, void *_m) { struct json_object *version; if (!json_object_object_get_ex(json, "human_readable", &version)) { @@ -264,7 +264,7 @@ handle_get_version_reply(int type, const struct json_object *json, void *_m) } static bool -handle_subscribe_reply(int type, const struct json_object *json, void *_m) +handle_subscribe_reply(int sock, int type, const struct json_object *json, void *_m) { struct json_object *success; if (!json_object_object_get_ex(json, "success", &success)) { @@ -310,7 +310,7 @@ workspace_update_or_add(struct private *m, const struct json_object *ws_json) } static bool -handle_get_workspaces_reply(int type, const struct json_object *json, void *_mod) +handle_get_workspaces_reply(int sock, int type, const struct json_object *json, void *_mod) { struct module *mod = _mod; struct private *m = mod->private; @@ -337,7 +337,7 @@ err: } static bool -handle_workspace_event(int type, const struct json_object *json, void *_mod) +handle_workspace_event(int sock, int type, const struct json_object *json, void *_mod) { struct module *mod = _mod; struct private *m = mod->private; @@ -354,6 +354,7 @@ handle_workspace_event(int type, const struct json_object *json, void *_mod) bool is_empty = strcmp(change_str, "empty") == 0; bool is_focused = strcmp(change_str, "focus") == 0; bool is_rename = strcmp(change_str, "rename") == 0; + bool is_move = strcmp(change_str, "move") == 0; bool is_urgent = strcmp(change_str, "urgent") == 0; struct json_object *current, *_current_id; @@ -446,6 +447,29 @@ handle_workspace_event(int type, const struct json_object *json, void *_mod) workspace_add(m, ws); } + else if (is_move) { + struct workspace *w = workspace_lookup(m, current_id); + assert(w != NULL); + + struct json_object *_current_output; + if (!json_object_object_get_ex(current, "output", &_current_output)) { + LOG_ERR("workspace 'move' event without 'output' property"); + mtx_unlock(&mod->lock); + return false; + } + + free(w->output); + w->output = strdup(json_object_get_string(_current_output)); + + /* + * If the moved workspace was focused, schedule a full update because + * visibility for other workspaces may have changed. + */ + if (w->focused) { + i3_send_pkg(sock, I3_IPC_MESSAGE_TYPE_GET_WORKSPACES, NULL); + } + } + else if (is_urgent) { struct json_object *urgent; if (!json_object_object_get_ex(current, "urgent", &urgent)) { @@ -472,7 +496,7 @@ err: } static bool -handle_window_event(int type, const struct json_object *json, void *_mod) +handle_window_event(int sock, int type, const struct json_object *json, void *_mod) { struct module *mod = _mod; struct private *m = mod->private; @@ -606,7 +630,7 @@ handle_window_event(int type, const struct json_object *json, void *_mod) } static bool -handle_mode_event(int type, const struct json_object *json, void *_mod) +handle_mode_event(int sock, int type, const struct json_object *json, void *_mod) { struct module *mod = _mod; struct private *m = mod->private; diff --git a/modules/sway-xkb.c b/modules/sway-xkb.c index 3f2e965..269df24 100644 --- a/modules/sway-xkb.c +++ b/modules/sway-xkb.c @@ -94,7 +94,7 @@ content(struct module *mod) } static bool -handle_input_reply(int type, const struct json_object *json, void *_mod) +handle_input_reply(int sock, int type, const struct json_object *json, void *_mod) { struct module *mod = _mod; struct private *m = mod->private; @@ -162,7 +162,7 @@ handle_input_reply(int type, const struct json_object *json, void *_mod) } static bool -handle_input_event(int type, const struct json_object *json, void *_mod) +handle_input_event(int sock, int type, const struct json_object *json, void *_mod) { struct module *mod = _mod; struct private *m = mod->private; From 266a2efbb6f654d095e1c571bc0d25b0ced4ee10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 13 Dec 2022 16:25:55 +0100 Subject: [PATCH 416/611] =?UTF-8?q?changelog:=20sway:=20workspace=20?= =?UTF-8?q?=E2=80=98move=E2=80=99=20and=20=E2=80=98rename=E2=80=99=20event?= =?UTF-8?q?s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a1f345f..ddde62d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,8 @@ * pipewire: add a new module for pipewire ([#224][224]) * on-click: support `next`/`previous` mouse buttons ([#228][228]). * dwl: add a new module for DWL ([#218][218]) +* sway: support for workspace ‘rename’ and ‘move’ events + ([#216][216]). [153]: https://codeberg.org/dnkl/yambar/issues/153 [159]: https://codeberg.org/dnkl/yambar/issues/159 @@ -43,6 +45,7 @@ [223]: https://codeberg.org/dnkl/yambar/pulls/223 [224]: https://codeberg.org/dnkl/yambar/pulls/224 [228]: https://codeberg.org/dnkl/yambar/pulls/228 +[216]: https://codeberg.org/dnkl/yambar/issues/216 ### Changed From 49576a26bf60839f8246c12a6fc09358479b9cab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 13 Dec 2022 16:33:56 +0100 Subject: [PATCH 417/611] =?UTF-8?q?readme:=20add=20=E2=80=98dwl=E2=80=99?= =?UTF-8?q?=20to=20list=20of=20plugins?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index e4b497d..c7b0505 100644 --- a/README.md +++ b/README.md @@ -80,6 +80,7 @@ Available modules: * clock * cpu * disk-io +* dwl * foreign-toplevel * i3 (and Sway) * label From f8f0d7ae99d020ebb42022691263149a536e0c93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 13 Dec 2022 16:36:55 +0100 Subject: [PATCH 418/611] meson_options: sort plugin options --- meson_options.txt | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/meson_options.txt b/meson_options.txt index bd77f77..4290696 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -5,15 +5,12 @@ option( option( 'core-plugins-as-shared-libraries', type: 'boolean', value: false, description: 'Compiles modules, particles and decorations as shared libraries, which are loaded on-demand') -option( - 'plugin-mpd', type: 'feature', value: 'auto', - description: 'Music Player Daemon (MPD) support') -option( - 'plugin-pulse', type: 'feature', value: 'auto', - description: 'PulseAudio support') -option( - 'plugin-pipewire', type: 'feature', value: 'auto', - description: 'Pipewire support') -option( - 'plugin-dwl', type: 'feature', value: 'auto', - description: 'DWL (dwm for wayland) support') + +option('plugin-dwl', type: 'feature', value: 'auto', + description: 'DWL (dwm for wayland) support') +option('plugin-mpd', type: 'feature', value: 'auto', + description: 'Music Player Daemon (MPD) support') +option('plugin-pipewire', type: 'feature', value: 'auto', + description: 'Pipewire support') +option('plugin-pulse', type: 'feature', value: 'auto', + description: 'PulseAudio support') From 4c1398f1a57c6f7beb43bd99def1bfdeef0c82d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 13 Dec 2022 16:40:49 +0100 Subject: [PATCH 419/611] =?UTF-8?q?meson:=20make=20=E2=80=98alsa=E2=80=99?= =?UTF-8?q?=20plugin=20compile=20time=20optional?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- meson.build | 10 ++++++---- meson_options.txt | 1 + modules/meson.build | 10 ++++++++-- plugin.c | 4 ++++ 4 files changed, 19 insertions(+), 6 deletions(-) diff --git a/meson.build b/meson.build index a26f682..f176467 100644 --- a/meson.build +++ b/meson.build @@ -132,10 +132,11 @@ yambar = executable( dependencies: [bar, libepoll, libinotify, pixman, yaml, threads, dl, tllist, fcft] + decorations + particles + modules, c_args: [ - plugin_mpd_enabled? '-DPLUGIN_ENABLED_MPD':[], - plugin_pulse_enabled? '-DPLUGIN_ENABLED_PULSE':[], - plugin_pipewire_enabled? '-DPLUGIN_ENABLED_PIPEWIRE':[], - plugin_dwl_enabled? '-DPLUGIN_ENABLED_DWL':[], + plugin_alsa_enabled ? '-DPLUGIN_ENABLED_ALSA' : [], + plugin_dwl_enabled ? '-DPLUGIN_ENABLED_DWL' : [], + plugin_mpd_enabled ? '-DPLUGIN_ENABLED_MPD' : [], + plugin_pipewire_enabled ? '-DPLUGIN_ENABLED_PIPEWIRE' : [], + plugin_pulse_enabled ? '-DPLUGIN_ENABLED_PULSE' : [], ], build_rpath: '$ORIGIN/modules:$ORIGIN/decorations:$ORIGIN/particles', export_dynamic: true, @@ -174,6 +175,7 @@ summary( summary( { + 'ALSA': plugin_alsa_enabled, 'DWL (dwm for Wayland)': plugin_dwl_enabled, 'Music Player Daemon (MPD)': plugin_mpd_enabled, 'Pipewire': plugin_pipewire_enabled, diff --git a/meson_options.txt b/meson_options.txt index 4290696..fd0718f 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -6,6 +6,7 @@ option( 'core-plugins-as-shared-libraries', type: 'boolean', value: false, description: 'Compiles modules, particles and decorations as shared libraries, which are loaded on-demand') +option('plugin-alsa', type: 'feature', value: 'auto', description: 'ALSA') option('plugin-dwl', type: 'feature', value: 'auto', description: 'DWL (dwm for wayland) support') option('plugin-mpd', type: 'feature', value: 'auto', diff --git a/modules/meson.build b/modules/meson.build index 56a4757..4adc705 100644 --- a/modules/meson.build +++ b/modules/meson.build @@ -2,14 +2,17 @@ module_sdk = declare_dependency(dependencies: [pixman, threads, tllist, fcft]) modules = [] -alsa = dependency('alsa') udev = dependency('libudev') json = dependency('json-c') xcb_xkb = dependency('xcb-xkb', required: get_option('backend-x11')) # Optional deps +alsa = dependency('alsa', required: get_option('plugin-alsa')) +plugin_alsa_enabled = alsa.found() + mpd = dependency('libmpdclient', required: get_option('plugin-mpd')) plugin_mpd_enabled = mpd.found() + pipewire = dependency('libpipewire-0.3', required: get_option('plugin-pipewire')) plugin_pipewire_enabled = pipewire.found() @@ -20,7 +23,6 @@ plugin_dwl_enabled = get_option('plugin-dwl').allowed() # Module name -> (source-list, dep-list) mod_data = { - 'alsa': [[], [m, alsa]], 'backlight': [[], [m, udev]], 'battery': [[], [udev]], 'clock': [[], []], @@ -35,6 +37,10 @@ mod_data = { 'sway-xkb': [['i3-common.c', 'i3-common.h'], [dynlist, json]], } +if plugin_alsa_enabled + mod_data += {'alsa': [[], [m, alsa]]} +endif + if plugin_dwl_enabled mod_data += {'dwl': [[], [dynlist]]} endif diff --git a/plugin.c b/plugin.c index 6320434..a83dbad 100644 --- a/plugin.c +++ b/plugin.c @@ -32,7 +32,9 @@ keychain_t *chain, const struct yml_node *node); \ extern struct deco *plug_name##_from_conf(const struct yml_node *node); +#if defined(PLUGIN_ENABLED_ALSA) EXTERN_MODULE(alsa); +#endif EXTERN_MODULE(backlight); EXTERN_MODULE(battery); EXTERN_MODULE(clock); @@ -124,7 +126,9 @@ init(void) tll_back(plugins).decoration = &deco_##func_prefix##_iface; \ } while (0) +#if defined(PLUGIN_ENABLED_ALSA) REGISTER_CORE_MODULE(alsa, alsa); +#endif REGISTER_CORE_MODULE(backlight, backlight); REGISTER_CORE_MODULE(battery, battery); REGISTER_CORE_MODULE(clock, clock); From 881359183f163046ccc3c9dd4ded63a11e030e0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 13 Dec 2022 16:47:48 +0100 Subject: [PATCH 420/611] =?UTF-8?q?meson:=20make=20=E2=80=98backlight?= =?UTF-8?q?=E2=80=99=20plugin=20compile=20time=20optional?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- meson.build | 2 ++ meson_options.txt | 7 ++++++- modules/meson.build | 10 ++++++++-- plugin.c | 4 ++++ 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/meson.build b/meson.build index f176467..102cbc0 100644 --- a/meson.build +++ b/meson.build @@ -133,6 +133,7 @@ yambar = executable( decorations + particles + modules, c_args: [ plugin_alsa_enabled ? '-DPLUGIN_ENABLED_ALSA' : [], + plugin_backlight_enabled ? '-DPLUGIN_ENABLED_BACKLIGHT' : [], plugin_dwl_enabled ? '-DPLUGIN_ENABLED_DWL' : [], plugin_mpd_enabled ? '-DPLUGIN_ENABLED_MPD' : [], plugin_pipewire_enabled ? '-DPLUGIN_ENABLED_PIPEWIRE' : [], @@ -176,6 +177,7 @@ summary( summary( { 'ALSA': plugin_alsa_enabled, + 'Backlight': plugin_backlight_enabled, 'DWL (dwm for Wayland)': plugin_dwl_enabled, 'Music Player Daemon (MPD)': plugin_mpd_enabled, 'Pipewire': plugin_pipewire_enabled, diff --git a/meson_options.txt b/meson_options.txt index fd0718f..ab8837f 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -6,7 +6,12 @@ option( 'core-plugins-as-shared-libraries', type: 'boolean', value: false, description: 'Compiles modules, particles and decorations as shared libraries, which are loaded on-demand') -option('plugin-alsa', type: 'feature', value: 'auto', description: 'ALSA') +option('plugin-alsa', type: 'feature', value: 'auto', + description: 'ALSA support') +option('plugin-backlight', type: 'feature', value: 'auto', + description: 'Backlight support') +option('plugin-battery', type: 'feature', value: 'auto', + description: 'Battery support') option('plugin-dwl', type: 'feature', value: 'auto', description: 'DWL (dwm for wayland) support') option('plugin-mpd', type: 'feature', value: 'auto', diff --git a/modules/meson.build b/modules/meson.build index 4adc705..e4b7027 100644 --- a/modules/meson.build +++ b/modules/meson.build @@ -2,7 +2,7 @@ module_sdk = declare_dependency(dependencies: [pixman, threads, tllist, fcft]) modules = [] -udev = dependency('libudev') +udev = dependency('udev') json = dependency('json-c') xcb_xkb = dependency('xcb-xkb', required: get_option('backend-x11')) @@ -10,6 +10,9 @@ xcb_xkb = dependency('xcb-xkb', required: get_option('backend-x11')) alsa = dependency('alsa', required: get_option('plugin-alsa')) plugin_alsa_enabled = alsa.found() +udev_backlight = dependency('libudev', required: get_option('plugin-backlight')) +plugin_backlight_enabled = udev_backlight.found() + mpd = dependency('libmpdclient', required: get_option('plugin-mpd')) plugin_mpd_enabled = mpd.found() @@ -23,7 +26,6 @@ plugin_dwl_enabled = get_option('plugin-dwl').allowed() # Module name -> (source-list, dep-list) mod_data = { - 'backlight': [[], [m, udev]], 'battery': [[], [udev]], 'clock': [[], []], 'cpu': [[], []], @@ -41,6 +43,10 @@ if plugin_alsa_enabled mod_data += {'alsa': [[], [m, alsa]]} endif +if plugin_backlight_enabled + mod_data += {'backlight': [[], [m, udev_backlight]]} +endif + if plugin_dwl_enabled mod_data += {'dwl': [[], [dynlist]]} endif diff --git a/plugin.c b/plugin.c index a83dbad..8421176 100644 --- a/plugin.c +++ b/plugin.c @@ -35,7 +35,9 @@ #if defined(PLUGIN_ENABLED_ALSA) EXTERN_MODULE(alsa); #endif +#if defined(PLUGIN_ENABLED_BACKLIGHT) EXTERN_MODULE(backlight); +#endif EXTERN_MODULE(battery); EXTERN_MODULE(clock); EXTERN_MODULE(disk_io); @@ -129,7 +131,9 @@ init(void) #if defined(PLUGIN_ENABLED_ALSA) REGISTER_CORE_MODULE(alsa, alsa); #endif +#if defined(PLUGIN_ENABLED_BACKLIGHT) REGISTER_CORE_MODULE(backlight, backlight); +#endif REGISTER_CORE_MODULE(battery, battery); REGISTER_CORE_MODULE(clock, clock); REGISTER_CORE_MODULE(disk-io, disk_io); From aeef3eca0ece4994169f1fac34533133f56ca29f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 13 Dec 2022 16:49:37 +0100 Subject: [PATCH 421/611] =?UTF-8?q?meson:=20make=20=E2=80=98battery?= =?UTF-8?q?=E2=80=99=20plugin=20compile=20time=20optional?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- meson.build | 2 ++ modules/meson.build | 8 +++++++- plugin.c | 4 ++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 102cbc0..9d2128a 100644 --- a/meson.build +++ b/meson.build @@ -134,6 +134,7 @@ yambar = executable( c_args: [ plugin_alsa_enabled ? '-DPLUGIN_ENABLED_ALSA' : [], plugin_backlight_enabled ? '-DPLUGIN_ENABLED_BACKLIGHT' : [], + plugin_battery_enabled ? '-DPLUGIN_ENABLED_BATTERY' : [], plugin_dwl_enabled ? '-DPLUGIN_ENABLED_DWL' : [], plugin_mpd_enabled ? '-DPLUGIN_ENABLED_MPD' : [], plugin_pipewire_enabled ? '-DPLUGIN_ENABLED_PIPEWIRE' : [], @@ -178,6 +179,7 @@ summary( { 'ALSA': plugin_alsa_enabled, 'Backlight': plugin_backlight_enabled, + 'Battery': plugin_battery_enabled, 'DWL (dwm for Wayland)': plugin_dwl_enabled, 'Music Player Daemon (MPD)': plugin_mpd_enabled, 'Pipewire': plugin_pipewire_enabled, diff --git a/modules/meson.build b/modules/meson.build index e4b7027..4252364 100644 --- a/modules/meson.build +++ b/modules/meson.build @@ -13,6 +13,9 @@ plugin_alsa_enabled = alsa.found() udev_backlight = dependency('libudev', required: get_option('plugin-backlight')) plugin_backlight_enabled = udev_backlight.found() +udev_battery = dependency('libudev', required: get_option('plugin-battery')) +plugin_battery_enabled = udev_battery.found() + mpd = dependency('libmpdclient', required: get_option('plugin-mpd')) plugin_mpd_enabled = mpd.found() @@ -26,7 +29,6 @@ plugin_dwl_enabled = get_option('plugin-dwl').allowed() # Module name -> (source-list, dep-list) mod_data = { - 'battery': [[], [udev]], 'clock': [[], []], 'cpu': [[], []], 'disk-io': [[], [dynlist]], @@ -47,6 +49,10 @@ if plugin_backlight_enabled mod_data += {'backlight': [[], [m, udev_backlight]]} endif +if plugin_battery_enabled + mod_data += {'battery': [[], [udev_battery]]} +endif + if plugin_dwl_enabled mod_data += {'dwl': [[], [dynlist]]} endif diff --git a/plugin.c b/plugin.c index 8421176..6f7e5cc 100644 --- a/plugin.c +++ b/plugin.c @@ -38,7 +38,9 @@ EXTERN_MODULE(alsa); #if defined(PLUGIN_ENABLED_BACKLIGHT) EXTERN_MODULE(backlight); #endif +#if defined(PLUGIN_ENABLED_BATTERY) EXTERN_MODULE(battery); +#endif EXTERN_MODULE(clock); EXTERN_MODULE(disk_io); #if defined(PLUGIN_ENABLED_DWL) @@ -134,7 +136,9 @@ init(void) #if defined(PLUGIN_ENABLED_BACKLIGHT) REGISTER_CORE_MODULE(backlight, backlight); #endif +#if defined(PLUGIN_ENABLED_BATTERY) REGISTER_CORE_MODULE(battery, battery); +#endif REGISTER_CORE_MODULE(clock, clock); REGISTER_CORE_MODULE(disk-io, disk_io); #if defined(PLUGIN_ENABLED_DWL) From 25e123fbe66ad6b2404bd3be7dd75a1cc6f47743 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 14 Dec 2022 09:28:42 +0100 Subject: [PATCH 422/611] =?UTF-8?q?meson:=20make=20=E2=80=98clock=E2=80=99?= =?UTF-8?q?=20plugin=20compile=20time=20optional?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- meson.build | 2 ++ meson_options.txt | 2 ++ modules/meson.build | 7 ++++++- plugin.c | 4 ++++ 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 9d2128a..7b4bf40 100644 --- a/meson.build +++ b/meson.build @@ -135,6 +135,7 @@ yambar = executable( plugin_alsa_enabled ? '-DPLUGIN_ENABLED_ALSA' : [], plugin_backlight_enabled ? '-DPLUGIN_ENABLED_BACKLIGHT' : [], plugin_battery_enabled ? '-DPLUGIN_ENABLED_BATTERY' : [], + plugin_clock_enabled ? '-DPLUGIN_ENABLED_CLOCK' : [], plugin_dwl_enabled ? '-DPLUGIN_ENABLED_DWL' : [], plugin_mpd_enabled ? '-DPLUGIN_ENABLED_MPD' : [], plugin_pipewire_enabled ? '-DPLUGIN_ENABLED_PIPEWIRE' : [], @@ -180,6 +181,7 @@ summary( 'ALSA': plugin_alsa_enabled, 'Backlight': plugin_backlight_enabled, 'Battery': plugin_battery_enabled, + 'Clock': plugin_clock_enabled, 'DWL (dwm for Wayland)': plugin_dwl_enabled, 'Music Player Daemon (MPD)': plugin_mpd_enabled, 'Pipewire': plugin_pipewire_enabled, diff --git a/meson_options.txt b/meson_options.txt index ab8837f..cce0d08 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -12,6 +12,8 @@ option('plugin-backlight', type: 'feature', value: 'auto', description: 'Backlight support') option('plugin-battery', type: 'feature', value: 'auto', description: 'Battery support') +option('plugin-clock', type: 'feature', value: 'auto', + description: 'Clock support') option('plugin-dwl', type: 'feature', value: 'auto', description: 'DWL (dwm for wayland) support') option('plugin-mpd', type: 'feature', value: 'auto', diff --git a/modules/meson.build b/modules/meson.build index 4252364..4a06e43 100644 --- a/modules/meson.build +++ b/modules/meson.build @@ -16,6 +16,8 @@ plugin_backlight_enabled = udev_backlight.found() udev_battery = dependency('libudev', required: get_option('plugin-battery')) plugin_battery_enabled = udev_battery.found() +plugin_clock_enabled = get_option('plugin-clock').allowed() + mpd = dependency('libmpdclient', required: get_option('plugin-mpd')) plugin_mpd_enabled = mpd.found() @@ -29,7 +31,6 @@ plugin_dwl_enabled = get_option('plugin-dwl').allowed() # Module name -> (source-list, dep-list) mod_data = { - 'clock': [[], []], 'cpu': [[], []], 'disk-io': [[], [dynlist]], 'mem': [[], []], @@ -53,6 +54,10 @@ if plugin_battery_enabled mod_data += {'battery': [[], [udev_battery]]} endif +if plugin_clock_enabled + mod_data += {'clock': [[], []]} +endif + if plugin_dwl_enabled mod_data += {'dwl': [[], [dynlist]]} endif diff --git a/plugin.c b/plugin.c index 6f7e5cc..10acb3f 100644 --- a/plugin.c +++ b/plugin.c @@ -41,7 +41,9 @@ EXTERN_MODULE(backlight); #if defined(PLUGIN_ENABLED_BATTERY) EXTERN_MODULE(battery); #endif +#if defined(PLUGIN_ENABLED_CLOCK) EXTERN_MODULE(clock); +#endif EXTERN_MODULE(disk_io); #if defined(PLUGIN_ENABLED_DWL) EXTERN_MODULE(dwl); @@ -139,7 +141,9 @@ init(void) #if defined(PLUGIN_ENABLED_BATTERY) REGISTER_CORE_MODULE(battery, battery); #endif +#if defined(PLUGIN_ENABLED_CLOCK) REGISTER_CORE_MODULE(clock, clock); +#endif REGISTER_CORE_MODULE(disk-io, disk_io); #if defined(PLUGIN_ENABLED_DWL) REGISTER_CORE_MODULE(dwl, dwl); From b23365ccacd25911a35a31d8b6b869512520f1b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 14 Dec 2022 09:31:48 +0100 Subject: [PATCH 423/611] =?UTF-8?q?meson:=20make=20=E2=80=98cpu=E2=80=99?= =?UTF-8?q?=20plugin=20compile=20time=20optional?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- meson.build | 2 ++ meson_options.txt | 2 ++ modules/meson.build | 6 +++++- plugin.c | 8 ++++++-- 4 files changed, 15 insertions(+), 3 deletions(-) diff --git a/meson.build b/meson.build index 7b4bf40..223d3c7 100644 --- a/meson.build +++ b/meson.build @@ -136,6 +136,7 @@ yambar = executable( plugin_backlight_enabled ? '-DPLUGIN_ENABLED_BACKLIGHT' : [], plugin_battery_enabled ? '-DPLUGIN_ENABLED_BATTERY' : [], plugin_clock_enabled ? '-DPLUGIN_ENABLED_CLOCK' : [], + plugin_cpu_enabled ? '-DPLUGIN_ENABLED_CPU' : [], plugin_dwl_enabled ? '-DPLUGIN_ENABLED_DWL' : [], plugin_mpd_enabled ? '-DPLUGIN_ENABLED_MPD' : [], plugin_pipewire_enabled ? '-DPLUGIN_ENABLED_PIPEWIRE' : [], @@ -182,6 +183,7 @@ summary( 'Backlight': plugin_backlight_enabled, 'Battery': plugin_battery_enabled, 'Clock': plugin_clock_enabled, + 'CPU': plugin_cpu_enabled, 'DWL (dwm for Wayland)': plugin_dwl_enabled, 'Music Player Daemon (MPD)': plugin_mpd_enabled, 'Pipewire': plugin_pipewire_enabled, diff --git a/meson_options.txt b/meson_options.txt index cce0d08..ca92b74 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -14,6 +14,8 @@ option('plugin-battery', type: 'feature', value: 'auto', description: 'Battery support') option('plugin-clock', type: 'feature', value: 'auto', description: 'Clock support') +option('plugin-cpu', type: 'feature', value: 'auto', + description: 'CPU support') option('plugin-dwl', type: 'feature', value: 'auto', description: 'DWL (dwm for wayland) support') option('plugin-mpd', type: 'feature', value: 'auto', diff --git a/modules/meson.build b/modules/meson.build index 4a06e43..a061e01 100644 --- a/modules/meson.build +++ b/modules/meson.build @@ -17,6 +17,7 @@ udev_battery = dependency('libudev', required: get_option('plugin-battery')) plugin_battery_enabled = udev_battery.found() plugin_clock_enabled = get_option('plugin-clock').allowed() +plugin_cpu_enabled = get_option('plugin-cpu').allowed() mpd = dependency('libmpdclient', required: get_option('plugin-mpd')) plugin_mpd_enabled = mpd.found() @@ -31,7 +32,6 @@ plugin_dwl_enabled = get_option('plugin-dwl').allowed() # Module name -> (source-list, dep-list) mod_data = { - 'cpu': [[], []], 'disk-io': [[], [dynlist]], 'mem': [[], []], 'i3': [['i3-common.c', 'i3-common.h'], [dynlist, json]], @@ -58,6 +58,10 @@ if plugin_clock_enabled mod_data += {'clock': [[], []]} endif +if plugin_cpu_enabled + mod_data += {'cpu': [[], []]} +endif + if plugin_dwl_enabled mod_data += {'dwl': [[], [dynlist]]} endif diff --git a/plugin.c b/plugin.c index 10acb3f..c54a97f 100644 --- a/plugin.c +++ b/plugin.c @@ -44,6 +44,9 @@ EXTERN_MODULE(battery); #if defined(PLUGIN_ENABLED_CLOCK) EXTERN_MODULE(clock); #endif +#if defined(PLUGIN_ENABLED_CPU) +EXTERN_MODULE(cpu); +#endif EXTERN_MODULE(disk_io); #if defined(PLUGIN_ENABLED_DWL) EXTERN_MODULE(dwl); @@ -67,7 +70,6 @@ EXTERN_MODULE(sway_xkb); EXTERN_MODULE(script); EXTERN_MODULE(xkb); EXTERN_MODULE(xwindow); -EXTERN_MODULE(cpu); EXTERN_MODULE(mem); EXTERN_PARTICLE(empty); @@ -143,6 +145,9 @@ init(void) #endif #if defined(PLUGIN_ENABLED_CLOCK) REGISTER_CORE_MODULE(clock, clock); +#endif +#if defined(PLUGIN_ENABLED_CPU) + REGISTER_CORE_MODULE(cpu, cpu); #endif REGISTER_CORE_MODULE(disk-io, disk_io); #if defined(PLUGIN_ENABLED_DWL) @@ -176,7 +181,6 @@ init(void) REGISTER_CORE_MODULE(xwindow, xwindow); #endif REGISTER_CORE_MODULE(mem, mem); - REGISTER_CORE_MODULE(cpu, cpu); REGISTER_CORE_PARTICLE(empty, empty); REGISTER_CORE_PARTICLE(list, list); From 659b2824458931dd311eaf7aa2258def1c397a96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 14 Dec 2022 09:34:05 +0100 Subject: [PATCH 424/611] =?UTF-8?q?meson:=20make=20=E2=80=98disk-io?= =?UTF-8?q?=E2=80=99=20plugin=20compile=20time=20optional?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- meson.build | 2 ++ meson_options.txt | 2 ++ modules/meson.build | 6 +++++- plugin.c | 4 ++++ 4 files changed, 13 insertions(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 223d3c7..ebcec99 100644 --- a/meson.build +++ b/meson.build @@ -137,6 +137,7 @@ yambar = executable( plugin_battery_enabled ? '-DPLUGIN_ENABLED_BATTERY' : [], plugin_clock_enabled ? '-DPLUGIN_ENABLED_CLOCK' : [], plugin_cpu_enabled ? '-DPLUGIN_ENABLED_CPU' : [], + plugin_disk_io_enabled ? '-DPLUGIN_ENABLED_DISK_IO' : [], plugin_dwl_enabled ? '-DPLUGIN_ENABLED_DWL' : [], plugin_mpd_enabled ? '-DPLUGIN_ENABLED_MPD' : [], plugin_pipewire_enabled ? '-DPLUGIN_ENABLED_PIPEWIRE' : [], @@ -184,6 +185,7 @@ summary( 'Battery': plugin_battery_enabled, 'Clock': plugin_clock_enabled, 'CPU': plugin_cpu_enabled, + 'Disk I/O': plugin_disk_io_enabled, 'DWL (dwm for Wayland)': plugin_dwl_enabled, 'Music Player Daemon (MPD)': plugin_mpd_enabled, 'Pipewire': plugin_pipewire_enabled, diff --git a/meson_options.txt b/meson_options.txt index ca92b74..ad1f130 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -16,6 +16,8 @@ option('plugin-clock', type: 'feature', value: 'auto', description: 'Clock support') option('plugin-cpu', type: 'feature', value: 'auto', description: 'CPU support') +option('plugin-disk-io', type: 'feature', value: 'auto', + description: 'Disk I/O support') option('plugin-dwl', type: 'feature', value: 'auto', description: 'DWL (dwm for wayland) support') option('plugin-mpd', type: 'feature', value: 'auto', diff --git a/modules/meson.build b/modules/meson.build index a061e01..aedc343 100644 --- a/modules/meson.build +++ b/modules/meson.build @@ -18,6 +18,7 @@ plugin_battery_enabled = udev_battery.found() plugin_clock_enabled = get_option('plugin-clock').allowed() plugin_cpu_enabled = get_option('plugin-cpu').allowed() +plugin_disk_io_enabled = get_option('plugin-disk-io').allowed() mpd = dependency('libmpdclient', required: get_option('plugin-mpd')) plugin_mpd_enabled = mpd.found() @@ -32,7 +33,6 @@ plugin_dwl_enabled = get_option('plugin-dwl').allowed() # Module name -> (source-list, dep-list) mod_data = { - 'disk-io': [[], [dynlist]], 'mem': [[], []], 'i3': [['i3-common.c', 'i3-common.h'], [dynlist, json]], 'label': [[], []], @@ -62,6 +62,10 @@ if plugin_cpu_enabled mod_data += {'cpu': [[], []]} endif +if plugin_disk_io_enabled + mod_data += {'disk-io': [[], [dynlist]]} +endif + if plugin_dwl_enabled mod_data += {'dwl': [[], [dynlist]]} endif diff --git a/plugin.c b/plugin.c index c54a97f..0828e7e 100644 --- a/plugin.c +++ b/plugin.c @@ -47,7 +47,9 @@ EXTERN_MODULE(clock); #if defined(PLUGIN_ENABLED_CPU) EXTERN_MODULE(cpu); #endif +#if defined(PLUGIN_ENABLED_DISK_IO) EXTERN_MODULE(disk_io); +#endif #if defined(PLUGIN_ENABLED_DWL) EXTERN_MODULE(dwl); #endif @@ -149,7 +151,9 @@ init(void) #if defined(PLUGIN_ENABLED_CPU) REGISTER_CORE_MODULE(cpu, cpu); #endif +#if defined(PLUGIN_ENABLED_DISK_IO) REGISTER_CORE_MODULE(disk-io, disk_io); +#endif #if defined(PLUGIN_ENABLED_DWL) REGISTER_CORE_MODULE(dwl, dwl); #endif From 85d55905f98f8586c1051c1c70b5e080d3d8e6c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 14 Dec 2022 09:36:34 +0100 Subject: [PATCH 425/611] =?UTF-8?q?meson:=20make=20=E2=80=98mem=E2=80=99?= =?UTF-8?q?=20plugin=20compile=20time=20optional?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- meson.build | 6 ++++-- meson_options.txt | 4 +++- modules/meson.build | 6 +++++- plugin.c | 8 ++++++-- 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/meson.build b/meson.build index ebcec99..6bf4e21 100644 --- a/meson.build +++ b/meson.build @@ -139,6 +139,7 @@ yambar = executable( plugin_cpu_enabled ? '-DPLUGIN_ENABLED_CPU' : [], plugin_disk_io_enabled ? '-DPLUGIN_ENABLED_DISK_IO' : [], plugin_dwl_enabled ? '-DPLUGIN_ENABLED_DWL' : [], + plugin_mem_enabled ? '-DPLUGIN_ENABLED_MEM' : [], plugin_mpd_enabled ? '-DPLUGIN_ENABLED_MPD' : [], plugin_pipewire_enabled ? '-DPLUGIN_ENABLED_PIPEWIRE' : [], plugin_pulse_enabled ? '-DPLUGIN_ENABLED_PULSE' : [], @@ -184,9 +185,10 @@ summary( 'Backlight': plugin_backlight_enabled, 'Battery': plugin_battery_enabled, 'Clock': plugin_clock_enabled, - 'CPU': plugin_cpu_enabled, - 'Disk I/O': plugin_disk_io_enabled, + 'CPU monitoring': plugin_cpu_enabled, + 'Disk I/O monitoring': plugin_disk_io_enabled, 'DWL (dwm for Wayland)': plugin_dwl_enabled, + 'Memory monitoring': plugin_mem_enabled, 'Music Player Daemon (MPD)': plugin_mpd_enabled, 'Pipewire': plugin_pipewire_enabled, 'PulseAudio': plugin_pulse_enabled, diff --git a/meson_options.txt b/meson_options.txt index ad1f130..4c287ab 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -15,11 +15,13 @@ option('plugin-battery', type: 'feature', value: 'auto', option('plugin-clock', type: 'feature', value: 'auto', description: 'Clock support') option('plugin-cpu', type: 'feature', value: 'auto', - description: 'CPU support') + description: 'CPU monitoring support') option('plugin-disk-io', type: 'feature', value: 'auto', description: 'Disk I/O support') option('plugin-dwl', type: 'feature', value: 'auto', description: 'DWL (dwm for wayland) support') +option('plugin-mem', type: 'feature', value: 'auto', + description: 'Memory monitoring support') option('plugin-mpd', type: 'feature', value: 'auto', description: 'Music Player Daemon (MPD) support') option('plugin-pipewire', type: 'feature', value: 'auto', diff --git a/modules/meson.build b/modules/meson.build index aedc343..c9d4aa3 100644 --- a/modules/meson.build +++ b/modules/meson.build @@ -19,6 +19,7 @@ plugin_battery_enabled = udev_battery.found() plugin_clock_enabled = get_option('plugin-clock').allowed() plugin_cpu_enabled = get_option('plugin-cpu').allowed() plugin_disk_io_enabled = get_option('plugin-disk-io').allowed() +plugin_mem_enabled = get_option('plugin-mem').allowed() mpd = dependency('libmpdclient', required: get_option('plugin-mpd')) plugin_mpd_enabled = mpd.found() @@ -33,7 +34,6 @@ plugin_dwl_enabled = get_option('plugin-dwl').allowed() # Module name -> (source-list, dep-list) mod_data = { - 'mem': [[], []], 'i3': [['i3-common.c', 'i3-common.h'], [dynlist, json]], 'label': [[], []], 'network': [[], []], @@ -70,6 +70,10 @@ if plugin_dwl_enabled mod_data += {'dwl': [[], [dynlist]]} endif +if plugin_mem_enabled + mod_data += {'mem': [[], []]} +endif + if plugin_mpd_enabled mod_data += {'mpd': [[], [mpd]]} endif diff --git a/plugin.c b/plugin.c index 0828e7e..f61a334 100644 --- a/plugin.c +++ b/plugin.c @@ -56,6 +56,9 @@ EXTERN_MODULE(dwl); EXTERN_MODULE(foreign_toplevel); EXTERN_MODULE(i3); EXTERN_MODULE(label); +#if defined(PLUGIN_ENABLED_MEM) +EXTERN_MODULE(mem); +#endif #if defined(PLUGIN_ENABLED_MPD) EXTERN_MODULE(mpd); #endif @@ -72,7 +75,6 @@ EXTERN_MODULE(sway_xkb); EXTERN_MODULE(script); EXTERN_MODULE(xkb); EXTERN_MODULE(xwindow); -EXTERN_MODULE(mem); EXTERN_PARTICLE(empty); EXTERN_PARTICLE(list); @@ -162,6 +164,9 @@ init(void) #endif REGISTER_CORE_MODULE(i3, i3); REGISTER_CORE_MODULE(label, label); +#if defined(PLUGIN_ENABLED_MEM) + REGISTER_CORE_MODULE(mem, mem); +#endif #if defined(PLUGIN_ENABLED_MPD) REGISTER_CORE_MODULE(mpd, mpd); #endif @@ -184,7 +189,6 @@ init(void) #if defined(HAVE_PLUGIN_xwindow) REGISTER_CORE_MODULE(xwindow, xwindow); #endif - REGISTER_CORE_MODULE(mem, mem); REGISTER_CORE_PARTICLE(empty, empty); REGISTER_CORE_PARTICLE(list, list); From f54f583be176f01f33cc5f2189da8f37a5338d9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 14 Dec 2022 09:39:47 +0100 Subject: [PATCH 426/611] =?UTF-8?q?meson:=20make=20=E2=80=98i3=E2=80=99=20?= =?UTF-8?q?plugin=20compile=20time=20optional?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- meson.build | 2 ++ meson_options.txt | 2 ++ modules/meson.build | 9 ++++++++- plugin.c | 8 ++++++-- 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/meson.build b/meson.build index 6bf4e21..fb71480 100644 --- a/meson.build +++ b/meson.build @@ -141,6 +141,7 @@ yambar = executable( plugin_dwl_enabled ? '-DPLUGIN_ENABLED_DWL' : [], plugin_mem_enabled ? '-DPLUGIN_ENABLED_MEM' : [], plugin_mpd_enabled ? '-DPLUGIN_ENABLED_MPD' : [], + plugin_i3_enabled ? '-DPLUGIN_ENABLED_I3' : [], plugin_pipewire_enabled ? '-DPLUGIN_ENABLED_PIPEWIRE' : [], plugin_pulse_enabled ? '-DPLUGIN_ENABLED_PULSE' : [], ], @@ -190,6 +191,7 @@ summary( 'DWL (dwm for Wayland)': plugin_dwl_enabled, 'Memory monitoring': plugin_mem_enabled, 'Music Player Daemon (MPD)': plugin_mpd_enabled, + 'i3+Sway': plugin_i3_enabled, 'Pipewire': plugin_pipewire_enabled, 'PulseAudio': plugin_pulse_enabled, }, diff --git a/meson_options.txt b/meson_options.txt index 4c287ab..abe233a 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -24,6 +24,8 @@ option('plugin-mem', type: 'feature', value: 'auto', description: 'Memory monitoring support') option('plugin-mpd', type: 'feature', value: 'auto', description: 'Music Player Daemon (MPD) support') +option('plugin-i3', type: 'feature', value: 'auto', + description: 'i3+Sway support') option('plugin-pipewire', type: 'feature', value: 'auto', description: 'Pipewire support') option('plugin-pulse', type: 'feature', value: 'auto', diff --git a/modules/meson.build b/modules/meson.build index c9d4aa3..91babbb 100644 --- a/modules/meson.build +++ b/modules/meson.build @@ -24,6 +24,9 @@ plugin_mem_enabled = get_option('plugin-mem').allowed() mpd = dependency('libmpdclient', required: get_option('plugin-mpd')) plugin_mpd_enabled = mpd.found() +json_i3 = dependency('json-c', required: get_option('plugin-i3')) +plugin_i3_enabled = json_i3.found() + pipewire = dependency('libpipewire-0.3', required: get_option('plugin-pipewire')) plugin_pipewire_enabled = pipewire.found() @@ -34,7 +37,6 @@ plugin_dwl_enabled = get_option('plugin-dwl').allowed() # Module name -> (source-list, dep-list) mod_data = { - 'i3': [['i3-common.c', 'i3-common.h'], [dynlist, json]], 'label': [[], []], 'network': [[], []], 'removables': [[], [dynlist, udev]], @@ -77,6 +79,11 @@ endif if plugin_mpd_enabled mod_data += {'mpd': [[], [mpd]]} endif + +if plugin_i3_enabled + mod_data += {'i3': [['i3-common.c', 'i3-common.h'], [dynlist, json_i3]]} +endif + if plugin_pipewire_enabled mod_data += {'pipewire': [[], [pipewire, dynlist, json]]} endif diff --git a/plugin.c b/plugin.c index f61a334..eda9cf2 100644 --- a/plugin.c +++ b/plugin.c @@ -54,7 +54,6 @@ EXTERN_MODULE(disk_io); EXTERN_MODULE(dwl); #endif EXTERN_MODULE(foreign_toplevel); -EXTERN_MODULE(i3); EXTERN_MODULE(label); #if defined(PLUGIN_ENABLED_MEM) EXTERN_MODULE(mem); @@ -62,6 +61,9 @@ EXTERN_MODULE(mem); #if defined(PLUGIN_ENABLED_MPD) EXTERN_MODULE(mpd); #endif +#if defined(PLUGIN_ENABLED_I3) +EXTERN_MODULE(i3); +#endif EXTERN_MODULE(network); #if defined(PLUGIN_ENABLED_PULSE) EXTERN_MODULE(pulse); @@ -162,13 +164,15 @@ init(void) #if defined(HAVE_PLUGIN_foreign_toplevel) REGISTER_CORE_MODULE(foreign-toplevel, foreign_toplevel); #endif - REGISTER_CORE_MODULE(i3, i3); REGISTER_CORE_MODULE(label, label); #if defined(PLUGIN_ENABLED_MEM) REGISTER_CORE_MODULE(mem, mem); #endif #if defined(PLUGIN_ENABLED_MPD) REGISTER_CORE_MODULE(mpd, mpd); +#endif +#if defined(PLUGIN_ENABLED_I3) + REGISTER_CORE_MODULE(i3, i3); #endif REGISTER_CORE_MODULE(network, network); #if defined(PLUGIN_ENABLED_PULSE) From 8d5e8b5f205469c1475059dd02bf80f5493060c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 14 Dec 2022 09:41:44 +0100 Subject: [PATCH 427/611] =?UTF-8?q?meson:=20make=20=E2=80=98label=E2=80=99?= =?UTF-8?q?=20plugin=20compile=20time=20optional?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- meson.build | 2 ++ meson_options.txt | 2 ++ modules/meson.build | 7 ++++++- plugin.c | 8 ++++++-- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/meson.build b/meson.build index fb71480..4a5c4d5 100644 --- a/meson.build +++ b/meson.build @@ -142,6 +142,7 @@ yambar = executable( plugin_mem_enabled ? '-DPLUGIN_ENABLED_MEM' : [], plugin_mpd_enabled ? '-DPLUGIN_ENABLED_MPD' : [], plugin_i3_enabled ? '-DPLUGIN_ENABLED_I3' : [], + plugin_label_enabled ? '-DPLUGIN_ENABLED_LABEL' : [], plugin_pipewire_enabled ? '-DPLUGIN_ENABLED_PIPEWIRE' : [], plugin_pulse_enabled ? '-DPLUGIN_ENABLED_PULSE' : [], ], @@ -192,6 +193,7 @@ summary( 'Memory monitoring': plugin_mem_enabled, 'Music Player Daemon (MPD)': plugin_mpd_enabled, 'i3+Sway': plugin_i3_enabled, + 'Label': plugin_label_enabled, 'Pipewire': plugin_pipewire_enabled, 'PulseAudio': plugin_pulse_enabled, }, diff --git a/meson_options.txt b/meson_options.txt index abe233a..c206f92 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -26,6 +26,8 @@ option('plugin-mpd', type: 'feature', value: 'auto', description: 'Music Player Daemon (MPD) support') option('plugin-i3', type: 'feature', value: 'auto', description: 'i3+Sway support') +option('plugin-label', type: 'feature', value: 'auto', + description: 'Label support') option('plugin-pipewire', type: 'feature', value: 'auto', description: 'Pipewire support') option('plugin-pulse', type: 'feature', value: 'auto', diff --git a/modules/meson.build b/modules/meson.build index 91babbb..6059970 100644 --- a/modules/meson.build +++ b/modules/meson.build @@ -27,6 +27,8 @@ plugin_mpd_enabled = mpd.found() json_i3 = dependency('json-c', required: get_option('plugin-i3')) plugin_i3_enabled = json_i3.found() +plugin_label_enabled = get_option('plugin-label').allowed() + pipewire = dependency('libpipewire-0.3', required: get_option('plugin-pipewire')) plugin_pipewire_enabled = pipewire.found() @@ -37,7 +39,6 @@ plugin_dwl_enabled = get_option('plugin-dwl').allowed() # Module name -> (source-list, dep-list) mod_data = { - 'label': [[], []], 'network': [[], []], 'removables': [[], [dynlist, udev]], 'script': [[], []], @@ -84,6 +85,10 @@ if plugin_i3_enabled mod_data += {'i3': [['i3-common.c', 'i3-common.h'], [dynlist, json_i3]]} endif +if plugin_label_enabled + mod_data += {'label': [[], []]} +endif + if plugin_pipewire_enabled mod_data += {'pipewire': [[], [pipewire, dynlist, json]]} endif diff --git a/plugin.c b/plugin.c index eda9cf2..9205df7 100644 --- a/plugin.c +++ b/plugin.c @@ -54,7 +54,6 @@ EXTERN_MODULE(disk_io); EXTERN_MODULE(dwl); #endif EXTERN_MODULE(foreign_toplevel); -EXTERN_MODULE(label); #if defined(PLUGIN_ENABLED_MEM) EXTERN_MODULE(mem); #endif @@ -64,6 +63,9 @@ EXTERN_MODULE(mpd); #if defined(PLUGIN_ENABLED_I3) EXTERN_MODULE(i3); #endif +#if defined(PLUGIN_ENABLED_LABEL) +EXTERN_MODULE(label); +#endif EXTERN_MODULE(network); #if defined(PLUGIN_ENABLED_PULSE) EXTERN_MODULE(pulse); @@ -164,7 +166,6 @@ init(void) #if defined(HAVE_PLUGIN_foreign_toplevel) REGISTER_CORE_MODULE(foreign-toplevel, foreign_toplevel); #endif - REGISTER_CORE_MODULE(label, label); #if defined(PLUGIN_ENABLED_MEM) REGISTER_CORE_MODULE(mem, mem); #endif @@ -173,6 +174,9 @@ init(void) #endif #if defined(PLUGIN_ENABLED_I3) REGISTER_CORE_MODULE(i3, i3); +#endif +#if defined(PLUGIN_ENABLED_LABEL) + REGISTER_CORE_MODULE(label, label); #endif REGISTER_CORE_MODULE(network, network); #if defined(PLUGIN_ENABLED_PULSE) From b901ac50eedaac933304b92889f6ff92592efc84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 14 Dec 2022 09:43:14 +0100 Subject: [PATCH 428/611] =?UTF-8?q?meson:=20make=20=E2=80=98network?= =?UTF-8?q?=E2=80=99=20plugin=20compile=20time=20optional?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- meson.build | 2 ++ meson_options.txt | 2 ++ modules/meson.build | 6 +++++- plugin.c | 4 ++++ 4 files changed, 13 insertions(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 4a5c4d5..0089fa7 100644 --- a/meson.build +++ b/meson.build @@ -143,6 +143,7 @@ yambar = executable( plugin_mpd_enabled ? '-DPLUGIN_ENABLED_MPD' : [], plugin_i3_enabled ? '-DPLUGIN_ENABLED_I3' : [], plugin_label_enabled ? '-DPLUGIN_ENABLED_LABEL' : [], + plugin_network_enabled ? '-DPLUGIN_ENABLED_NETWORK' : [], plugin_pipewire_enabled ? '-DPLUGIN_ENABLED_PIPEWIRE' : [], plugin_pulse_enabled ? '-DPLUGIN_ENABLED_PULSE' : [], ], @@ -194,6 +195,7 @@ summary( 'Music Player Daemon (MPD)': plugin_mpd_enabled, 'i3+Sway': plugin_i3_enabled, 'Label': plugin_label_enabled, + 'Network monitoring': plugin_network_enabled, 'Pipewire': plugin_pipewire_enabled, 'PulseAudio': plugin_pulse_enabled, }, diff --git a/meson_options.txt b/meson_options.txt index c206f92..d64f85b 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -28,6 +28,8 @@ option('plugin-i3', type: 'feature', value: 'auto', description: 'i3+Sway support') option('plugin-label', type: 'feature', value: 'auto', description: 'Label support') +option('plugin-network', type: 'feature', value: 'auto', + description: 'Network monitoring support') option('plugin-pipewire', type: 'feature', value: 'auto', description: 'Pipewire support') option('plugin-pulse', type: 'feature', value: 'auto', diff --git a/modules/meson.build b/modules/meson.build index 6059970..ba5647e 100644 --- a/modules/meson.build +++ b/modules/meson.build @@ -28,6 +28,7 @@ json_i3 = dependency('json-c', required: get_option('plugin-i3')) plugin_i3_enabled = json_i3.found() plugin_label_enabled = get_option('plugin-label').allowed() +plugin_network_enabled = get_option('plugin-network').allowed() pipewire = dependency('libpipewire-0.3', required: get_option('plugin-pipewire')) plugin_pipewire_enabled = pipewire.found() @@ -39,7 +40,6 @@ plugin_dwl_enabled = get_option('plugin-dwl').allowed() # Module name -> (source-list, dep-list) mod_data = { - 'network': [[], []], 'removables': [[], [dynlist, udev]], 'script': [[], []], 'sway-xkb': [['i3-common.c', 'i3-common.h'], [dynlist, json]], @@ -89,6 +89,10 @@ if plugin_label_enabled mod_data += {'label': [[], []]} endif +if plugin_network_enabled + mod_data += {'network': [[], []]} +endif + if plugin_pipewire_enabled mod_data += {'pipewire': [[], [pipewire, dynlist, json]]} endif diff --git a/plugin.c b/plugin.c index 9205df7..b08d5c5 100644 --- a/plugin.c +++ b/plugin.c @@ -66,7 +66,9 @@ EXTERN_MODULE(i3); #if defined(PLUGIN_ENABLED_LABEL) EXTERN_MODULE(label); #endif +#if defined(PLUGIN_ENABLED_NETWORK) EXTERN_MODULE(network); +#endif #if defined(PLUGIN_ENABLED_PULSE) EXTERN_MODULE(pulse); #endif @@ -178,7 +180,9 @@ init(void) #if defined(PLUGIN_ENABLED_LABEL) REGISTER_CORE_MODULE(label, label); #endif +#if defined(PLUGIN_ENABLED_NETWORK) REGISTER_CORE_MODULE(network, network); +#endif #if defined(PLUGIN_ENABLED_PULSE) REGISTER_CORE_MODULE(pulse, pulse); #endif From eb26f64ea73098913fd6bdc9218a06c5a0b53474 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 14 Dec 2022 09:46:08 +0100 Subject: [PATCH 429/611] =?UTF-8?q?meson:=20make=20=E2=80=98removables?= =?UTF-8?q?=E2=80=99=20plugin=20compile=20time=20optional?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- meson.build | 2 ++ meson_options.txt | 2 ++ modules/meson.build | 9 +++++++-- plugin.c | 8 ++++++-- 4 files changed, 17 insertions(+), 4 deletions(-) diff --git a/meson.build b/meson.build index 0089fa7..09255a1 100644 --- a/meson.build +++ b/meson.build @@ -144,6 +144,7 @@ yambar = executable( plugin_i3_enabled ? '-DPLUGIN_ENABLED_I3' : [], plugin_label_enabled ? '-DPLUGIN_ENABLED_LABEL' : [], plugin_network_enabled ? '-DPLUGIN_ENABLED_NETWORK' : [], + plugin_removables_enabled ? '-DPLUGIN_ENABLED_REMOVABLES' : [], plugin_pipewire_enabled ? '-DPLUGIN_ENABLED_PIPEWIRE' : [], plugin_pulse_enabled ? '-DPLUGIN_ENABLED_PULSE' : [], ], @@ -196,6 +197,7 @@ summary( 'i3+Sway': plugin_i3_enabled, 'Label': plugin_label_enabled, 'Network monitoring': plugin_network_enabled, + 'Removables monitoring': plugin_removables_enabled, 'Pipewire': plugin_pipewire_enabled, 'PulseAudio': plugin_pulse_enabled, }, diff --git a/meson_options.txt b/meson_options.txt index d64f85b..e130a0a 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -30,6 +30,8 @@ option('plugin-label', type: 'feature', value: 'auto', description: 'Label support') option('plugin-network', type: 'feature', value: 'auto', description: 'Network monitoring support') +option('plugin-removables', type: 'feature', value: 'auto', + description: 'Removables (USB sticks, CD-ROM etc) monitoring support') option('plugin-pipewire', type: 'feature', value: 'auto', description: 'Pipewire support') option('plugin-pulse', type: 'feature', value: 'auto', diff --git a/modules/meson.build b/modules/meson.build index ba5647e..7eba966 100644 --- a/modules/meson.build +++ b/modules/meson.build @@ -2,7 +2,6 @@ module_sdk = declare_dependency(dependencies: [pixman, threads, tllist, fcft]) modules = [] -udev = dependency('udev') json = dependency('json-c') xcb_xkb = dependency('xcb-xkb', required: get_option('backend-x11')) @@ -30,6 +29,9 @@ plugin_i3_enabled = json_i3.found() plugin_label_enabled = get_option('plugin-label').allowed() plugin_network_enabled = get_option('plugin-network').allowed() +udev_removables = dependency('udev', required: get_option('plugin-removables')) +plugin_removables_enabled = udev_removables.found() + pipewire = dependency('libpipewire-0.3', required: get_option('plugin-pipewire')) plugin_pipewire_enabled = pipewire.found() @@ -40,7 +42,6 @@ plugin_dwl_enabled = get_option('plugin-dwl').allowed() # Module name -> (source-list, dep-list) mod_data = { - 'removables': [[], [dynlist, udev]], 'script': [[], []], 'sway-xkb': [['i3-common.c', 'i3-common.h'], [dynlist, json]], } @@ -93,6 +94,10 @@ if plugin_network_enabled mod_data += {'network': [[], []]} endif +if plugin_removables_enabled + mod_data += {'removables': [[], [dynlist, udev_removables]]} +endif + if plugin_pipewire_enabled mod_data += {'pipewire': [[], [pipewire, dynlist, json]]} endif diff --git a/plugin.c b/plugin.c index b08d5c5..95fcff1 100644 --- a/plugin.c +++ b/plugin.c @@ -69,13 +69,15 @@ EXTERN_MODULE(label); #if defined(PLUGIN_ENABLED_NETWORK) EXTERN_MODULE(network); #endif +#if defined(PLUGIN_ENABLED_REMOVABLES) +EXTERN_MODULE(removables); +#endif #if defined(PLUGIN_ENABLED_PULSE) EXTERN_MODULE(pulse); #endif #if defined(PLUGIN_ENABLED_PIPEWIRE) EXTERN_MODULE(pipewire); #endif -EXTERN_MODULE(removables); EXTERN_MODULE(river); EXTERN_MODULE(sway_xkb); EXTERN_MODULE(script); @@ -183,13 +185,15 @@ init(void) #if defined(PLUGIN_ENABLED_NETWORK) REGISTER_CORE_MODULE(network, network); #endif +#if defined(PLUGIN_ENABLED_REMOVABLES) + REGISTER_CORE_MODULE(removables, removables); +#endif #if defined(PLUGIN_ENABLED_PULSE) REGISTER_CORE_MODULE(pulse, pulse); #endif #if defined(PLUGIN_ENABLED_PIPEWIRE) REGISTER_CORE_MODULE(pipewire, pipewire); #endif - REGISTER_CORE_MODULE(removables, removables); #if defined(HAVE_PLUGIN_river) REGISTER_CORE_MODULE(river, river); #endif From ec9ed66b6b30520fbbc560902401b54947c6a716 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 14 Dec 2022 09:48:50 +0100 Subject: [PATCH 430/611] =?UTF-8?q?meson:=20make=20=E2=80=98script?= =?UTF-8?q?=E2=80=99=20plugin=20compile=20time=20optional?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- meson.build | 6 ++++-- meson_options.txt | 2 ++ modules/meson.build | 15 ++++++++++----- plugin.c | 24 ++++++++++++++---------- 4 files changed, 30 insertions(+), 17 deletions(-) diff --git a/meson.build b/meson.build index 09255a1..fd4eb67 100644 --- a/meson.build +++ b/meson.build @@ -144,9 +144,10 @@ yambar = executable( plugin_i3_enabled ? '-DPLUGIN_ENABLED_I3' : [], plugin_label_enabled ? '-DPLUGIN_ENABLED_LABEL' : [], plugin_network_enabled ? '-DPLUGIN_ENABLED_NETWORK' : [], - plugin_removables_enabled ? '-DPLUGIN_ENABLED_REMOVABLES' : [], plugin_pipewire_enabled ? '-DPLUGIN_ENABLED_PIPEWIRE' : [], plugin_pulse_enabled ? '-DPLUGIN_ENABLED_PULSE' : [], + plugin_removables_enabled ? '-DPLUGIN_ENABLED_REMOVABLES' : [], + plugin_script_enabled ? '-DPLUGIN_ENABLED_SCRIPT' : [], ], build_rpath: '$ORIGIN/modules:$ORIGIN/decorations:$ORIGIN/particles', export_dynamic: true, @@ -197,9 +198,10 @@ summary( 'i3+Sway': plugin_i3_enabled, 'Label': plugin_label_enabled, 'Network monitoring': plugin_network_enabled, - 'Removables monitoring': plugin_removables_enabled, 'Pipewire': plugin_pipewire_enabled, 'PulseAudio': plugin_pulse_enabled, + 'Removables monitoring': plugin_removables_enabled, + 'Script': plugin_script_enabled, }, section: 'Optional modules', bool_yn: true diff --git a/meson_options.txt b/meson_options.txt index e130a0a..918f207 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -32,6 +32,8 @@ option('plugin-network', type: 'feature', value: 'auto', description: 'Network monitoring support') option('plugin-removables', type: 'feature', value: 'auto', description: 'Removables (USB sticks, CD-ROM etc) monitoring support') +option('plugin-script', type: 'feature', value: 'auto', + description: 'Script support') option('plugin-pipewire', type: 'feature', value: 'auto', description: 'Pipewire support') option('plugin-pulse', type: 'feature', value: 'auto', diff --git a/modules/meson.build b/modules/meson.build index 7eba966..279b050 100644 --- a/modules/meson.build +++ b/modules/meson.build @@ -32,6 +32,8 @@ plugin_network_enabled = get_option('plugin-network').allowed() udev_removables = dependency('udev', required: get_option('plugin-removables')) plugin_removables_enabled = udev_removables.found() +plugin_script_enabled = get_option('plugin-script').allowed() + pipewire = dependency('libpipewire-0.3', required: get_option('plugin-pipewire')) plugin_pipewire_enabled = pipewire.found() @@ -42,7 +44,6 @@ plugin_dwl_enabled = get_option('plugin-dwl').allowed() # Module name -> (source-list, dep-list) mod_data = { - 'script': [[], []], 'sway-xkb': [['i3-common.c', 'i3-common.h'], [dynlist, json]], } @@ -94,10 +95,6 @@ if plugin_network_enabled mod_data += {'network': [[], []]} endif -if plugin_removables_enabled - mod_data += {'removables': [[], [dynlist, udev_removables]]} -endif - if plugin_pipewire_enabled mod_data += {'pipewire': [[], [pipewire, dynlist, json]]} endif @@ -106,6 +103,14 @@ if plugin_pulse_enabled mod_data += {'pulse': [[], [pulse]]} endif +if plugin_removables_enabled + mod_data += {'removables': [[], [dynlist, udev_removables]]} +endif + +if plugin_script_enabled + mod_data += {'script': [[], []]} +endif + if backend_x11 mod_data += { 'xkb': [[], [xcb_stuff, xcb_xkb]], diff --git a/plugin.c b/plugin.c index 95fcff1..c6efc40 100644 --- a/plugin.c +++ b/plugin.c @@ -69,18 +69,20 @@ EXTERN_MODULE(label); #if defined(PLUGIN_ENABLED_NETWORK) EXTERN_MODULE(network); #endif -#if defined(PLUGIN_ENABLED_REMOVABLES) -EXTERN_MODULE(removables); +#if defined(PLUGIN_ENABLED_PIPEWIRE) +EXTERN_MODULE(pipewire); #endif #if defined(PLUGIN_ENABLED_PULSE) EXTERN_MODULE(pulse); #endif -#if defined(PLUGIN_ENABLED_PIPEWIRE) -EXTERN_MODULE(pipewire); +#if defined(PLUGIN_ENABLED_REMOVABLES) +EXTERN_MODULE(removables); +#endif +#if defined(PLUGIN_ENABLED_SCRIPT) +EXTERN_MODULE(script); #endif EXTERN_MODULE(river); EXTERN_MODULE(sway_xkb); -EXTERN_MODULE(script); EXTERN_MODULE(xkb); EXTERN_MODULE(xwindow); @@ -185,20 +187,22 @@ init(void) #if defined(PLUGIN_ENABLED_NETWORK) REGISTER_CORE_MODULE(network, network); #endif -#if defined(PLUGIN_ENABLED_REMOVABLES) - REGISTER_CORE_MODULE(removables, removables); +#if defined(PLUGIN_ENABLED_PIPEWIRE) + REGISTER_CORE_MODULE(pipewire, pipewire); #endif #if defined(PLUGIN_ENABLED_PULSE) REGISTER_CORE_MODULE(pulse, pulse); #endif -#if defined(PLUGIN_ENABLED_PIPEWIRE) - REGISTER_CORE_MODULE(pipewire, pipewire); +#if defined(PLUGIN_ENABLED_REMOVABLES) + REGISTER_CORE_MODULE(removables, removables); +#endif +#if defined(PLUGIN_ENABLED_SCRIPT) + REGISTER_CORE_MODULE(script, script); #endif #if defined(HAVE_PLUGIN_river) REGISTER_CORE_MODULE(river, river); #endif REGISTER_CORE_MODULE(sway-xkb, sway_xkb); - REGISTER_CORE_MODULE(script, script); #if defined(HAVE_PLUGIN_xkb) REGISTER_CORE_MODULE(xkb, xkb); #endif From 0cf0d64970b2595b08ea09ed33f406bf30b89eae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 14 Dec 2022 09:50:22 +0100 Subject: [PATCH 431/611] =?UTF-8?q?meson:=20pipewire-specific=20=E2=80=98j?= =?UTF-8?q?son=E2=80=99=20dependency?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/meson.build | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/modules/meson.build b/modules/meson.build index 279b050..5f34d00 100644 --- a/modules/meson.build +++ b/modules/meson.build @@ -18,6 +18,7 @@ plugin_battery_enabled = udev_battery.found() plugin_clock_enabled = get_option('plugin-clock').allowed() plugin_cpu_enabled = get_option('plugin-cpu').allowed() plugin_disk_io_enabled = get_option('plugin-disk-io').allowed() +plugin_dwl_enabled = get_option('plugin-dwl').allowed() plugin_mem_enabled = get_option('plugin-mem').allowed() mpd = dependency('libmpdclient', required: get_option('plugin-mpd')) @@ -29,18 +30,17 @@ plugin_i3_enabled = json_i3.found() plugin_label_enabled = get_option('plugin-label').allowed() plugin_network_enabled = get_option('plugin-network').allowed() -udev_removables = dependency('udev', required: get_option('plugin-removables')) -plugin_removables_enabled = udev_removables.found() - -plugin_script_enabled = get_option('plugin-script').allowed() - pipewire = dependency('libpipewire-0.3', required: get_option('plugin-pipewire')) -plugin_pipewire_enabled = pipewire.found() +json_pipewire = dependency('json-c', required: get_option('plugin-pipewire')) +plugin_pipewire_enabled = pipewire.found() and json_pipewire.found() pulse = dependency('libpulse', required: get_option('plugin-pulse')) plugin_pulse_enabled = pulse.found() -plugin_dwl_enabled = get_option('plugin-dwl').allowed() +udev_removables = dependency('udev', required: get_option('plugin-removables')) +plugin_removables_enabled = udev_removables.found() + +plugin_script_enabled = get_option('plugin-script').allowed() # Module name -> (source-list, dep-list) mod_data = { @@ -96,7 +96,7 @@ if plugin_network_enabled endif if plugin_pipewire_enabled - mod_data += {'pipewire': [[], [pipewire, dynlist, json]]} + mod_data += {'pipewire': [[], [pipewire, dynlist, json_pipewire]]} endif if plugin_pulse_enabled From b6450446a8814c12160bf69a4d173d04b387189d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 14 Dec 2022 09:53:24 +0100 Subject: [PATCH 432/611] =?UTF-8?q?meson:=20make=20=E2=80=98sway-xkb?= =?UTF-8?q?=E2=80=99=20plugin=20compile=20time=20optional?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- meson.build | 2 ++ meson_options.txt | 10 ++++++---- modules/meson.build | 12 ++++++++---- plugin.c | 8 ++++++-- 4 files changed, 22 insertions(+), 10 deletions(-) diff --git a/meson.build b/meson.build index fd4eb67..3223683 100644 --- a/meson.build +++ b/meson.build @@ -148,6 +148,7 @@ yambar = executable( plugin_pulse_enabled ? '-DPLUGIN_ENABLED_PULSE' : [], plugin_removables_enabled ? '-DPLUGIN_ENABLED_REMOVABLES' : [], plugin_script_enabled ? '-DPLUGIN_ENABLED_SCRIPT' : [], + plugin_sway_xkb_enabled ? '-DPLUGIN_ENABLED_SWAY_XKB' : [], ], build_rpath: '$ORIGIN/modules:$ORIGIN/decorations:$ORIGIN/particles', export_dynamic: true, @@ -202,6 +203,7 @@ summary( 'PulseAudio': plugin_pulse_enabled, 'Removables monitoring': plugin_removables_enabled, 'Script': plugin_script_enabled, + 'Sway XKB keyboard': plugin_sway_xkb_enabled, }, section: 'Optional modules', bool_yn: true diff --git a/meson_options.txt b/meson_options.txt index 918f207..6c85875 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -30,11 +30,13 @@ option('plugin-label', type: 'feature', value: 'auto', description: 'Label support') option('plugin-network', type: 'feature', value: 'auto', description: 'Network monitoring support') -option('plugin-removables', type: 'feature', value: 'auto', - description: 'Removables (USB sticks, CD-ROM etc) monitoring support') -option('plugin-script', type: 'feature', value: 'auto', - description: 'Script support') option('plugin-pipewire', type: 'feature', value: 'auto', description: 'Pipewire support') option('plugin-pulse', type: 'feature', value: 'auto', description: 'PulseAudio support') +option('plugin-removables', type: 'feature', value: 'auto', + description: 'Removables (USB sticks, CD-ROM etc) monitoring support') +option('plugin-script', type: 'feature', value: 'auto', + description: 'Script support') +option('plugin-sway-xkb', type: 'feature', value: 'auto', + description: 'keyboard support for Sway') diff --git a/modules/meson.build b/modules/meson.build index 5f34d00..9964b00 100644 --- a/modules/meson.build +++ b/modules/meson.build @@ -2,7 +2,6 @@ module_sdk = declare_dependency(dependencies: [pixman, threads, tllist, fcft]) modules = [] -json = dependency('json-c') xcb_xkb = dependency('xcb-xkb', required: get_option('backend-x11')) # Optional deps @@ -42,10 +41,11 @@ plugin_removables_enabled = udev_removables.found() plugin_script_enabled = get_option('plugin-script').allowed() +json_sway_xkb = dependency('json-c', required: get_option('plugin-sway-xkb')) +plugin_sway_xkb_enabled = json_sway_xkb.found() + # Module name -> (source-list, dep-list) -mod_data = { - 'sway-xkb': [['i3-common.c', 'i3-common.h'], [dynlist, json]], -} +mod_data = {} if plugin_alsa_enabled mod_data += {'alsa': [[], [m, alsa]]} @@ -111,6 +111,10 @@ if plugin_script_enabled mod_data += {'script': [[], []]} endif +if plugin_sway_xkb_enabled + mod_data += {'sway-xkb': [['i3-common.c', 'i3-common.h'], [dynlist, json_sway_xkb]]} +endif + if backend_x11 mod_data += { 'xkb': [[], [xcb_stuff, xcb_xkb]], diff --git a/plugin.c b/plugin.c index c6efc40..79936cd 100644 --- a/plugin.c +++ b/plugin.c @@ -81,8 +81,10 @@ EXTERN_MODULE(removables); #if defined(PLUGIN_ENABLED_SCRIPT) EXTERN_MODULE(script); #endif -EXTERN_MODULE(river); +#if defined(PLUGIN_ENABLED_SWAY_XKB) EXTERN_MODULE(sway_xkb); +#endif +EXTERN_MODULE(river); EXTERN_MODULE(xkb); EXTERN_MODULE(xwindow); @@ -199,10 +201,12 @@ init(void) #if defined(PLUGIN_ENABLED_SCRIPT) REGISTER_CORE_MODULE(script, script); #endif +#if defined(PLUGIN_ENABLED_SWAY_XKB) + REGISTER_CORE_MODULE(sway-xkb, sway_xkb); +#endif #if defined(HAVE_PLUGIN_river) REGISTER_CORE_MODULE(river, river); #endif - REGISTER_CORE_MODULE(sway-xkb, sway_xkb); #if defined(HAVE_PLUGIN_xkb) REGISTER_CORE_MODULE(xkb, xkb); #endif From a14d38b0cb0834a9147ed3ff64bfaf87a199adce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 14 Dec 2022 09:58:45 +0100 Subject: [PATCH 433/611] =?UTF-8?q?meson:=20make=20=E2=80=98xkb=E2=80=99?= =?UTF-8?q?=20plugin=20compile=20time=20optional?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- meson.build | 2 ++ meson_options.txt | 2 ++ modules/meson.build | 10 +++++++--- plugin.c | 10 ++++++---- 4 files changed, 17 insertions(+), 7 deletions(-) diff --git a/meson.build b/meson.build index 3223683..874abf6 100644 --- a/meson.build +++ b/meson.build @@ -149,6 +149,7 @@ yambar = executable( plugin_removables_enabled ? '-DPLUGIN_ENABLED_REMOVABLES' : [], plugin_script_enabled ? '-DPLUGIN_ENABLED_SCRIPT' : [], plugin_sway_xkb_enabled ? '-DPLUGIN_ENABLED_SWAY_XKB' : [], + plugin_xkb_enabled ? '-DPLUGIN_ENABLED_XKB' : [], ], build_rpath: '$ORIGIN/modules:$ORIGIN/decorations:$ORIGIN/particles', export_dynamic: true, @@ -204,6 +205,7 @@ summary( 'Removables monitoring': plugin_removables_enabled, 'Script': plugin_script_enabled, 'Sway XKB keyboard': plugin_sway_xkb_enabled, + 'XKB keyboard (for X11)': plugin_xkb_enabled, }, section: 'Optional modules', bool_yn: true diff --git a/meson_options.txt b/meson_options.txt index 6c85875..faa64a4 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -40,3 +40,5 @@ option('plugin-script', type: 'feature', value: 'auto', description: 'Script support') option('plugin-sway-xkb', type: 'feature', value: 'auto', description: 'keyboard support for Sway') +option('plugin-xkb', type: 'feature', value: 'auto', + description: 'keyboard support for X11') diff --git a/modules/meson.build b/modules/meson.build index 9964b00..a89fed0 100644 --- a/modules/meson.build +++ b/modules/meson.build @@ -2,8 +2,6 @@ module_sdk = declare_dependency(dependencies: [pixman, threads, tllist, fcft]) modules = [] -xcb_xkb = dependency('xcb-xkb', required: get_option('backend-x11')) - # Optional deps alsa = dependency('alsa', required: get_option('plugin-alsa')) plugin_alsa_enabled = alsa.found() @@ -44,6 +42,9 @@ plugin_script_enabled = get_option('plugin-script').allowed() json_sway_xkb = dependency('json-c', required: get_option('plugin-sway-xkb')) plugin_sway_xkb_enabled = json_sway_xkb.found() +xcb_xkb = dependency('xcb-xkb', required: get_option('plugin-xkb')) +plugin_xkb_enabled = backend_x11 and xcb_xkb.found() + # Module name -> (source-list, dep-list) mod_data = {} @@ -115,9 +116,12 @@ if plugin_sway_xkb_enabled mod_data += {'sway-xkb': [['i3-common.c', 'i3-common.h'], [dynlist, json_sway_xkb]]} endif +if plugin_xkb_enabled + mod_data += {'xkb': [[], [xcb_stuff, xcb_xkb]]} +endif + if backend_x11 mod_data += { - 'xkb': [[], [xcb_stuff, xcb_xkb]], 'xwindow': [[], [xcb_stuff]], } endif diff --git a/plugin.c b/plugin.c index 79936cd..5b4024a 100644 --- a/plugin.c +++ b/plugin.c @@ -84,8 +84,10 @@ EXTERN_MODULE(script); #if defined(PLUGIN_ENABLED_SWAY_XKB) EXTERN_MODULE(sway_xkb); #endif -EXTERN_MODULE(river); +#if defined(PLUGIN_ENABLED_XKB) EXTERN_MODULE(xkb); +#endif +EXTERN_MODULE(river); EXTERN_MODULE(xwindow); EXTERN_PARTICLE(empty); @@ -204,12 +206,12 @@ init(void) #if defined(PLUGIN_ENABLED_SWAY_XKB) REGISTER_CORE_MODULE(sway-xkb, sway_xkb); #endif +#if defined(PLUGIN_ENABLED_XKB) + REGISTER_CORE_MODULE(xkb, xkb); +#endif #if defined(HAVE_PLUGIN_river) REGISTER_CORE_MODULE(river, river); #endif -#if defined(HAVE_PLUGIN_xkb) - REGISTER_CORE_MODULE(xkb, xkb); -#endif #if defined(HAVE_PLUGIN_xwindow) REGISTER_CORE_MODULE(xwindow, xwindow); #endif From 1a8125557943127d8e4cc6003d2589dd38e17efe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 14 Dec 2022 10:02:13 +0100 Subject: [PATCH 434/611] =?UTF-8?q?meson:=20make=20=E2=80=98xwindow?= =?UTF-8?q?=E2=80=99=20plugin=20compile=20time=20optional?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- meson.build | 2 ++ meson_options.txt | 2 ++ modules/meson.build | 8 ++++---- plugin.c | 10 ++++++---- 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/meson.build b/meson.build index 874abf6..e2c332b 100644 --- a/meson.build +++ b/meson.build @@ -150,6 +150,7 @@ yambar = executable( plugin_script_enabled ? '-DPLUGIN_ENABLED_SCRIPT' : [], plugin_sway_xkb_enabled ? '-DPLUGIN_ENABLED_SWAY_XKB' : [], plugin_xkb_enabled ? '-DPLUGIN_ENABLED_XKB' : [], + plugin_xwindow_enabled ? '-DPLUGIN_ENABLED_XWINDOW' : [], ], build_rpath: '$ORIGIN/modules:$ORIGIN/decorations:$ORIGIN/particles', export_dynamic: true, @@ -206,6 +207,7 @@ summary( 'Script': plugin_script_enabled, 'Sway XKB keyboard': plugin_sway_xkb_enabled, 'XKB keyboard (for X11)': plugin_xkb_enabled, + 'XWindow': plugin_xwindow_enabled, }, section: 'Optional modules', bool_yn: true diff --git a/meson_options.txt b/meson_options.txt index faa64a4..46c0abb 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -42,3 +42,5 @@ option('plugin-sway-xkb', type: 'feature', value: 'auto', description: 'keyboard support for Sway') option('plugin-xkb', type: 'feature', value: 'auto', description: 'keyboard support for X11') +option('plugin-xwindow', type: 'feature', value: 'auto', + description: 'XWindow support') diff --git a/modules/meson.build b/modules/meson.build index a89fed0..f1c8f41 100644 --- a/modules/meson.build +++ b/modules/meson.build @@ -45,6 +45,8 @@ plugin_sway_xkb_enabled = json_sway_xkb.found() xcb_xkb = dependency('xcb-xkb', required: get_option('plugin-xkb')) plugin_xkb_enabled = backend_x11 and xcb_xkb.found() +plugin_xwindow_enabled = backend_x11 and get_option('plugin-xwindow').allowed() + # Module name -> (source-list, dep-list) mod_data = {} @@ -120,10 +122,8 @@ if plugin_xkb_enabled mod_data += {'xkb': [[], [xcb_stuff, xcb_xkb]]} endif -if backend_x11 - mod_data += { - 'xwindow': [[], [xcb_stuff]], - } +if plugin_xwindow_enabled + mod_data += {'xwindow': [[], [xcb_stuff]]} endif if backend_wayland diff --git a/plugin.c b/plugin.c index 5b4024a..b7fc913 100644 --- a/plugin.c +++ b/plugin.c @@ -87,8 +87,10 @@ EXTERN_MODULE(sway_xkb); #if defined(PLUGIN_ENABLED_XKB) EXTERN_MODULE(xkb); #endif -EXTERN_MODULE(river); +#if defined(PLUGIN_ENABLED_XWINDOW) EXTERN_MODULE(xwindow); +#endif +EXTERN_MODULE(river); EXTERN_PARTICLE(empty); EXTERN_PARTICLE(list); @@ -209,12 +211,12 @@ init(void) #if defined(PLUGIN_ENABLED_XKB) REGISTER_CORE_MODULE(xkb, xkb); #endif +#if defined(PLUGIN_ENABLED_XWINDOW) + REGISTER_CORE_MODULE(xwindow, xwindow); +#endif #if defined(HAVE_PLUGIN_river) REGISTER_CORE_MODULE(river, river); #endif -#if defined(HAVE_PLUGIN_xwindow) - REGISTER_CORE_MODULE(xwindow, xwindow); -#endif REGISTER_CORE_PARTICLE(empty, empty); REGISTER_CORE_PARTICLE(list, list); From 56b0047004410bb592a3d9f53a9a7b840dfa2744 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 14 Dec 2022 10:05:23 +0100 Subject: [PATCH 435/611] =?UTF-8?q?meson:=20make=20=E2=80=98river=E2=80=99?= =?UTF-8?q?=20plugin=20compile=20time=20optional?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- meson.build | 2 ++ meson_options.txt | 2 ++ modules/meson.build | 10 ++++++---- plugin.c | 10 ++++++---- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/meson.build b/meson.build index e2c332b..e40f488 100644 --- a/meson.build +++ b/meson.build @@ -147,6 +147,7 @@ yambar = executable( plugin_pipewire_enabled ? '-DPLUGIN_ENABLED_PIPEWIRE' : [], plugin_pulse_enabled ? '-DPLUGIN_ENABLED_PULSE' : [], plugin_removables_enabled ? '-DPLUGIN_ENABLED_REMOVABLES' : [], + plugin_river_enabled ? '-DPLUGIN_ENABLED_RIVER' : [], plugin_script_enabled ? '-DPLUGIN_ENABLED_SCRIPT' : [], plugin_sway_xkb_enabled ? '-DPLUGIN_ENABLED_SWAY_XKB' : [], plugin_xkb_enabled ? '-DPLUGIN_ENABLED_XKB' : [], @@ -204,6 +205,7 @@ summary( 'Pipewire': plugin_pipewire_enabled, 'PulseAudio': plugin_pulse_enabled, 'Removables monitoring': plugin_removables_enabled, + 'River': plugin_river_enabled, 'Script': plugin_script_enabled, 'Sway XKB keyboard': plugin_sway_xkb_enabled, 'XKB keyboard (for X11)': plugin_xkb_enabled, diff --git a/meson_options.txt b/meson_options.txt index 46c0abb..e5ab795 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -36,6 +36,8 @@ option('plugin-pulse', type: 'feature', value: 'auto', description: 'PulseAudio support') option('plugin-removables', type: 'feature', value: 'auto', description: 'Removables (USB sticks, CD-ROM etc) monitoring support') +option('plugin-river', type: 'feature', value: 'auto', + description: 'River support') option('plugin-script', type: 'feature', value: 'auto', description: 'Script support') option('plugin-sway-xkb', type: 'feature', value: 'auto', diff --git a/modules/meson.build b/modules/meson.build index f1c8f41..933a195 100644 --- a/modules/meson.build +++ b/modules/meson.build @@ -37,6 +37,8 @@ plugin_pulse_enabled = pulse.found() udev_removables = dependency('udev', required: get_option('plugin-removables')) plugin_removables_enabled = udev_removables.found() +plugin_river_enabled = backend_wayland and get_option('plugin-river').allowed() + plugin_script_enabled = get_option('plugin-script').allowed() json_sway_xkb = dependency('json-c', required: get_option('plugin-sway-xkb')) @@ -126,7 +128,7 @@ if plugin_xwindow_enabled mod_data += {'xwindow': [[], [xcb_stuff]]} endif -if backend_wayland +if plugin_river_enabled river_proto_headers = [] river_proto_src = [] @@ -145,10 +147,10 @@ if backend_wayland command: [wscanner_prog, 'private-code', '@INPUT@', '@OUTPUT@']) endforeach - mod_data += { - 'river': [[wl_proto_src + wl_proto_headers + river_proto_src + river_proto_headers], [dynlist, wayland_client]], - } + mod_data += {'river': [[wl_proto_src + wl_proto_headers + river_proto_src + river_proto_headers], [dynlist, wayland_client]]} +endif +if backend_wayland ftop_proto_headers = [] ftop_proto_src = [] diff --git a/plugin.c b/plugin.c index b7fc913..7e47ee7 100644 --- a/plugin.c +++ b/plugin.c @@ -78,6 +78,9 @@ EXTERN_MODULE(pulse); #if defined(PLUGIN_ENABLED_REMOVABLES) EXTERN_MODULE(removables); #endif +#if defined(PLUGIN_ENABLED_RIVER) +EXTERN_MODULE(river); +#endif #if defined(PLUGIN_ENABLED_SCRIPT) EXTERN_MODULE(script); #endif @@ -90,7 +93,6 @@ EXTERN_MODULE(xkb); #if defined(PLUGIN_ENABLED_XWINDOW) EXTERN_MODULE(xwindow); #endif -EXTERN_MODULE(river); EXTERN_PARTICLE(empty); EXTERN_PARTICLE(list); @@ -202,6 +204,9 @@ init(void) #if defined(PLUGIN_ENABLED_REMOVABLES) REGISTER_CORE_MODULE(removables, removables); #endif +#if defined(PLUGIN_ENABLED_RIVER) + REGISTER_CORE_MODULE(river, river); +#endif #if defined(PLUGIN_ENABLED_SCRIPT) REGISTER_CORE_MODULE(script, script); #endif @@ -214,9 +219,6 @@ init(void) #if defined(PLUGIN_ENABLED_XWINDOW) REGISTER_CORE_MODULE(xwindow, xwindow); #endif -#if defined(HAVE_PLUGIN_river) - REGISTER_CORE_MODULE(river, river); -#endif REGISTER_CORE_PARTICLE(empty, empty); REGISTER_CORE_PARTICLE(list, list); From 9ef6d73663300c222521679ea24f42ced6540fb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 14 Dec 2022 10:08:48 +0100 Subject: [PATCH 436/611] =?UTF-8?q?meson:=20make=20=E2=80=98foreign-toplev?= =?UTF-8?q?el=E2=80=99=20plugin=20compile=20time=20optional?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- meson.build | 4 +++- meson_options.txt | 4 +++- modules/meson.build | 7 +++---- plugin.c | 4 +++- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/meson.build b/meson.build index e40f488..4e14277 100644 --- a/meson.build +++ b/meson.build @@ -139,6 +139,7 @@ yambar = executable( plugin_cpu_enabled ? '-DPLUGIN_ENABLED_CPU' : [], plugin_disk_io_enabled ? '-DPLUGIN_ENABLED_DISK_IO' : [], plugin_dwl_enabled ? '-DPLUGIN_ENABLED_DWL' : [], + plugin_foreign_toplevel_enabled ? '-DPLUGIN_ENABLED_FOREIGN_TOPLEVEL' : [], plugin_mem_enabled ? '-DPLUGIN_ENABLED_MEM' : [], plugin_mpd_enabled ? '-DPLUGIN_ENABLED_MPD' : [], plugin_i3_enabled ? '-DPLUGIN_ENABLED_I3' : [], @@ -197,6 +198,7 @@ summary( 'CPU monitoring': plugin_cpu_enabled, 'Disk I/O monitoring': plugin_disk_io_enabled, 'DWL (dwm for Wayland)': plugin_dwl_enabled, + 'Foreign toplevel (window tracking for Wayland)': plugin_foreign_toplevel_enabled, 'Memory monitoring': plugin_mem_enabled, 'Music Player Daemon (MPD)': plugin_mpd_enabled, 'i3+Sway': plugin_i3_enabled, @@ -209,7 +211,7 @@ summary( 'Script': plugin_script_enabled, 'Sway XKB keyboard': plugin_sway_xkb_enabled, 'XKB keyboard (for X11)': plugin_xkb_enabled, - 'XWindow': plugin_xwindow_enabled, + 'XWindow (window tracking for X11)': plugin_xwindow_enabled, }, section: 'Optional modules', bool_yn: true diff --git a/meson_options.txt b/meson_options.txt index e5ab795..03c0ead 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -20,6 +20,8 @@ option('plugin-disk-io', type: 'feature', value: 'auto', description: 'Disk I/O support') option('plugin-dwl', type: 'feature', value: 'auto', description: 'DWL (dwm for wayland) support') +option('plugin-foreign-toplevel', type: 'feature', value: 'auto', + description: 'Foreign toplevel (window tracking for Wayland) support') option('plugin-mem', type: 'feature', value: 'auto', description: 'Memory monitoring support') option('plugin-mpd', type: 'feature', value: 'auto', @@ -45,4 +47,4 @@ option('plugin-sway-xkb', type: 'feature', value: 'auto', option('plugin-xkb', type: 'feature', value: 'auto', description: 'keyboard support for X11') option('plugin-xwindow', type: 'feature', value: 'auto', - description: 'XWindow support') + description: 'XWindow (window tracking for X11) support') diff --git a/modules/meson.build b/modules/meson.build index 933a195..f14b3df 100644 --- a/modules/meson.build +++ b/modules/meson.build @@ -16,6 +16,7 @@ plugin_clock_enabled = get_option('plugin-clock').allowed() plugin_cpu_enabled = get_option('plugin-cpu').allowed() plugin_disk_io_enabled = get_option('plugin-disk-io').allowed() plugin_dwl_enabled = get_option('plugin-dwl').allowed() +plugin_foreign_toplevel_enabled = backend_wayland and get_option('plugin-foreign-toplevel').allowed() plugin_mem_enabled = get_option('plugin-mem').allowed() mpd = dependency('libmpdclient', required: get_option('plugin-mpd')) @@ -150,7 +151,7 @@ if plugin_river_enabled mod_data += {'river': [[wl_proto_src + wl_proto_headers + river_proto_src + river_proto_headers], [dynlist, wayland_client]]} endif -if backend_wayland +if plugin_foreign_toplevel_enabled ftop_proto_headers = [] ftop_proto_src = [] @@ -169,9 +170,7 @@ if backend_wayland command: [wscanner_prog, 'private-code', '@INPUT@', '@OUTPUT@']) endforeach - mod_data += { - 'foreign-toplevel': [[wl_proto_src + wl_proto_headers + ftop_proto_headers + ftop_proto_src], [dynlist, wayland_client]], - } + mod_data += {'foreign-toplevel': [[wl_proto_src + wl_proto_headers + ftop_proto_headers + ftop_proto_src], [dynlist, wayland_client]]} endif foreach mod, data : mod_data diff --git a/plugin.c b/plugin.c index 7e47ee7..93fa771 100644 --- a/plugin.c +++ b/plugin.c @@ -53,7 +53,9 @@ EXTERN_MODULE(disk_io); #if defined(PLUGIN_ENABLED_DWL) EXTERN_MODULE(dwl); #endif +#if defined(PLUGIN_ENABLED_FOREIGN_TOPLEVEL) EXTERN_MODULE(foreign_toplevel); +#endif #if defined(PLUGIN_ENABLED_MEM) EXTERN_MODULE(mem); #endif @@ -177,7 +179,7 @@ init(void) #if defined(PLUGIN_ENABLED_DWL) REGISTER_CORE_MODULE(dwl, dwl); #endif -#if defined(HAVE_PLUGIN_foreign_toplevel) +#if defined(PLUGIN_ENABLED_FOREIGN_TOPLEVEL) REGISTER_CORE_MODULE(foreign-toplevel, foreign_toplevel); #endif #if defined(PLUGIN_ENABLED_MEM) From 690bd630a2821b92587649e7bd86cff28b4192bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 14 Dec 2022 10:16:54 +0100 Subject: [PATCH 437/611] plugin: use auto-generated defines for enabled plugins --- meson.build | 23 ---------- plugin.c | 126 ++++++++++++++++++++++++++-------------------------- 2 files changed, 63 insertions(+), 86 deletions(-) diff --git a/meson.build b/meson.build index 4e14277..6ad0463 100644 --- a/meson.build +++ b/meson.build @@ -131,29 +131,6 @@ yambar = executable( version, dependencies: [bar, libepoll, libinotify, pixman, yaml, threads, dl, tllist, fcft] + decorations + particles + modules, - c_args: [ - plugin_alsa_enabled ? '-DPLUGIN_ENABLED_ALSA' : [], - plugin_backlight_enabled ? '-DPLUGIN_ENABLED_BACKLIGHT' : [], - plugin_battery_enabled ? '-DPLUGIN_ENABLED_BATTERY' : [], - plugin_clock_enabled ? '-DPLUGIN_ENABLED_CLOCK' : [], - plugin_cpu_enabled ? '-DPLUGIN_ENABLED_CPU' : [], - plugin_disk_io_enabled ? '-DPLUGIN_ENABLED_DISK_IO' : [], - plugin_dwl_enabled ? '-DPLUGIN_ENABLED_DWL' : [], - plugin_foreign_toplevel_enabled ? '-DPLUGIN_ENABLED_FOREIGN_TOPLEVEL' : [], - plugin_mem_enabled ? '-DPLUGIN_ENABLED_MEM' : [], - plugin_mpd_enabled ? '-DPLUGIN_ENABLED_MPD' : [], - plugin_i3_enabled ? '-DPLUGIN_ENABLED_I3' : [], - plugin_label_enabled ? '-DPLUGIN_ENABLED_LABEL' : [], - plugin_network_enabled ? '-DPLUGIN_ENABLED_NETWORK' : [], - plugin_pipewire_enabled ? '-DPLUGIN_ENABLED_PIPEWIRE' : [], - plugin_pulse_enabled ? '-DPLUGIN_ENABLED_PULSE' : [], - plugin_removables_enabled ? '-DPLUGIN_ENABLED_REMOVABLES' : [], - plugin_river_enabled ? '-DPLUGIN_ENABLED_RIVER' : [], - plugin_script_enabled ? '-DPLUGIN_ENABLED_SCRIPT' : [], - plugin_sway_xkb_enabled ? '-DPLUGIN_ENABLED_SWAY_XKB' : [], - plugin_xkb_enabled ? '-DPLUGIN_ENABLED_XKB' : [], - plugin_xwindow_enabled ? '-DPLUGIN_ENABLED_XWINDOW' : [], - ], build_rpath: '$ORIGIN/modules:$ORIGIN/decorations:$ORIGIN/particles', export_dynamic: true, install: true, diff --git a/plugin.c b/plugin.c index 93fa771..ed7f63c 100644 --- a/plugin.c +++ b/plugin.c @@ -32,68 +32,68 @@ keychain_t *chain, const struct yml_node *node); \ extern struct deco *plug_name##_from_conf(const struct yml_node *node); -#if defined(PLUGIN_ENABLED_ALSA) -EXTERN_MODULE(alsa); +#if defined(HAVE_PLUGIN_alsa) + EXTERN_MODULE(alsa); #endif -#if defined(PLUGIN_ENABLED_BACKLIGHT) -EXTERN_MODULE(backlight); +#if defined(HAVE_PLUGIN_backlight) + EXTERN_MODULE(backlight); #endif -#if defined(PLUGIN_ENABLED_BATTERY) -EXTERN_MODULE(battery); +#if defined(HAVE_PLUGIN_battery) + EXTERN_MODULE(battery); #endif -#if defined(PLUGIN_ENABLED_CLOCK) -EXTERN_MODULE(clock); +#if defined(HAVE_PLUGIN_clock) + EXTERN_MODULE(clock); #endif -#if defined(PLUGIN_ENABLED_CPU) -EXTERN_MODULE(cpu); +#if defined(HAVE_PLUGIN_cpu) + EXTERN_MODULE(cpu); #endif -#if defined(PLUGIN_ENABLED_DISK_IO) -EXTERN_MODULE(disk_io); +#if defined(HAVE_PLUGIN_disk_io) + EXTERN_MODULE(disk_io); #endif -#if defined(PLUGIN_ENABLED_DWL) -EXTERN_MODULE(dwl); +#if defined(HAVE_PLUGIN_dwl) + EXTERN_MODULE(dwl); #endif -#if defined(PLUGIN_ENABLED_FOREIGN_TOPLEVEL) -EXTERN_MODULE(foreign_toplevel); +#if defined(HAVE_PLUGIN_foreign_toplevel) + EXTERN_MODULE(foreign_toplevel); #endif -#if defined(PLUGIN_ENABLED_MEM) -EXTERN_MODULE(mem); +#if defined(HAVE_PLUGIN_mem) + EXTERN_MODULE(mem); #endif -#if defined(PLUGIN_ENABLED_MPD) -EXTERN_MODULE(mpd); +#if defined(HAVE_PLUGIN_mpd) + EXTERN_MODULE(mpd); #endif -#if defined(PLUGIN_ENABLED_I3) -EXTERN_MODULE(i3); +#if defined(HAVE_PLUGIN_i3) + EXTERN_MODULE(i3); #endif -#if defined(PLUGIN_ENABLED_LABEL) -EXTERN_MODULE(label); +#if defined(HAVE_PLUGIN_label) + EXTERN_MODULE(label); #endif -#if defined(PLUGIN_ENABLED_NETWORK) -EXTERN_MODULE(network); +#if defined(HAVE_PLUGIN_network) + EXTERN_MODULE(network); #endif -#if defined(PLUGIN_ENABLED_PIPEWIRE) -EXTERN_MODULE(pipewire); +#if defined(HAVE_PLUGIN_pipewire) + EXTERN_MODULE(pipewire); #endif -#if defined(PLUGIN_ENABLED_PULSE) -EXTERN_MODULE(pulse); +#if defined(HAVE_PLUGIN_pulse) + EXTERN_MODULE(pulse); #endif -#if defined(PLUGIN_ENABLED_REMOVABLES) -EXTERN_MODULE(removables); +#if defined(HAVE_PLUGIN_removables) + EXTERN_MODULE(removables); #endif -#if defined(PLUGIN_ENABLED_RIVER) -EXTERN_MODULE(river); +#if defined(HAVE_PLUGIN_river) + EXTERN_MODULE(river); #endif -#if defined(PLUGIN_ENABLED_SCRIPT) -EXTERN_MODULE(script); +#if defined(HAVE_PLUGIN_script) + EXTERN_MODULE(script); #endif -#if defined(PLUGIN_ENABLED_SWAY_XKB) -EXTERN_MODULE(sway_xkb); +#if defined(HAVE_PLUGIN_sway_xkb) + EXTERN_MODULE(sway_xkb); #endif -#if defined(PLUGIN_ENABLED_XKB) -EXTERN_MODULE(xkb); +#if defined(HAVE_PLUGIN_xkb) + EXTERN_MODULE(xkb); #endif -#if defined(PLUGIN_ENABLED_XWINDOW) -EXTERN_MODULE(xwindow); +#if defined(HAVE_PLUGIN_xwindow) + EXTERN_MODULE(xwindow); #endif EXTERN_PARTICLE(empty); @@ -158,67 +158,67 @@ init(void) tll_back(plugins).decoration = &deco_##func_prefix##_iface; \ } while (0) -#if defined(PLUGIN_ENABLED_ALSA) +#if defined(HAVE_PLUGIN_alsa) REGISTER_CORE_MODULE(alsa, alsa); #endif -#if defined(PLUGIN_ENABLED_BACKLIGHT) +#if defined(HAVE_PLUGIN_backlight) REGISTER_CORE_MODULE(backlight, backlight); #endif -#if defined(PLUGIN_ENABLED_BATTERY) +#if defined(HAVE_PLUGIN_battery) REGISTER_CORE_MODULE(battery, battery); #endif -#if defined(PLUGIN_ENABLED_CLOCK) +#if defined(HAVE_PLUGIN_clock) REGISTER_CORE_MODULE(clock, clock); #endif -#if defined(PLUGIN_ENABLED_CPU) +#if defined(HAVE_PLUGIN_cpu) REGISTER_CORE_MODULE(cpu, cpu); #endif -#if defined(PLUGIN_ENABLED_DISK_IO) +#if defined(HAVE_PLUGIN_disk_io) REGISTER_CORE_MODULE(disk-io, disk_io); #endif -#if defined(PLUGIN_ENABLED_DWL) +#if defined(HAVE_PLUGIN_dwl) REGISTER_CORE_MODULE(dwl, dwl); #endif -#if defined(PLUGIN_ENABLED_FOREIGN_TOPLEVEL) +#if defined(HAVE_PLUGIN_foreign_toplevel) REGISTER_CORE_MODULE(foreign-toplevel, foreign_toplevel); #endif -#if defined(PLUGIN_ENABLED_MEM) +#if defined(HAVE_PLUGIN_mem) REGISTER_CORE_MODULE(mem, mem); #endif -#if defined(PLUGIN_ENABLED_MPD) +#if defined(HAVE_PLUGIN_mpd) REGISTER_CORE_MODULE(mpd, mpd); #endif -#if defined(PLUGIN_ENABLED_I3) +#if defined(HAVE_PLUGIN_i3) REGISTER_CORE_MODULE(i3, i3); #endif -#if defined(PLUGIN_ENABLED_LABEL) +#if defined(HAVE_PLUGIN_label) REGISTER_CORE_MODULE(label, label); #endif -#if defined(PLUGIN_ENABLED_NETWORK) +#if defined(HAVE_PLUGIN_network) REGISTER_CORE_MODULE(network, network); #endif -#if defined(PLUGIN_ENABLED_PIPEWIRE) +#if defined(HAVE_PLUGIN_pipewire) REGISTER_CORE_MODULE(pipewire, pipewire); #endif -#if defined(PLUGIN_ENABLED_PULSE) +#if defined(HAVE_PLUGIN_pulse) REGISTER_CORE_MODULE(pulse, pulse); #endif -#if defined(PLUGIN_ENABLED_REMOVABLES) +#if defined(HAVE_PLUGIN_removables) REGISTER_CORE_MODULE(removables, removables); #endif -#if defined(PLUGIN_ENABLED_RIVER) +#if defined(HAVE_PLUGIN_river) REGISTER_CORE_MODULE(river, river); #endif -#if defined(PLUGIN_ENABLED_SCRIPT) +#if defined(HAVE_PLUGIN_script) REGISTER_CORE_MODULE(script, script); #endif -#if defined(PLUGIN_ENABLED_SWAY_XKB) +#if defined(HAVE_PLUGIN_sway_xkb) REGISTER_CORE_MODULE(sway-xkb, sway_xkb); #endif -#if defined(PLUGIN_ENABLED_XKB) +#if defined(HAVE_PLUGIN_xkb) REGISTER_CORE_MODULE(xkb, xkb); #endif -#if defined(PLUGIN_ENABLED_XWINDOW) +#if defined(HAVE_PLUGIN_xwindow) REGISTER_CORE_MODULE(xwindow, xwindow); #endif From a5e79d14b79a092d23d178cbae2c3bd8b7f20622 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 14 Dec 2022 10:18:18 +0100 Subject: [PATCH 438/611] pkgbuild: add pipewire dependency --- PKGBUILD | 1 + PKGBUILD.wayland-only | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/PKGBUILD b/PKGBUILD index b675823..8f20543 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -16,6 +16,7 @@ depends=( 'json-c' 'libmpdclient' 'libpulse' + 'pipewire' 'fcft>=3.0.0' 'fcft<4.0.0') optdepends=('xcb-util-errors: better X error messages') source=() diff --git a/PKGBUILD.wayland-only b/PKGBUILD.wayland-only index e836093..cc956e9 100644 --- a/PKGBUILD.wayland-only +++ b/PKGBUILD.wayland-only @@ -1,5 +1,5 @@ pkgname=yambar-wayland -pkgver=1.8.0 +pkgver=1.8.0.r111.g690bd63 pkgrel=1 pkgdesc="Simplistic and highly configurable status panel for Wayland" arch=('x86_64' 'aarch64') @@ -17,6 +17,7 @@ depends=( 'json-c' 'libmpdclient' 'libpulse' + 'pipewire' 'fcft>=3.0.0' 'fcft<4.0.0') source=() From a53e48a2c16dcfd80b9b1c91063a3177f4f55918 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 14 Dec 2022 10:25:25 +0100 Subject: [PATCH 439/611] doc: meson: only install man pages for modules we actually build --- doc/meson.build | 83 +++++++++++++++++++++++++++++++++++++++---------- meson.build | 2 +- 2 files changed, 68 insertions(+), 17 deletions(-) diff --git a/doc/meson.build b/doc/meson.build index e561ef3..fa9673d 100644 --- a/doc/meson.build +++ b/doc/meson.build @@ -3,22 +3,73 @@ sh = find_program('sh', native: true) scdoc = dependency('scdoc', native: true) scdoc_prog = find_program(scdoc.get_variable('scdoc'), native: true) -foreach man_src : ['yambar.1.scd', 'yambar.5.scd', 'yambar-decorations.5.scd', - 'yambar-modules-alsa.5.scd', 'yambar-modules-backlight.5.scd', - 'yambar-modules-battery.5.scd', 'yambar-modules-clock.5.scd', - 'yambar-modules-disk-io.5.scd', 'yambar-modules-dwl.5.scd', - 'yambar-modules-foreign-toplevel.5.scd', - 'yambar-modules-i3.5.scd', 'yambar-modules-label.5.scd', - 'yambar-modules-mpd.5.scd', 'yambar-modules-network.5.scd', - 'yambar-modules-pulse.5.scd', - 'yambar-modules-pipewire.5.scd', - 'yambar-modules-removables.5.scd', 'yambar-modules-river.5.scd', - 'yambar-modules-script.5.scd', 'yambar-modules-sway-xkb.5.scd', - 'yambar-modules-sway.5.scd', 'yambar-modules-xkb.5.scd', - 'yambar-modules-xwindow.5.scd', 'yambar-modules.5.scd', - 'yambar-modules-cpu.5.scd', - 'yambar-modules-mem.5.scd', - 'yambar-particles.5.scd', 'yambar-tags.5.scd'] +plugin_pages = [] +if plugin_alsa_enabled + plugin_pages += ['yambar-modules-alsa.5.scd'] +endif +if plugin_backlight_enabled + plugin_pages += ['yambar-modules-backlight.5.scd'] +endif +if plugin_battery_enabled + plugin_pages += ['yambar-modules-battery.5.scd'] +endif +if plugin_clock_enabled + plugin_pages += ['yambar-modules-clock.5.scd'] +endif +if plugin_cpu_enabled + plugin_pages += ['yambar-modules-cpu.5.scd'] +endif +if plugin_disk_io_enabled + plugin_pages += ['yambar-modules-disk-io.5.scd'] +endif +if plugin_dwl_enabled + plugin_pages += ['yambar-modules-dwl.5.scd'] +endif +if plugin_foreign_toplevel_enabled + plugin_pages += ['yambar-modules-foreign-toplevel.5.scd'] +endif +if plugin_mem_enabled + plugin_pages += ['yambar-modules-mem.5.scd'] +endif +if plugin_mpd_enabled + plugin_pages += ['yambar-modules-mpd.5.scd'] +endif +if plugin_i3_enabled + plugin_pages += ['yambar-modules-i3.5.scd'] +endif +if plugin_label_enabled + plugin_pages += ['yambar-modules-label.5.scd'] +endif +if plugin_network_enabled + plugin_pages += ['yambar-modules-network.5.scd'] +endif +if plugin_pipewire_enabled + plugin_pages += ['yambar-modules-pipewire.5.scd'] +endif +if plugin_pulse_enabled + plugin_pages += ['yambar-modules-pulse.5.scd'] +endif +if plugin_removables_enabled + plugin_pages += ['yambar-modules-removables.5.scd'] +endif +if plugin_river_enabled + plugin_pages += ['yambar-modules-river.5.scd'] +endif +if plugin_script_enabled + plugin_pages += ['yambar-modules-script.5.scd'] +endif +if plugin_sway_xkb_enabled + plugin_pages += ['yambar-modules-sway-xkb.5.scd'] +endif +if plugin_xkb_enabled + plugin_pages += ['yambar-modules-xkb.5.scd'] +endif + +foreach man_src : ['yambar.1.scd', + 'yambar.5.scd', + 'yambar-decorations.5.scd', + 'yambar-particles.5.scd', + 'yambar-tags.5.scd'] + plugin_pages parts = man_src.split('.') name = parts[-3] section = parts[-2] diff --git a/meson.build b/meson.build index 6ad0463..3f02985 100644 --- a/meson.build +++ b/meson.build @@ -99,11 +99,11 @@ if backend_x11 endif subdir('completions') -subdir('doc') subdir('bar') subdir('decorations') subdir('particles') subdir('modules') +subdir('doc') env = find_program('env', native: true) generate_version_sh = files('generate-version.sh') From c4cc1b7a360d1a3e512c651f68aa766af5031bf1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 14 Dec 2022 10:30:20 +0100 Subject: [PATCH 440/611] changelog: all modules are now compile-time optional --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ddde62d..8360e27 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,6 +50,7 @@ ### Changed +* All modules are now compile-time optional. * Minimum required meson version is now 0.59. * Float tags are now treated as floats instead of integers when formatted with the `kb`/`kib`/`mb`/`mib`/`gb`/`gib` string particle From b22614ecc3f707c8d1f392991ee39e43fddc1c48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 14 Dec 2022 10:54:26 +0100 Subject: [PATCH 441/611] ci (woodpecker): add pipewire-dev to x86 builds Before this, it was only added in the x64 builds --- .woodpecker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.woodpecker.yml b/.woodpecker.yml index 02fd5f6..058b08a 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -95,7 +95,7 @@ pipeline: - apk add pixman-dev freetype-dev fontconfig-dev - apk add libxcb-dev xcb-util-wm-dev xcb-util-cursor-dev yaml-dev - apk add wayland-dev wayland-protocols wlroots-dev - - apk add json-c-dev libmpdclient-dev alsa-lib-dev pulseaudio-dev + - apk add json-c-dev libmpdclient-dev alsa-lib-dev pulseaudio-dev pipewire-dev - apk add ttf-dejavu - apk add git - apk add flex bison From 95b1f5f2618f8e0cf1e3b9aad836dd6fc5f46bcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 14 Dec 2022 11:01:05 +0100 Subject: [PATCH 442/611] =?UTF-8?q?modules:=20meson:=20regression:=20it?= =?UTF-8?q?=E2=80=99s=20=E2=80=98libudev=E2=80=99,=20not=20=E2=80=98udev?= =?UTF-8?q?=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/meson.build b/modules/meson.build index f14b3df..066e5d9 100644 --- a/modules/meson.build +++ b/modules/meson.build @@ -35,7 +35,7 @@ plugin_pipewire_enabled = pipewire.found() and json_pipewire.found() pulse = dependency('libpulse', required: get_option('plugin-pulse')) plugin_pulse_enabled = pulse.found() -udev_removables = dependency('udev', required: get_option('plugin-removables')) +udev_removables = dependency('libudev', required: get_option('plugin-removables')) plugin_removables_enabled = udev_removables.found() plugin_river_enabled = backend_wayland and get_option('plugin-river').allowed() From 3ca274759aa440892a34a2ed3caf59a987ea434f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 14 Dec 2022 12:05:17 +0100 Subject: [PATCH 443/611] =?UTF-8?q?module:=20const:ify=20=E2=80=98module?= =?UTF-8?q?=E2=80=99=20argument=20to=20module->description()?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- module.h | 2 +- modules/alsa.c | 4 ++-- modules/backlight.c | 2 +- modules/battery.c | 4 ++-- modules/clock.c | 2 +- modules/cpu.c | 2 +- modules/disk-io.c | 2 +- modules/dwl.c | 2 +- modules/foreign-toplevel.c | 2 +- modules/i3.c | 2 +- modules/label.c | 2 +- modules/mem.c | 2 +- modules/mpd.c | 2 +- modules/network.c | 4 ++-- modules/pipewire.c | 2 +- modules/pulse.c | 2 +- modules/removables.c | 2 +- modules/river.c | 2 +- modules/script.c | 4 ++-- modules/sway-xkb.c | 2 +- modules/xkb.c | 2 +- modules/xwindow.c | 2 +- 22 files changed, 26 insertions(+), 26 deletions(-) diff --git a/module.h b/module.h index e525c87..5f1bc7c 100644 --- a/module.h +++ b/module.h @@ -27,7 +27,7 @@ struct module { * specified number of milliseconds */ bool (*refresh_in)(struct module *mod, long milli_seconds); - const char *(*description)(struct module *mod); + const char *(*description)(const struct module *mod); }; struct module *module_common_new(void); diff --git a/modules/alsa.c b/modules/alsa.c index b3aaba8..6003ae6 100644 --- a/modules/alsa.c +++ b/modules/alsa.c @@ -84,10 +84,10 @@ destroy(struct module *mod) } static const char * -description(struct module *mod) +description(const struct module *mod) { static char desc[32]; - struct private *m = mod->private; + const struct private *m = mod->private; snprintf(desc, sizeof(desc), "alsa(%s)", m->card); return desc; } diff --git a/modules/backlight.c b/modules/backlight.c index af58280..9f26a14 100644 --- a/modules/backlight.c +++ b/modules/backlight.c @@ -40,7 +40,7 @@ destroy(struct module *mod) } static const char * -description(struct module *mod) +description(const struct module *mod) { return "backlight"; } diff --git a/modules/battery.c b/modules/battery.c index 0833310..c4f949c 100644 --- a/modules/battery.c +++ b/modules/battery.c @@ -58,10 +58,10 @@ destroy(struct module *mod) } static const char * -description(struct module *mod) +description(const struct module *mod) { static char desc[32]; - struct private *m = mod->private; + const struct private *m = mod->private; snprintf(desc, sizeof(desc), "bat(%s)", m->battery); return desc; } diff --git a/modules/clock.c b/modules/clock.c index 15392aa..0487a9d 100644 --- a/modules/clock.c +++ b/modules/clock.c @@ -38,7 +38,7 @@ destroy(struct module *mod) } static const char * -description(struct module *mod) +description(const struct module *mod) { return "clock"; } diff --git a/modules/cpu.c b/modules/cpu.c index 4d2c3c8..41ac95f 100644 --- a/modules/cpu.c +++ b/modules/cpu.c @@ -48,7 +48,7 @@ destroy(struct module *mod) } static const char * -description(struct module *mod) +description(const struct module *mod) { return "cpu"; } diff --git a/modules/disk-io.c b/modules/disk-io.c index ee6da8a..4ce665f 100644 --- a/modules/disk-io.c +++ b/modules/disk-io.c @@ -92,7 +92,7 @@ destroy(struct module *mod) } static const char * -description(struct module *mod) +description(const struct module *mod) { return "disk-io"; } diff --git a/modules/dwl.c b/modules/dwl.c index 6c8ccff..00b4cb8 100644 --- a/modules/dwl.c +++ b/modules/dwl.c @@ -67,7 +67,7 @@ destroy(struct module *module) } static char const * -description(struct module *module) +description(const struct module *module) { return "dwl"; } diff --git a/modules/foreign-toplevel.c b/modules/foreign-toplevel.c index 2997042..d454a39 100644 --- a/modules/foreign-toplevel.c +++ b/modules/foreign-toplevel.c @@ -92,7 +92,7 @@ destroy(struct module *mod) } static const char * -description(struct module *mod) +description(const struct module *mod) { return "toplevel"; } diff --git a/modules/i3.c b/modules/i3.c index 160913b..f0aa137 100644 --- a/modules/i3.c +++ b/modules/i3.c @@ -760,7 +760,7 @@ ws_content_for_name(struct private *m, const char *name) } static const char * -description(struct module *mod) +description(const struct module *mod) { return "i3/sway"; } diff --git a/modules/label.c b/modules/label.c index 01fce76..7e8ee09 100644 --- a/modules/label.c +++ b/modules/label.c @@ -22,7 +22,7 @@ destroy(struct module *mod) } static const char * -description(struct module *mod) +description(const struct module *mod) { return "label"; } diff --git a/modules/mem.c b/modules/mem.c index d79861e..339501b 100644 --- a/modules/mem.c +++ b/modules/mem.c @@ -35,7 +35,7 @@ destroy(struct module *mod) } static const char * -description(struct module *mod) +description(const struct module *mod) { return "mem"; } diff --git a/modules/mpd.c b/modules/mpd.c index 05edd7b..604bf88 100644 --- a/modules/mpd.c +++ b/modules/mpd.c @@ -86,7 +86,7 @@ destroy(struct module *mod) } static const char * -description(struct module *mod) +description(const struct module *mod) { return "mpd"; } diff --git a/modules/network.c b/modules/network.c index 1cb7a2e..17358a6 100644 --- a/modules/network.c +++ b/modules/network.c @@ -107,10 +107,10 @@ destroy(struct module *mod) } static const char * -description(struct module *mod) +description(const struct module *mod) { static char desc[32]; - struct private *m = mod->private; + const struct private *m = mod->private; snprintf(desc, sizeof(desc), "net(%s)", m->iface); return desc; diff --git a/modules/pipewire.c b/modules/pipewire.c index e369bd2..bfbb15b 100644 --- a/modules/pipewire.c +++ b/modules/pipewire.c @@ -818,7 +818,7 @@ destroy(struct module *module) } static char const * -description(struct module *module) +description(const struct module *module) { return "pipewire"; } diff --git a/modules/pulse.c b/modules/pulse.c index c4955ac..9eba3a5 100644 --- a/modules/pulse.c +++ b/modules/pulse.c @@ -57,7 +57,7 @@ destroy(struct module *mod) } static const char * -description(struct module *mod) +description(const struct module *mod) { return "pulse"; } diff --git a/modules/removables.c b/modules/removables.c index d8fe7c9..4d1d508 100644 --- a/modules/removables.c +++ b/modules/removables.c @@ -101,7 +101,7 @@ destroy(struct module *mod) } static const char * -description(struct module *mod) +description(const struct module *mod) { return "removables"; } diff --git a/modules/river.c b/modules/river.c index a9dc8b4..059a44e 100644 --- a/modules/river.c +++ b/modules/river.c @@ -70,7 +70,7 @@ destroy(struct module *mod) } static const char * -description(struct module *mod) +description(const struct module *mod) { return "river"; } diff --git a/modules/script.c b/modules/script.c index fd149d2..1c349a1 100644 --- a/modules/script.c +++ b/modules/script.c @@ -60,10 +60,10 @@ destroy(struct module *mod) } static const char * -description(struct module *mod) +description(const struct module *mod) { static char desc[32]; - struct private *m = mod->private; + const struct private *m = mod->private; char *path = strdup(m->path); snprintf(desc, sizeof(desc), "script(%s)", basename(path)); diff --git a/modules/sway-xkb.c b/modules/sway-xkb.c index 269df24..96d8388 100644 --- a/modules/sway-xkb.c +++ b/modules/sway-xkb.c @@ -55,7 +55,7 @@ destroy(struct module *mod) } static const char * -description(struct module *mod) +description(const struct module *mod) { return "sway-xkb"; } diff --git a/modules/xkb.c b/modules/xkb.c index 965664d..edd0afe 100644 --- a/modules/xkb.c +++ b/modules/xkb.c @@ -73,7 +73,7 @@ destroy(struct module *mod) } static const char * -description(struct module *mod) +description(const struct module *mod) { return "xkb"; } diff --git a/modules/xwindow.c b/modules/xwindow.c index baf1239..e53114e 100644 --- a/modules/xwindow.c +++ b/modules/xwindow.c @@ -39,7 +39,7 @@ struct private { }; static const char * -description(struct module *mod) +description(const struct module *mod) { return "xwindow"; } From 2e0e1a402fab051e0b803406d65f47e485e5ea1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 14 Dec 2022 12:05:34 +0100 Subject: [PATCH 444/611] bar: also log module name when logging a failed module --- bar/bar.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/bar/bar.c b/bar/bar.c index ab9a587..10e0785 100644 --- a/bar/bar.c +++ b/bar/bar.c @@ -345,20 +345,29 @@ run(struct bar *_bar) int mod_ret; for (size_t i = 0; i < bar->left.count; i++) { thrd_join(thrd_left[i], &mod_ret); - if (mod_ret != 0) - LOG_ERR("module: LEFT #%zu: non-zero exit value: %d", i, mod_ret); + if (mod_ret != 0) { + const struct module *m = bar->left.mods[i]; + LOG_ERR("module: LEFT #%zu (%s): non-zero exit value: %d", + i, m->description(m), mod_ret); + } ret = ret == 0 && mod_ret != 0 ? mod_ret : ret; } for (size_t i = 0; i < bar->center.count; i++) { thrd_join(thrd_center[i], &mod_ret); - if (mod_ret != 0) - LOG_ERR("module: CENTER #%zu: non-zero exit value: %d", i, mod_ret); + if (mod_ret != 0) { + const struct module *m = bar->center.mods[i]; + LOG_ERR("module: CENTER #%zu (%s): non-zero exit value: %d", + i, m->description(m), mod_ret); + } ret = ret == 0 && mod_ret != 0 ? mod_ret : ret; } for (size_t i = 0; i < bar->right.count; i++) { thrd_join(thrd_right[i], &mod_ret); - if (mod_ret != 0) - LOG_ERR("module: RIGHT #%zu: non-zero exit value: %d", i, mod_ret); + if (mod_ret != 0) { + const struct module *m = bar->right.mods[i]; + LOG_ERR("module: RIGHT #%zu (%s): non-zero exit value: %d", + i, m->description(m), mod_ret); + } ret = ret == 0 && mod_ret != 0 ? mod_ret : ret; } From b195bc4dcb1c335744cc4d4247c6deb1dd6fd96b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 14 Dec 2022 11:49:14 +0100 Subject: [PATCH 445/611] =?UTF-8?q?module/cpu:=20make=20=E2=80=98content?= =?UTF-8?q?=E2=80=99=20particle=20a=20template?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before this patch, the cpu module instantiated a single particle (the ‘content’ particle), with one tag ("cpu") representing the total CPU usage, and then one tag (cpuN) for each core. This makes it cumbersome to configure, since you need to explicitly reference each cpuN tag to get per-core usage. This patch rewrites this, so that ‘content’ is now a template. It’s instantiated once to represent the total CPU usage, and then once for each core. Each instance has a "cpu" tag, representing the CPU usage of that core (or total usage). It also has an "id" tag. The ID is 0..n for actual cores, and -1 for total usage. This means you can do something like this in your config: - cpu: content: map: conditions: id < 0: {string: {text: "Total: {cpu}%"}} id >= 0: {string: {text: "Core #{id}: {cpu}%"}} Closes #207 --- CHANGELOG.md | 4 ++ doc/yambar-modules-cpu.5.scd | 49 +++++++++++-- modules/cpu.c | 135 ++++++++++++++++++++++------------- modules/meson.build | 2 +- 4 files changed, 135 insertions(+), 55 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8360e27..7f15bcf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -62,6 +62,9 @@ non-empty ([#191][191]) * alsa: use dB instead of raw volume values, if possible, when calculating the `percent` tag ([#202][202]) +* cpu: `content` particle is now a template instantiated once for each + core, and once for the total CPU usage. See + **yambar-modules-cpu**(5) for more information ([#207][207]). * **BREAKING CHANGE**: overhaul of the `map` particle. Instead of specifying a `tag` and then an array of `values`, you must now simply use an array of `conditions`, that consist of: @@ -119,6 +122,7 @@ [182]: https://codeberg.org/dnkl/yambar/issues/182 [191]: https://codeberg.org/dnkl/yambar/issues/191 [202]: https://codeberg.org/dnkl/yambar/issues/202 +[207]: https://codeberg.org/dnkl/yambar/issues/207 ### Deprecated diff --git a/doc/yambar-modules-cpu.5.scd b/doc/yambar-modules-cpu.5.scd index 2a05fdf..2e2bda4 100644 --- a/doc/yambar-modules-cpu.5.scd +++ b/doc/yambar-modules-cpu.5.scd @@ -3,17 +3,24 @@ yambar-modules-cpu(5) # NAME cpu - This module provides the CPU usage +# DESCRIPTION + +This module reports CPU usage, in percent. The _content_ particle is a +template that is instantiated once for each core, and once for the +total CPU usage. + # TAGS [[ *Name* :[ *Type* :[ *Description* +| id +: int +: Core ID. 0..n represents individual cores, and -1 represents the + total usage | cpu : range -: Current usage of the whole CPU in percent -| cpu<0..X> -: range -: Current usage of CPU core X in percent +: Current usage of CPU core {id}, in percent # CONFIGURATION @@ -24,19 +31,49 @@ cpu - This module provides the CPU usage | interval : int : no -: Refresh interval of the CPU usage stats in ms (default=500). Cannot be less then 500 ms +: Refresh interval of the CPU usage stats in ms (default=500). Cannot + be less then 500 ms # EXAMPLES +## Display total CPU usage as a number ``` bar: left: - cpu: interval: 2500 content: - string: {text: "{cpu1}%"} + map: + conditions: + id < 0: + - string: {text: , font: Font Awesome 6 Free:style=solid} + - string: {text: "{cpu}%"} ``` +## Display a vertical bar for each core +``` +bar: + left: + - cpu: + interval: 2500 + content: + map: + conditions: + id >= 0: + - ramp: + tag: cpu + items: + - string: {text: ▁} + - string: {text: ▂} + - string: {text: ▃} + - string: {text: ▄} + - string: {text: ▅} + - string: {text: ▆} + - string: {text: ▇} + - string: {text: █} +``` + + # SEE ALSO *yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5) diff --git a/modules/cpu.c b/modules/cpu.c index 41ac95f..b63e926 100644 --- a/modules/cpu.c +++ b/modules/cpu.c @@ -14,11 +14,14 @@ #define LOG_MODULE "cpu" #define LOG_ENABLE_DBG 0 #define SMALLEST_INTERVAL 500 +#include "../log.h" + #include "../bar/bar.h" #include "../config-verify.h" #include "../config.h" -#include "../log.h" +#include "../particles/dynlist.h" #include "../plugin.h" + struct cpu_stats { uint32_t *prev_cores_idle; uint32_t *prev_cores_nidle; @@ -27,10 +30,10 @@ struct cpu_stats { uint32_t *cur_cores_nidle; }; -struct private -{ - struct particle *label; +struct private { + struct particle *template; uint16_t interval; + size_t core_count; struct cpu_stats cpu_stats; }; @@ -38,12 +41,14 @@ static void destroy(struct module *mod) { struct private *m = mod->private; - m->label->destroy(m->label); + + m->template->destroy(m->template); free(m->cpu_stats.prev_cores_idle); free(m->cpu_stats.prev_cores_nidle); free(m->cpu_stats.cur_cores_idle); free(m->cpu_stats.cur_cores_nidle); free(m); + module_default_destroy(mod); } @@ -63,9 +68,10 @@ get_cpu_nb_cores() } static bool -parse_proc_stat_line(const char *line, uint32_t *user, uint32_t *nice, uint32_t *system, uint32_t *idle, - uint32_t *iowait, uint32_t *irq, uint32_t *softirq, uint32_t *steal, uint32_t *guest, - uint32_t *guestnice) +parse_proc_stat_line(const char *line, uint32_t *user, uint32_t *nice, + uint32_t *system, uint32_t *idle, uint32_t *iowait, + uint32_t *irq, uint32_t *softirq, uint32_t *steal, + uint32_t *guest, uint32_t *guestnice) { int32_t core_id; if (line[sizeof("cpu") - 1] == ' ') { @@ -91,21 +97,26 @@ parse_proc_stat_line(const char *line, uint32_t *user, uint32_t *nice, uint32_t static uint8_t get_cpu_usage_percent(const struct cpu_stats *cpu_stats, int8_t core_idx) { - uint32_t prev_total = cpu_stats->prev_cores_idle[core_idx + 1] + cpu_stats->prev_cores_nidle[core_idx + 1]; - uint32_t cur_total = cpu_stats->cur_cores_idle[core_idx + 1] + cpu_stats->cur_cores_nidle[core_idx + 1]; + uint32_t prev_total = + cpu_stats->prev_cores_idle[core_idx + 1] + + cpu_stats->prev_cores_nidle[core_idx + 1]; + + uint32_t cur_total = + cpu_stats->cur_cores_idle[core_idx + 1] + + cpu_stats->cur_cores_nidle[core_idx + 1]; double totald = cur_total - prev_total; - double nidled = cpu_stats->cur_cores_nidle[core_idx + 1] - cpu_stats->prev_cores_nidle[core_idx + 1]; + double nidled = + cpu_stats->cur_cores_nidle[core_idx + 1] - + cpu_stats->prev_cores_nidle[core_idx + 1]; double percent = (nidled * 100) / (totald + 1); - return round(percent); } static void -refresh_cpu_stats(struct cpu_stats *cpu_stats) +refresh_cpu_stats(struct cpu_stats *cpu_stats, size_t core_count) { - uint32_t nb_cores = get_cpu_nb_cores(); int32_t core = 0; uint32_t user = 0; uint32_t nice = 0; @@ -129,11 +140,11 @@ refresh_cpu_stats(struct cpu_stats *cpu_stats) return; } - while ((read = getline(&line, &len, fp)) != -1 && core <= nb_cores) { + while ((read = getline(&line, &len, fp)) != -1 && core <= core_count) { if (strncmp(line, "cpu", sizeof("cpu") - 1) == 0) { if (!parse_proc_stat_line( - line, &user, &nice, &system, &idle, &iowait, &irq, &softirq, &steal, - &guest, &guestnice)) + line, &user, &nice, &system, &idle, &iowait, &irq, &softirq, + &steal, &guest, &guestnice)) { LOG_ERR("unable to parse /proc/stat line"); goto exit; @@ -148,6 +159,7 @@ refresh_cpu_stats(struct cpu_stats *cpu_stats) core++; } } + exit: fclose(fp); free(line); @@ -156,29 +168,45 @@ exit: static struct exposable * content(struct module *mod) { - const struct private *p = mod->private; - uint32_t nb_cores = get_cpu_nb_cores(); + const struct private *m = mod->private; - char cpu_name[32]; - struct tag_set tags; - tags.count = nb_cores + 1; - tags.tags = calloc(tags.count, sizeof(*tags.tags)); mtx_lock(&mod->lock); - uint8_t cpu_usage = get_cpu_usage_percent(&p->cpu_stats, -1); - tags.tags[0] = tag_new_int_range(mod, "cpu", cpu_usage, 0, 100); - for (uint32_t i = 0; i < nb_cores; ++i) { - uint8_t cpu_usage = get_cpu_usage_percent(&p->cpu_stats, i); - snprintf(cpu_name, sizeof(cpu_name), "cpu%u", i); - tags.tags[i + 1] = tag_new_int_range(mod, cpu_name, cpu_usage, 0, 100); + const size_t list_count = m->core_count + 1; + struct exposable *parts[list_count]; + + { + uint8_t total_usage = get_cpu_usage_percent(&m->cpu_stats, -1); + + struct tag_set tags = { + .tags = (struct tag *[]){ + tag_new_int(mod, "id", -1), + tag_new_int_range(mod, "cpu", total_usage, 0, 100), + }, + .count = 2, + }; + + parts[0] = m->template->instantiate(m->template, &tags); + tag_set_destroy(&tags); } + + for (size_t i = 0; i < m->core_count; i++) { + uint8_t core_usage = get_cpu_usage_percent(&m->cpu_stats, i); + + struct tag_set tags = { + .tags = (struct tag *[]){ + tag_new_int(mod, "id", i), + tag_new_int_range(mod, "cpu", core_usage, 0, 100), + }, + .count = 2, + }; + + parts[i + 1] = m->template->instantiate(m->template, &tags); + tag_set_destroy(&tags); + } + mtx_unlock(&mod->lock); - - struct exposable *exposable = p->label->instantiate(p->label, &tags); - - tag_set_destroy(&tags); - free(tags.tags); - return exposable; + return dynlist_exposable_new(parts, list_count, 0, 0); } static int @@ -186,6 +214,7 @@ run(struct module *mod) { const struct bar *bar = mod->bar; bar->refresh(bar); + struct private *p = mod->private; while (true) { struct pollfd fds[] = {{.fd = mod->abort_fd, .events = POLLIN}}; @@ -203,7 +232,7 @@ run(struct module *mod) break; mtx_lock(&mod->lock); - refresh_cpu_stats(&p->cpu_stats); + refresh_cpu_stats(&p->cpu_stats, p->core_count); mtx_unlock(&mod->lock); bar->refresh(bar); } @@ -212,17 +241,24 @@ run(struct module *mod) } static struct module * -cpu_new(uint16_t interval, struct particle *label) +cpu_new(uint16_t interval, struct particle *template) { - struct private *p = calloc(1, sizeof(*p)); - p->label = label; uint32_t nb_cores = get_cpu_nb_cores(); - p->interval = interval; - p->cpu_stats.prev_cores_nidle = calloc(nb_cores + 1, sizeof(*p->cpu_stats.prev_cores_nidle)); - p->cpu_stats.prev_cores_idle = calloc(nb_cores + 1, sizeof(*p->cpu_stats.prev_cores_idle)); - p->cpu_stats.cur_cores_nidle = calloc(nb_cores + 1, sizeof(*p->cpu_stats.cur_cores_nidle)); - p->cpu_stats.cur_cores_idle = calloc(nb_cores + 1, sizeof(*p->cpu_stats.cur_cores_idle)); + struct private *p = calloc(1, sizeof(*p)); + p->template = template; + p->interval = interval; + p->core_count = nb_cores; + + p->cpu_stats.prev_cores_nidle = calloc( + nb_cores + 1, sizeof(*p->cpu_stats.prev_cores_nidle)); + p->cpu_stats.prev_cores_idle = calloc( + nb_cores + 1, sizeof(*p->cpu_stats.prev_cores_idle)); + + p->cpu_stats.cur_cores_nidle = calloc( + nb_cores + 1, sizeof(*p->cpu_stats.cur_cores_nidle)); + p->cpu_stats.cur_cores_idle = calloc( + nb_cores + 1, sizeof(*p->cpu_stats.cur_cores_idle)); struct module *mod = module_common_new(); mod->private = p; @@ -236,10 +272,12 @@ cpu_new(uint16_t interval, struct particle *label) static struct module * from_conf(const struct yml_node *node, struct conf_inherit inherited) { - const struct yml_node *interval = yml_get_value(node, "interval"); + const struct yml_node *interval = yml_get_value(node, "poll-interval"); const struct yml_node *c = yml_get_value(node, "content"); - return cpu_new(interval == NULL ? SMALLEST_INTERVAL : yml_value_as_int(interval), conf_to_particle(c, inherited)); + return cpu_new( + interval == NULL ? SMALLEST_INTERVAL : yml_value_as_int(interval), + conf_to_particle(c, inherited)); } static bool @@ -249,7 +287,8 @@ conf_verify_interval(keychain_t *chain, const struct yml_node *node) return false; if (yml_value_as_int(node) < SMALLEST_INTERVAL) { - LOG_ERR("%s: interval value cannot be less than %d ms", conf_err_prefix(chain, node), SMALLEST_INTERVAL); + LOG_ERR("%s: interval value cannot be less than %d ms", + conf_err_prefix(chain, node), SMALLEST_INTERVAL); return false; } @@ -260,7 +299,7 @@ static bool verify_conf(keychain_t *chain, const struct yml_node *node) { static const struct attr_info attrs[] = { - {"interval", false, &conf_verify_interval}, + {"poll-interval", false, &conf_verify_interval}, MODULE_COMMON_ATTRS, }; diff --git a/modules/meson.build b/modules/meson.build index 066e5d9..49aba6d 100644 --- a/modules/meson.build +++ b/modules/meson.build @@ -70,7 +70,7 @@ if plugin_clock_enabled endif if plugin_cpu_enabled - mod_data += {'cpu': [[], []]} + mod_data += {'cpu': [[], [dynlist]]} endif if plugin_disk_io_enabled From 06bf1273324be1625c020a9e6a9409554491cf42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 14 Dec 2022 12:18:47 +0100 Subject: [PATCH 446/611] doc: expand last column to fill screen in all tables --- doc/yambar-decorations.5.scd | 8 ++++---- doc/yambar-modules-alsa.5.scd | 4 ++-- doc/yambar-modules-backlight.5.scd | 2 +- doc/yambar-modules-battery.5.scd | 4 ++-- doc/yambar-modules-clock.5.scd | 4 ++-- doc/yambar-modules-cpu.5.scd | 4 ++-- doc/yambar-modules-disk-io.5.scd | 4 ++-- doc/yambar-modules-dwl.5.scd | 4 ++-- doc/yambar-modules-foreign-toplevel.5.scd | 4 ++-- doc/yambar-modules-i3.5.scd | 4 ++-- doc/yambar-modules-mem.5.scd | 4 ++-- doc/yambar-modules-mpd.5.scd | 4 ++-- doc/yambar-modules-network.5.scd | 4 ++-- doc/yambar-modules-pipewire.5.scd | 2 +- doc/yambar-modules-pulse.5.scd | 4 ++-- doc/yambar-modules-removables.5.scd | 4 ++-- doc/yambar-modules-river.5.scd | 10 ++++++---- doc/yambar-modules-script.5.scd | 2 +- doc/yambar-modules-sway-xkb.5.scd | 4 ++-- doc/yambar-modules-xkb.5.scd | 2 +- doc/yambar-modules-xwindow.5.scd | 2 +- doc/yambar-modules.5.scd | 2 +- doc/yambar-particles.5.scd | 12 ++++++------ doc/yambar-tags.5.scd | 4 ++-- doc/yambar.5.scd | 2 +- 25 files changed, 53 insertions(+), 51 deletions(-) diff --git a/doc/yambar-decorations.5.scd b/doc/yambar-decorations.5.scd index 24282b5..9dd21b8 100644 --- a/doc/yambar-decorations.5.scd +++ b/doc/yambar-decorations.5.scd @@ -23,7 +23,7 @@ This decoration sets the particles background color. [[ *Name* :[ *Type* :[ *Req* -:[ *Description* +:< *Description* | color : color : yes @@ -49,7 +49,7 @@ bottom of the particle. [[ *Name* :[ *Type* :[ *Req* -:[ *Description* +:< *Description* | size : int : yes @@ -81,7 +81,7 @@ size and color at the top of the particle. [[ *Name* :[ *Type* :[ *Req* -:[ *Description* +:< *Description* | size : int : yes @@ -113,7 +113,7 @@ width) around the particle. [[ *Name* :[ *Type* :[ *Req* -:[ *Description* +:< *Description* | color : color : yes diff --git a/doc/yambar-modules-alsa.5.scd b/doc/yambar-modules-alsa.5.scd index 17f1a29..14804e5 100644 --- a/doc/yambar-modules-alsa.5.scd +++ b/doc/yambar-modules-alsa.5.scd @@ -7,7 +7,7 @@ alsa - Monitors an alsa soundcard for volume and mute/unmute changes [[ *Name* :[ *Type* -:[ *Description* +:< *Description* | online : bool : True when the ALSA device has successfully been opened @@ -32,7 +32,7 @@ alsa - Monitors an alsa soundcard for volume and mute/unmute changes [[ *Name* :[ *Type* :[ *Req* -:[ *Description* +:< *Description* | card : string : yes diff --git a/doc/yambar-modules-backlight.5.scd b/doc/yambar-modules-backlight.5.scd index 7c1e6c6..6fa3a9a 100644 --- a/doc/yambar-modules-backlight.5.scd +++ b/doc/yambar-modules-backlight.5.scd @@ -11,7 +11,7 @@ _/sys/class/backlight_, and uses *udev* to monitor for changes. [[ *Name* :[ *Type* -:[ *Description* +:< *Description* | brightness : range : The current brightness level, in absolute value diff --git a/doc/yambar-modules-battery.5.scd b/doc/yambar-modules-battery.5.scd index 3d55ed9..101c02b 100644 --- a/doc/yambar-modules-battery.5.scd +++ b/doc/yambar-modules-battery.5.scd @@ -23,7 +23,7 @@ the state *unknown* under other conditions. [[ *Name* :[ *Type* -:[ *Description* +:< *Description* | name : string : Battery device name @@ -49,7 +49,7 @@ the state *unknown* under other conditions. [[ *Name* :[ *Type* :[ *Req* -:[ *Description* +:< *Description* | name : string : yes diff --git a/doc/yambar-modules-clock.5.scd b/doc/yambar-modules-clock.5.scd index e8dbadf..bf3506b 100644 --- a/doc/yambar-modules-clock.5.scd +++ b/doc/yambar-modules-clock.5.scd @@ -7,7 +7,7 @@ clock - This module provides the current date and time [[ *Name* :[ *Type* -:[ *Description* +:< *Description* | time : string : Current time, formatted using the _time-format_ attribute @@ -20,7 +20,7 @@ clock - This module provides the current date and time [[ *Name* :[ *Type* :[ *Req* -:[ *Description* +:< *Description* | time-format : string : no diff --git a/doc/yambar-modules-cpu.5.scd b/doc/yambar-modules-cpu.5.scd index 2e2bda4..1b4219e 100644 --- a/doc/yambar-modules-cpu.5.scd +++ b/doc/yambar-modules-cpu.5.scd @@ -13,7 +13,7 @@ total CPU usage. [[ *Name* :[ *Type* -:[ *Description* +:< *Description* | id : int : Core ID. 0..n represents individual cores, and -1 represents the @@ -27,7 +27,7 @@ total CPU usage. [[ *Name* :[ *Type* :[ *Req* -:[ *Description* +:< *Description* | interval : int : no diff --git a/doc/yambar-modules-disk-io.5.scd b/doc/yambar-modules-disk-io.5.scd index 92ce449..4def79f 100644 --- a/doc/yambar-modules-disk-io.5.scd +++ b/doc/yambar-modules-disk-io.5.scd @@ -9,7 +9,7 @@ currently present in the machine. [[ *Name* :[ *Type* -:[ *Description* +:< *Description* | device : string : Name of the device being tracked (use the command *lsblk* to see these). @@ -34,7 +34,7 @@ currently present in the machine. [[ *Name* :[ *Type* :[ *Req* -:[ *Description* +:< *Description* | interval : int : no diff --git a/doc/yambar-modules-dwl.5.scd b/doc/yambar-modules-dwl.5.scd index 4e41691..be34de1 100644 --- a/doc/yambar-modules-dwl.5.scd +++ b/doc/yambar-modules-dwl.5.scd @@ -25,7 +25,7 @@ Running multiple instances at the same time may result in [[ *Name* :[ *Type* -:[ *Description* +:< *Description* | id : int : Dwl tag id. @@ -59,7 +59,7 @@ Running multiple instances at the same time may result in [[ *Name* :[ *Type* :[ *Req* -:[ *Description* +:< *Description* | number-of-tags : int : yes diff --git a/doc/yambar-modules-foreign-toplevel.5.scd b/doc/yambar-modules-foreign-toplevel.5.scd index 94c4b84..8b1d576 100644 --- a/doc/yambar-modules-foreign-toplevel.5.scd +++ b/doc/yambar-modules-foreign-toplevel.5.scd @@ -21,7 +21,7 @@ Note: Wayland only. [[ *Name* :[ *Type* -:[ *Description* +:< *Description* | app-id : string : The application ID (typically the application name) @@ -47,7 +47,7 @@ Note: Wayland only. [[ *Name* :[ *Type* :[ *Req* -:[ *Description* +:< *Description* | content : particle : yes diff --git a/doc/yambar-modules-i3.5.scd b/doc/yambar-modules-i3.5.scd index f82d131..37d54bf 100644 --- a/doc/yambar-modules-i3.5.scd +++ b/doc/yambar-modules-i3.5.scd @@ -22,7 +22,7 @@ with the _application_ and _title_ tags to replace the X11-only [[ *Name* :[ *Type* -:[ *Description* +:< *Description* | name : string : The workspace name @@ -57,7 +57,7 @@ with the _application_ and _title_ tags to replace the X11-only [[ *Name* :[ *Type* :[ *Req* -:[ *Description* +:< *Description* | content : associative array : yes diff --git a/doc/yambar-modules-mem.5.scd b/doc/yambar-modules-mem.5.scd index cec575c..82a70c3 100644 --- a/doc/yambar-modules-mem.5.scd +++ b/doc/yambar-modules-mem.5.scd @@ -7,7 +7,7 @@ mem - This module provides the memory usage [[ *Name* :[ *Type* -:[ *Description* +:< *Description* | free : int : Free memory in bytes @@ -29,7 +29,7 @@ mem - This module provides the memory usage [[ *Name* :[ *Type* :[ *Req* -:[ *Description* +:< *Description* | interval : string : no diff --git a/doc/yambar-modules-mpd.5.scd b/doc/yambar-modules-mpd.5.scd index ccc5d85..aff6227 100644 --- a/doc/yambar-modules-mpd.5.scd +++ b/doc/yambar-modules-mpd.5.scd @@ -7,7 +7,7 @@ mpd - This module provides MPD status such as currently playing artist/album/son [[ *Name* :[ *Type* -:[ *Description* +:< *Description* | state : string : One of *offline*, *stopped*, *paused* or *playing* @@ -56,7 +56,7 @@ mpd - This module provides MPD status such as currently playing artist/album/son [[ *Name* :[ *Type* :[ *Req* -:[ *Description* +:< *Description* | host : string : yes diff --git a/doc/yambar-modules-network.5.scd b/doc/yambar-modules-network.5.scd index d8129ef..7bb9afc 100644 --- a/doc/yambar-modules-network.5.scd +++ b/doc/yambar-modules-network.5.scd @@ -16,7 +16,7 @@ address. [[ *Name* :[ *Type* -:[ *Description* +:< *Description* | name : string : Network interface name @@ -64,7 +64,7 @@ address. [[ *Name* :[ *Type* :[ *Req* -:[ *Description* +:< *Description* | name : string : yes diff --git a/doc/yambar-modules-pipewire.5.scd b/doc/yambar-modules-pipewire.5.scd index 4e0f587..0f7a40b 100644 --- a/doc/yambar-modules-pipewire.5.scd +++ b/doc/yambar-modules-pipewire.5.scd @@ -7,7 +7,7 @@ pipewire - Monitors pipewire for volume, mute/unmute, device change [[ *Name* :[ *Type* -:[ *Description* +:< *Description* | type : string : Either "source" (capture) or "sink" (speaker) diff --git a/doc/yambar-modules-pulse.5.scd b/doc/yambar-modules-pulse.5.scd index 95df59a..008ec78 100644 --- a/doc/yambar-modules-pulse.5.scd +++ b/doc/yambar-modules-pulse.5.scd @@ -7,7 +7,7 @@ pulse - Monitors a PulseAudio source and/or sink [[ *Name* :[ *Type* -:[ *Description* +:< *Description* | online : bool : True when connected to the PulseAudio server @@ -41,7 +41,7 @@ pulse - Monitors a PulseAudio source and/or sink [[ *Name* :[ *Type* :[ *Req* -:[ *Description* +:< *Description* | sink : string : no diff --git a/doc/yambar-modules-removables.5.scd b/doc/yambar-modules-removables.5.scd index ee7911f..0a193d4 100644 --- a/doc/yambar-modules-removables.5.scd +++ b/doc/yambar-modules-removables.5.scd @@ -12,7 +12,7 @@ instantiates the provided _content_ particle for each detected drive. [[ *Name* :[ *Type* -:[ *Description* +:< *Description* | vendor : string : Name of the drive vendor @@ -48,7 +48,7 @@ instantiates the provided _content_ particle for each detected drive. [[ *Name* :[ *Type* :[ *Req* -:[ *Description* +:< *Description* | left-spacing : int : no diff --git a/doc/yambar-modules-river.5.scd b/doc/yambar-modules-river.5.scd index 4b3c6d8..7f2920f 100644 --- a/doc/yambar-modules-river.5.scd +++ b/doc/yambar-modules-river.5.scd @@ -24,7 +24,7 @@ once for all 32 river tags. This means you probably want to use a [[ *Name* :[ *Type* -:[ *Description* +:< *Description* | id : int : River tag number @@ -42,14 +42,16 @@ once for all 32 river tags. This means you probably want to use a : True if the river tag has views (i.e. windows). | state : string -: Set to *urgent* if _urgent_ is true, *focused* if _focused_ is true, *unfocused* if _visible_ is true, but _focused_ is false, or *invisible* if the river tag is not visible on any monitors. +: Set to *urgent* if _urgent_ is true, *focused* if _focused_ is true, + *unfocused* if _visible_ is true, but _focused_ is false, or + *invisible* if the river tag is not visible on any monitors. # TAGS (for the "title" particle) [[ *Name* :[ *Type* -:[ *Description* +:< *Description* | seat : string : The name of the seat (*title* particle only, see CONFIGURATION) @@ -66,7 +68,7 @@ once for all 32 river tags. This means you probably want to use a [[ *Name* :[ *Type* :[ *Req* -:[ *Description* +:< *Description* | title : particle : no diff --git a/doc/yambar-modules-script.5.scd b/doc/yambar-modules-script.5.scd index d021bf5..17ac34e 100644 --- a/doc/yambar-modules-script.5.scd +++ b/doc/yambar-modules-script.5.scd @@ -66,7 +66,7 @@ User defined. [[ *Name* :[ *Type* :[ *Req* -:[ *Description* +:< *Description* | path : string : yes diff --git a/doc/yambar-modules-sway-xkb.5.scd b/doc/yambar-modules-sway-xkb.5.scd index 567f11a..bcf948c 100644 --- a/doc/yambar-modules-sway-xkb.5.scd +++ b/doc/yambar-modules-sway-xkb.5.scd @@ -16,7 +16,7 @@ instantiated from this template, and represents an input device. [[ *Name* :[ *Type* -:[ *Description* +:< *Description* | id : string : Input device identifier @@ -29,7 +29,7 @@ instantiated from this template, and represents an input device. [[ *Name* :[ *Type* :[ *Req* -:[ *Description* +:< *Description* | identifiers : list of strings : yes diff --git a/doc/yambar-modules-xkb.5.scd b/doc/yambar-modules-xkb.5.scd index cb9b81c..ef03097 100644 --- a/doc/yambar-modules-xkb.5.scd +++ b/doc/yambar-modules-xkb.5.scd @@ -14,7 +14,7 @@ Note: this module is X11 only. It does not work in Wayland. [[ *Name* :[ *Type* -:[ *Description* +:< *Description* | name : string : Name of currently selected layout, long version (e.g. "English (US)") diff --git a/doc/yambar-modules-xwindow.5.scd b/doc/yambar-modules-xwindow.5.scd index b4c8e66..1aadbf7 100644 --- a/doc/yambar-modules-xwindow.5.scd +++ b/doc/yambar-modules-xwindow.5.scd @@ -16,7 +16,7 @@ _title_ tags. [[ *Name* :[ *Type* -:[ *Description* +:< *Description* | application : string : Name of the application that owns the currently focused window diff --git a/doc/yambar-modules.5.scd b/doc/yambar-modules.5.scd index 5767a52..177418f 100644 --- a/doc/yambar-modules.5.scd +++ b/doc/yambar-modules.5.scd @@ -110,7 +110,7 @@ following attributes are supported by all modules: [[ *Name* :[ *Type* :[ *Req* -:[ *Description* +:< *Description* | content : particle : yes diff --git a/doc/yambar-particles.5.scd b/doc/yambar-particles.5.scd index c04bf93..adf4485 100644 --- a/doc/yambar-particles.5.scd +++ b/doc/yambar-particles.5.scd @@ -12,7 +12,7 @@ following attributes are supported by all particles: [[ *Name* :[ *Type* :[ *Req* -:[ *Description* +:< *Description* | left-margin : int : no @@ -165,7 +165,7 @@ particle itself. [[ *Name* :[ *Type* :[ *Req* -:[ *Description* +:< *Description* | items : list : yes @@ -239,7 +239,7 @@ Where is the tag you would like to map, is one of: and is the value you would like to compare it to. *If the value contains any non-alphanumerical characters, you must -surround it with ' " ' *: +surround it with ' \" ' *: ``` "hello world" @@ -305,7 +305,7 @@ tx-bitrate > 1000: [[ *Name* :[ *Type* :[ *Req* -:[ *Description* +:< *Description* | conditions : associative array : yes @@ -359,7 +359,7 @@ indicator. [[ *Name* :[ *Type* :[ *Req* -:[ *Description* +:< *Description* | tag : string : yes @@ -414,7 +414,7 @@ itself when needed. [[ *Name* :[ *Type* :[ *Req* -:[ *Description* +:< *Description* | tag : string : yes diff --git a/doc/yambar-tags.5.scd b/doc/yambar-tags.5.scd index a8bb2ce..488fc77 100644 --- a/doc/yambar-tags.5.scd +++ b/doc/yambar-tags.5.scd @@ -11,7 +11,7 @@ their information. Each module defines its own set of tags. The available tag *types* are: [[ *Type* -:[ *Description* +:< *Description* | string : Value is a string. Rendered as-is by the _string_ particle. | int @@ -59,7 +59,7 @@ be used. [[ *Formatter* :[ *Kind* :[ *Description* -:[ *Applies to* +:< *Applies to* | . : format : How many decimals to print diff --git a/doc/yambar.5.scd b/doc/yambar.5.scd index 35c87b8..d7bf0b8 100644 --- a/doc/yambar.5.scd +++ b/doc/yambar.5.scd @@ -28,7 +28,7 @@ types that are frequently used: [[ *Name* :[ *Type* :[ *Req* -:[ *Description* +:< *Description* | height : int : yes From ef7b4ce9b38e2d6816fea55bf0d0df3ffd8ddb89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 17 Dec 2022 10:28:41 +0100 Subject: [PATCH 447/611] changelog: prepare for 1.9.0 --- CHANGELOG.md | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f15bcf..fd0b4e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -* [Unreleased](#unreleased) +* [1.9.0](#1-9-0) * [1.8.0](#1-8-0) * [1.7.0](#1-7-0) * [1.6.2](#1-6-2) @@ -9,7 +9,7 @@ * [1.5.0](#1-5-0) -## Unreleased +## 1.9.0 ### Added @@ -125,8 +125,6 @@ [207]: https://codeberg.org/dnkl/yambar/issues/207 -### Deprecated -### Removed ### Fixed * i3: fixed “missing workspace indicator” (_err: modules/i3.c:94: @@ -160,10 +158,19 @@ [229]: https://codeberg.org/dnkl/yambar/issues/229 -### Security ### Contributors +* Baptiste Daroussin * Horus +* Johannes +* Leonardo Gibrowski Faé +* Leonardo Neumann +* Midgard +* Ogromny +* Peter Rice +* Timur Celik +* Willem van de Krol +* hiog ## 1.8.0 From 1353d635c211bf563c006a35c70c3e4d5db461a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 17 Dec 2022 10:29:11 +0100 Subject: [PATCH 448/611] meson/pkgbuild: bump version to 1.9.0 --- PKGBUILD | 2 +- PKGBUILD.wayland-only | 2 +- meson.build | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/PKGBUILD b/PKGBUILD index 8f20543..256706c 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -1,5 +1,5 @@ pkgname=yambar -pkgver=1.8.0.r77.ge9a6994 +pkgver=1.9.0 pkgrel=1 pkgdesc="Simplistic and highly configurable status panel for X and Wayland" arch=('x86_64' 'aarch64') diff --git a/PKGBUILD.wayland-only b/PKGBUILD.wayland-only index cc956e9..b64ae25 100644 --- a/PKGBUILD.wayland-only +++ b/PKGBUILD.wayland-only @@ -1,5 +1,5 @@ pkgname=yambar-wayland -pkgver=1.8.0.r111.g690bd63 +pkgver=1.9.0 pkgrel=1 pkgdesc="Simplistic and highly configurable status panel for Wayland" arch=('x86_64' 'aarch64') diff --git a/meson.build b/meson.build index 3f02985..763b412 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('yambar', 'c', - version: '1.8.0', + version: '1.9.0', license: 'MIT', meson_version: '>=0.59.0', default_options: ['c_std=c18', From a9ce81b3763276df58364bb1f62e4e3131b69e2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 17 Dec 2022 10:32:36 +0100 Subject: [PATCH 449/611] =?UTF-8?q?changelog:=20add=20new=20=E2=80=98unrel?= =?UTF-8?q?eased=E2=80=99=20section?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fd0b4e6..1d6fa9e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # Changelog +* [Unreleased](#unreleased) * [1.9.0](#1-9-0) * [1.8.0](#1-8-0) * [1.7.0](#1-7-0) @@ -9,6 +10,16 @@ * [1.5.0](#1-5-0) +## Unreleased +### Added +### Changed +### Deprecated +### Removed +### Fixed +### Security +### Contributors + + ## 1.9.0 ### Added From ede6a541e1c6a861461c189513187a69e3e5ce91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 17 Dec 2022 18:15:14 +0100 Subject: [PATCH 450/611] =?UTF-8?q?modules:=20meson:=20add=20missing=20?= =?UTF-8?q?=E2=80=98m=E2=80=99=20(math)=20dependency?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The following modules used functions from the ‘m’ (math) library (e.g. round()), but didn’t explicitly link against it. This caused build failures when no other plugin that _did_ link against ‘m’ was enabled. * cpu * mem * pulse * pipewire * foreign-toplevel Closes #239 --- CHANGELOG.md | 7 +++++++ modules/meson.build | 10 +++++----- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d6fa9e..ae97a0a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,13 @@ ### Deprecated ### Removed ### Fixed + +* Build failures for certain combinations of enabled and disabled + plugins ([#239][239]). + +[239]: https://codeberg.org/dnkl/yambar/issues/239 + + ### Security ### Contributors diff --git a/modules/meson.build b/modules/meson.build index 49aba6d..f8d1e80 100644 --- a/modules/meson.build +++ b/modules/meson.build @@ -70,7 +70,7 @@ if plugin_clock_enabled endif if plugin_cpu_enabled - mod_data += {'cpu': [[], [dynlist]]} + mod_data += {'cpu': [[], [m, dynlist]]} endif if plugin_disk_io_enabled @@ -82,7 +82,7 @@ if plugin_dwl_enabled endif if plugin_mem_enabled - mod_data += {'mem': [[], []]} + mod_data += {'mem': [[], [m]]} endif if plugin_mpd_enabled @@ -102,11 +102,11 @@ if plugin_network_enabled endif if plugin_pipewire_enabled - mod_data += {'pipewire': [[], [pipewire, dynlist, json_pipewire]]} + mod_data += {'pipewire': [[], [m, pipewire, dynlist, json_pipewire]]} endif if plugin_pulse_enabled - mod_data += {'pulse': [[], [pulse]]} + mod_data += {'pulse': [[], [m, pulse]]} endif if plugin_removables_enabled @@ -170,7 +170,7 @@ if plugin_foreign_toplevel_enabled command: [wscanner_prog, 'private-code', '@INPUT@', '@OUTPUT@']) endforeach - mod_data += {'foreign-toplevel': [[wl_proto_src + wl_proto_headers + ftop_proto_headers + ftop_proto_src], [dynlist, wayland_client]]} + mod_data += {'foreign-toplevel': [[wl_proto_src + wl_proto_headers + ftop_proto_headers + ftop_proto_src], [m, dynlist, wayland_client]]} endif foreach mod, data : mod_data From 710cede371191bce857077ccd2da782f3dd91d9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 17 Dec 2022 18:26:31 +0100 Subject: [PATCH 451/611] module/pipewire: disable debug logging --- modules/pipewire.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/pipewire.c b/modules/pipewire.c index bfbb15b..b70fdff 100644 --- a/modules/pipewire.c +++ b/modules/pipewire.c @@ -12,7 +12,7 @@ #include #define LOG_MODULE "pipewire" -#define LOG_ENABLE_DBG 1 +#define LOG_ENABLE_DBG 0 #include "../config-verify.h" #include "../config.h" #include "../log.h" From 252a7a1580ea6ba7c71bac0306e60c1496ea5d5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 18 Dec 2022 10:36:01 +0100 Subject: [PATCH 452/611] =?UTF-8?q?doc:=20cpu:=20=E2=80=98interval?= =?UTF-8?q?=E2=80=99=20has=20been=20renamed=20to=20=E2=80=98poll-interval?= =?UTF-8?q?=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #241 --- CHANGELOG.md | 3 +++ doc/yambar-modules-cpu.5.scd | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ae97a0a..3c243d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,8 +19,11 @@ * Build failures for certain combinations of enabled and disabled plugins ([#239][239]). +* Documentation for the `cpu` module; `interval` has been renamed to + `poll-interval` ([#241][241]). [239]: https://codeberg.org/dnkl/yambar/issues/239 +[241]: https://codeberg.org/dnkl/yambar/issues/241 ### Security diff --git a/doc/yambar-modules-cpu.5.scd b/doc/yambar-modules-cpu.5.scd index 1b4219e..bfd9357 100644 --- a/doc/yambar-modules-cpu.5.scd +++ b/doc/yambar-modules-cpu.5.scd @@ -28,7 +28,7 @@ total CPU usage. :[ *Type* :[ *Req* :< *Description* -| interval +| poll-interval : int : no : Refresh interval of the CPU usage stats in ms (default=500). Cannot @@ -41,7 +41,7 @@ total CPU usage. bar: left: - cpu: - interval: 2500 + poll-interval: 2500 content: map: conditions: @@ -55,7 +55,7 @@ bar: bar: left: - cpu: - interval: 2500 + poll-interval: 2500 content: map: conditions: From 63c9c90a61781d307adc007a3957db62efb2049d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 18 Dec 2022 10:38:56 +0100 Subject: [PATCH 453/611] =?UTF-8?q?module/disk-io:=20rename=20=E2=80=98int?= =?UTF-8?q?erval=E2=80=99=20to=20=E2=80=98poll-interval=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 4 ++++ doc/yambar-modules-disk-io.5.scd | 4 ++-- modules/disk-io.c | 6 +++--- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c243d0..e64acd0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,10 @@ ## Unreleased ### Added ### Changed + +* disk-io: `interval` renamed to `poll-interval` + + ### Deprecated ### Removed ### Fixed diff --git a/doc/yambar-modules-disk-io.5.scd b/doc/yambar-modules-disk-io.5.scd index 4def79f..77220d9 100644 --- a/doc/yambar-modules-disk-io.5.scd +++ b/doc/yambar-modules-disk-io.5.scd @@ -35,7 +35,7 @@ currently present in the machine. :[ *Type* :[ *Req* :< *Description* -| interval +| poll-interval : int : no : Refresh interval of disk's stats in ms (default=500). @@ -50,7 +50,7 @@ formatting in b/s, kb/s, mb/s, or gb/s, as appropriate. bar: left: - disk-io: - interval: 1000 + poll-interval: 1000 content: map: conditions: diff --git a/modules/disk-io.c b/modules/disk-io.c index 4ce665f..8609c44 100644 --- a/modules/disk-io.c +++ b/modules/disk-io.c @@ -310,7 +310,7 @@ disk_io_new(uint16_t interval, struct particle *label) static struct module * from_conf(const struct yml_node *node, struct conf_inherit inherited) { - const struct yml_node *interval = yml_get_value(node, "interval"); + const struct yml_node *interval = yml_get_value(node, "poll-interval"); const struct yml_node *c = yml_get_value(node, "content"); return disk_io_new( @@ -326,7 +326,7 @@ conf_verify_interval(keychain_t *chain, const struct yml_node *node) if (yml_value_as_int(node) < SMALLEST_INTERVAL) { LOG_ERR( - "%s: interval value cannot be less than %d ms", + "%s: poll-interval value cannot be less than %d ms", conf_err_prefix(chain, node), SMALLEST_INTERVAL); return false; } @@ -338,7 +338,7 @@ static bool verify_conf(keychain_t *chain, const struct yml_node *node) { static const struct attr_info attrs[] = { - {"interval", false, &conf_verify_interval}, + {"poll-interval", false, &conf_verify_interval}, MODULE_COMMON_ATTRS, }; From cb8acf261a054cbdf727585d63798e36b4ee3bdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 18 Dec 2022 10:40:04 +0100 Subject: [PATCH 454/611] =?UTF-8?q?module/mem:=20rename=20=E2=80=98interva?= =?UTF-8?q?l=E2=80=99=20to=20=E2=80=98poll-interval=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 1 + doc/yambar-modules-mem.5.scd | 7 ++++--- modules/mem.c | 8 +++++--- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e64acd0..196202a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ ### Changed * disk-io: `interval` renamed to `poll-interval` +* mem: `interval` renamed to `poll-interval` ### Deprecated diff --git a/doc/yambar-modules-mem.5.scd b/doc/yambar-modules-mem.5.scd index 82a70c3..82710c3 100644 --- a/doc/yambar-modules-mem.5.scd +++ b/doc/yambar-modules-mem.5.scd @@ -30,10 +30,11 @@ mem - This module provides the memory usage :[ *Type* :[ *Req* :< *Description* -| interval +| poll-interval : string : no -: Refresh interval of the memory usage stats in ms (default=500). Cannot be less then 500 ms +: Refresh interval of the memory usage stats in ms + (default=500). Cannot be less then 500 ms. # EXAMPLES @@ -41,7 +42,7 @@ mem - This module provides the memory usage bar: left: - mem: - interval: 2500 + poll-interval: 2500 content: string: {text: "{used:mb}MB"} ``` diff --git a/modules/mem.c b/modules/mem.c index 339501b..417c8df 100644 --- a/modules/mem.c +++ b/modules/mem.c @@ -151,10 +151,12 @@ mem_new(uint16_t interval, struct particle *label) static struct module * from_conf(const struct yml_node *node, struct conf_inherit inherited) { - const struct yml_node *interval = yml_get_value(node, "interval"); + const struct yml_node *interval = yml_get_value(node, "poll-interval"); const struct yml_node *c = yml_get_value(node, "content"); - return mem_new(interval == NULL ? SMALLEST_INTERVAL : yml_value_as_int(interval), conf_to_particle(c, inherited)); + return mem_new( + interval == NULL ? SMALLEST_INTERVAL : yml_value_as_int(interval), + conf_to_particle(c, inherited)); } static bool @@ -175,7 +177,7 @@ static bool verify_conf(keychain_t *chain, const struct yml_node *node) { static const struct attr_info attrs[] = { - {"interval", false, &conf_verify_interval}, + {"poll-interval", false, &conf_verify_interval}, MODULE_COMMON_ATTRS, }; From 43187650304f0207805be1f447c0ff1439e00c48 Mon Sep 17 00:00:00 2001 From: Ben Brown Date: Mon, 19 Dec 2022 17:56:25 +0000 Subject: [PATCH 455/611] doc: Reinclude yambar-modules man page --- doc/meson.build | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/meson.build b/doc/meson.build index fa9673d..0b4e13a 100644 --- a/doc/meson.build +++ b/doc/meson.build @@ -68,6 +68,7 @@ endif foreach man_src : ['yambar.1.scd', 'yambar.5.scd', 'yambar-decorations.5.scd', + 'yambar-modules.5.scd', 'yambar-particles.5.scd', 'yambar-tags.5.scd'] + plugin_pages parts = man_src.split('.') From c0e0702a6c2ac0eb949b5ec65362c40217b28aef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stanislav=20Ochotnick=C3=BD?= Date: Wed, 21 Dec 2022 17:06:25 +0100 Subject: [PATCH 456/611] Update Font Awesome 5 to 6 This makes it less likely that new users will get confused and accidentally use fallback fonts. --- README.md | 4 ++-- doc/yambar-modules.5.scd | 4 ++-- doc/yambar.5.scd | 4 ++-- examples/configurations/laptop.conf | 4 ++-- particles/string.c | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index c7b0505..759f667 100644 --- a/README.md +++ b/README.md @@ -59,9 +59,9 @@ bar: right: - clock: content: - - string: {text: , font: "Font Awesome 5 Free:style=solid:size=12"} + - string: {text: , font: "Font Awesome 6 Free:style=solid:size=12"} - string: {text: "{date}", right-margin: 5} - - string: {text: , font: "Font Awesome 5 Free:style=solid:size=12"} + - string: {text: , font: "Font Awesome 6 Free:style=solid:size=12"} - string: {text: "{time}"} ``` diff --git a/doc/yambar-modules.5.scd b/doc/yambar-modules.5.scd index 177418f..e8c388c 100644 --- a/doc/yambar-modules.5.scd +++ b/doc/yambar-modules.5.scd @@ -38,7 +38,7 @@ For example, to render _backlight_ as " 20%", you could use: ``` content: - string: - font: Font Awesome 5 Free:style=solid:pixelsize=14 + font: Font Awesome 6 Free:style=solid:pixelsize=14 text:  - string: font: Adobe Helvetica:pixelsize=12 @@ -91,7 +91,7 @@ In these cases, you can define an anchor point, either at top-level, or in a module's _anchors_ attribute: ``` -awesome: &awesome Font Awesome 5 Free:style=solid:pixelsize=14 +awesome: &awesome Font Awesome 6 Free:style=solid:pixelsize=14 ``` diff --git a/doc/yambar.5.scd b/doc/yambar.5.scd index d7bf0b8..3cc9557 100644 --- a/doc/yambar.5.scd +++ b/doc/yambar.5.scd @@ -14,8 +14,8 @@ types that are frequently used: - *font*: this is a comma separated list of fonts in _fontconfig_ format. Example of valid values: - - Font Awesome 5 Brands - - Font Awesome 5 Free:style=solid + - Font Awesome 6 Brands + - Font Awesome 6 Free:style=solid - Dina:pixelsize=10:slant=italic - Dina:pixelsize=10:weight=bold - *color*: an rgba hexstring; _RRGGBBAA_. Examples: diff --git a/examples/configurations/laptop.conf b/examples/configurations/laptop.conf index 96383ca..de04ed7 100644 --- a/examples/configurations/laptop.conf +++ b/examples/configurations/laptop.conf @@ -5,8 +5,8 @@ # the sway-xkb module with the xkb module. # fonts we'll be re-using here and there -awesome: &awesome Font Awesome 5 Free:style=solid:pixelsize=14 -awesome_brands: &awesome_brands Font Awesome 5 Brands:pixelsize=16 +awesome: &awesome Font Awesome 6 Free:style=solid:pixelsize=14 +awesome_brands: &awesome_brands Font Awesome 6 Brands:pixelsize=16 std_underline: &std_underline {underline: { size: 2, color: ff0000ff}} diff --git a/particles/string.c b/particles/string.c index e679600..e11ec45 100644 --- a/particles/string.c +++ b/particles/string.c @@ -94,7 +94,7 @@ expose(const struct exposable *exposable, pixman_image_t *pix, int x, int y, int * * Finally, if the font's descent is negative, ignore it (except * for the height calculation). This is unfortunately not based on - * any real facts, but works very well with e.g. the "Awesome 5" + * any real facts, but works very well with e.g. the "Awesome 6" * font family. */ const double baseline = (double)y + From 1f25978eb4bcd36a75c0d0e03f427c7b4bf9ba24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 22 Dec 2022 11:46:00 +0100 Subject: [PATCH 457/611] module/cpu: cleanup poll-interval MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * man page: spell out ‘milliseconds’ * use a ‘static const’ variable for min_poll_interval, instead of a macro --- doc/yambar-modules-cpu.5.scd | 4 ++-- modules/cpu.c | 15 ++++++++------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/doc/yambar-modules-cpu.5.scd b/doc/yambar-modules-cpu.5.scd index bfd9357..400b2dd 100644 --- a/doc/yambar-modules-cpu.5.scd +++ b/doc/yambar-modules-cpu.5.scd @@ -31,8 +31,8 @@ total CPU usage. | poll-interval : int : no -: Refresh interval of the CPU usage stats in ms (default=500). Cannot - be less then 500 ms +: Refresh interval of the CPU usage stats in milliseconds + (default=500). Cannot be less then 500ms. # EXAMPLES diff --git a/modules/cpu.c b/modules/cpu.c index b63e926..a18cc9b 100644 --- a/modules/cpu.c +++ b/modules/cpu.c @@ -13,7 +13,6 @@ #define LOG_MODULE "cpu" #define LOG_ENABLE_DBG 0 -#define SMALLEST_INTERVAL 500 #include "../log.h" #include "../bar/bar.h" @@ -22,6 +21,8 @@ #include "../particles/dynlist.h" #include "../plugin.h" +static const long min_poll_interval = 500; + struct cpu_stats { uint32_t *prev_cores_idle; uint32_t *prev_cores_nidle; @@ -276,19 +277,19 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) const struct yml_node *c = yml_get_value(node, "content"); return cpu_new( - interval == NULL ? SMALLEST_INTERVAL : yml_value_as_int(interval), + interval == NULL ? min_poll_interval : yml_value_as_int(interval), conf_to_particle(c, inherited)); } static bool -conf_verify_interval(keychain_t *chain, const struct yml_node *node) +conf_verify_poll_interval(keychain_t *chain, const struct yml_node *node) { if (!conf_verify_unsigned(chain, node)) return false; - if (yml_value_as_int(node) < SMALLEST_INTERVAL) { - LOG_ERR("%s: interval value cannot be less than %d ms", - conf_err_prefix(chain, node), SMALLEST_INTERVAL); + if (yml_value_as_int(node) < min_poll_interval) { + LOG_ERR("%s: interval value cannot be less than %ldms", + conf_err_prefix(chain, node), min_poll_interval); return false; } @@ -299,7 +300,7 @@ static bool verify_conf(keychain_t *chain, const struct yml_node *node) { static const struct attr_info attrs[] = { - {"poll-interval", false, &conf_verify_interval}, + {"poll-interval", false, &conf_verify_poll_interval}, MODULE_COMMON_ATTRS, }; From a18296a179ce4987d1d7edd9538f9ff24040fd95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 22 Dec 2022 11:47:08 +0100 Subject: [PATCH 458/611] module/disk-io: cleanup poll-interval MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * man page: spell out ‘milliseconds’ * use a ‘static const’ variable for min_poll_interval, instead of a macro --- doc/yambar-modules-disk-io.5.scd | 4 ++-- modules/disk-io.c | 23 ++++++++++++----------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/doc/yambar-modules-disk-io.5.scd b/doc/yambar-modules-disk-io.5.scd index 77220d9..6f6c7dc 100644 --- a/doc/yambar-modules-disk-io.5.scd +++ b/doc/yambar-modules-disk-io.5.scd @@ -38,8 +38,8 @@ currently present in the machine. | poll-interval : int : no -: Refresh interval of disk's stats in ms (default=500). - Cannot be less then 500 ms +: Refresh interval of disk's stats in milliseconds (default=500). + Cannot be less then 500ms. # EXAMPLES diff --git a/modules/disk-io.c b/modules/disk-io.c index 8609c44..2142b56 100644 --- a/modules/disk-io.c +++ b/modules/disk-io.c @@ -7,16 +7,17 @@ #include -#include "../particles/dynlist.h" +#define LOG_MODULE "disk-io" +#define LOG_ENABLE_DBG 0 +#include "../log.h" + #include "../bar/bar.h" #include "../config-verify.h" #include "../config.h" -#include "../log.h" +#include "../particles/dynlist.h" #include "../plugin.h" -#define LOG_MODULE "disk-io" -#define LOG_ENABLE_DBG 0 -#define SMALLEST_INTERVAL 500 +static const long min_poll_interval = 500; struct device_stats { char *name; @@ -314,20 +315,20 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) const struct yml_node *c = yml_get_value(node, "content"); return disk_io_new( - interval == NULL ? SMALLEST_INTERVAL : yml_value_as_int(interval), + interval == NULL ? min_poll_interval : yml_value_as_int(interval), conf_to_particle(c, inherited)); } static bool -conf_verify_interval(keychain_t *chain, const struct yml_node *node) +conf_verify_poll_interval(keychain_t *chain, const struct yml_node *node) { if (!conf_verify_unsigned(chain, node)) return false; - if (yml_value_as_int(node) < SMALLEST_INTERVAL) { + if (yml_value_as_int(node) < min_poll_interval) { LOG_ERR( - "%s: poll-interval value cannot be less than %d ms", - conf_err_prefix(chain, node), SMALLEST_INTERVAL); + "%s: poll-interval value cannot be less than %ldms", + conf_err_prefix(chain, node), min_poll_interval); return false; } @@ -338,7 +339,7 @@ static bool verify_conf(keychain_t *chain, const struct yml_node *node) { static const struct attr_info attrs[] = { - {"poll-interval", false, &conf_verify_interval}, + {"poll-interval", false, &conf_verify_poll_interval}, MODULE_COMMON_ATTRS, }; From ac8e45c3312690ff6b49dfc192c217284c569106 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 22 Dec 2022 11:47:15 +0100 Subject: [PATCH 459/611] module/mem: cleanup poll-interval MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * man page: spell out ‘milliseconds’ * use a ‘static const’ variable for min_poll_interval, instead of a macro --- doc/yambar-modules-mem.5.scd | 4 ++-- modules/mem.c | 14 ++++++++------ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/doc/yambar-modules-mem.5.scd b/doc/yambar-modules-mem.5.scd index 82710c3..7d72256 100644 --- a/doc/yambar-modules-mem.5.scd +++ b/doc/yambar-modules-mem.5.scd @@ -33,8 +33,8 @@ mem - This module provides the memory usage | poll-interval : string : no -: Refresh interval of the memory usage stats in ms - (default=500). Cannot be less then 500 ms. +: Refresh interval of the memory usage stats in milliseconds + (default=500). Cannot be less then 500ms. # EXAMPLES diff --git a/modules/mem.c b/modules/mem.c index 417c8df..54f7f83 100644 --- a/modules/mem.c +++ b/modules/mem.c @@ -12,13 +12,14 @@ #define LOG_MODULE "mem" #define LOG_ENABLE_DBG 0 -#define SMALLEST_INTERVAL 500 #include "../bar/bar.h" #include "../config-verify.h" #include "../config.h" #include "../log.h" #include "../plugin.h" +static const long min_poll_interval = 500; + struct private { struct particle *label; @@ -155,18 +156,19 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) const struct yml_node *c = yml_get_value(node, "content"); return mem_new( - interval == NULL ? SMALLEST_INTERVAL : yml_value_as_int(interval), + interval == NULL ? min_poll_interval : yml_value_as_int(interval), conf_to_particle(c, inherited)); } static bool -conf_verify_interval(keychain_t *chain, const struct yml_node *node) +conf_verify_poll_interval(keychain_t *chain, const struct yml_node *node) { if (!conf_verify_unsigned(chain, node)) return false; - if (yml_value_as_int(node) < SMALLEST_INTERVAL) { - LOG_ERR("%s: interval value cannot be less than %d ms", conf_err_prefix(chain, node), SMALLEST_INTERVAL); + if (yml_value_as_int(node) < min_poll_interval) { + LOG_ERR("%s: interval value cannot be less than %ldms", + conf_err_prefix(chain, node), min_poll_interval); return false; } @@ -177,7 +179,7 @@ static bool verify_conf(keychain_t *chain, const struct yml_node *node) { static const struct attr_info attrs[] = { - {"poll-interval", false, &conf_verify_interval}, + {"poll-interval", false, &conf_verify_poll_interval}, MODULE_COMMON_ATTRS, }; From 8fbbce10a5e555adfe61ebc83b20437c52e3b0ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 22 Dec 2022 11:47:50 +0100 Subject: [PATCH 460/611] =?UTF-8?q?module/battery:=20poll-interval:=20conv?= =?UTF-8?q?ert=20value=20from=20=E2=80=98seconds=E2=80=99=20to=20=E2=80=98?= =?UTF-8?q?milliseconds=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/yambar-modules-battery.5.scd | 7 +++++-- examples/configurations/laptop.conf | 2 +- modules/battery.c | 32 +++++++++++++++++++++++------ 3 files changed, 32 insertions(+), 9 deletions(-) diff --git a/doc/yambar-modules-battery.5.scd b/doc/yambar-modules-battery.5.scd index 101c02b..045b3fa 100644 --- a/doc/yambar-modules-battery.5.scd +++ b/doc/yambar-modules-battery.5.scd @@ -57,7 +57,10 @@ the state *unknown* under other conditions. | poll-interval : int : no -: How often, in seconds, to poll for capacity changes (default=*60*). Set to `0` to disable polling (*warning*: many batteries do not support asynchronous reporting). +: How often, in milliseconds, to poll for capacity changes + (default=*60000*). Set to `0` to disable polling (*warning*: many + batteries do not support asynchronous reporting). Cannot be less + than 500ms. # EXAMPLES @@ -66,7 +69,7 @@ bar: left: - battery: name: BAT0 - poll-interval: 30 + poll-interval: 30000 content: string: {text: "BAT: {capacity}% {estimate}"} ``` diff --git a/examples/configurations/laptop.conf b/examples/configurations/laptop.conf index de04ed7..dd7fbcb 100644 --- a/examples/configurations/laptop.conf +++ b/examples/configurations/laptop.conf @@ -221,7 +221,7 @@ bar: content: [ string: {text: , font: *awesome}, string: {text: "{percent}%"}] - battery: name: BAT0 - poll-interval: 30 + poll-interval: 30000 anchors: discharging: &discharging list: diff --git a/modules/battery.c b/modules/battery.c index c4f949c..7d31aa3 100644 --- a/modules/battery.c +++ b/modules/battery.c @@ -20,12 +20,15 @@ #include "../config-verify.h" #include "../plugin.h" +static const long min_poll_interval = 500; +static const long default_poll_interval = 60 * 1000; + enum state { STATE_FULL, STATE_NOTCHARGING, STATE_CHARGING, STATE_DISCHARGING, STATE_UNKNOWN }; struct private { struct particle *label; - int poll_interval; + long poll_interval; char *battery; char *manufacturer; char *model; @@ -429,7 +432,7 @@ run(struct module *mod) {.fd = udev_monitor_get_fd(mon), .events = POLLIN}, }; if (poll(fds, sizeof(fds) / sizeof(fds[0]), - m->poll_interval > 0 ? m->poll_interval * 1000 : -1) < 0) + m->poll_interval > 0 ? m->poll_interval : -1) < 0) { if (errno == EINTR) continue; @@ -469,11 +472,11 @@ out: } static struct module * -battery_new(const char *battery, struct particle *label, int poll_interval_secs) +battery_new(const char *battery, struct particle *label, long poll_interval_msecs) { struct private *m = calloc(1, sizeof(*m)); m->label = label; - m->poll_interval = poll_interval_secs; + m->poll_interval = poll_interval_msecs; m->battery = strdup(battery); m->state = STATE_UNKNOWN; @@ -496,7 +499,24 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) return battery_new( yml_value_as_string(name), conf_to_particle(c, inherited), - poll_interval != NULL ? yml_value_as_int(poll_interval) : 60); + (poll_interval != NULL + ? yml_value_as_int(poll_interval) + : default_poll_interval)); +} + +static bool +conf_verify_poll_interval(keychain_t *chain, const struct yml_node *node) +{ + if (!conf_verify_unsigned(chain, node)) + return false; + + if (yml_value_as_int(node) < min_poll_interval) { + LOG_ERR("%s: interval value cannot be less than %ldms", + conf_err_prefix(chain, node), min_poll_interval); + return false; + } + + return true; } static bool @@ -504,7 +524,7 @@ verify_conf(keychain_t *chain, const struct yml_node *node) { static const struct attr_info attrs[] = { {"name", true, &conf_verify_string}, - {"poll-interval", false, &conf_verify_unsigned}, + {"poll-interval", false, &conf_verify_poll_interval}, MODULE_COMMON_ATTRS, }; From 500b051fe4b3ed34445325f1caf0f347edd73dbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 22 Dec 2022 11:59:08 +0100 Subject: [PATCH 461/611] =?UTF-8?q?module/network:=20poll-interval:=20conv?= =?UTF-8?q?ert=20value=20from=20=E2=80=98seconds=E2=80=99=20to=20=E2=80=98?= =?UTF-8?q?milliseconds=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/yambar-modules-network.5.scd | 5 +-- examples/configurations/laptop.conf | 2 +- modules/network.c | 47 +++++++++++++++++++++-------- 3 files changed, 38 insertions(+), 16 deletions(-) diff --git a/doc/yambar-modules-network.5.scd b/doc/yambar-modules-network.5.scd index 7bb9afc..a07a87f 100644 --- a/doc/yambar-modules-network.5.scd +++ b/doc/yambar-modules-network.5.scd @@ -72,8 +72,9 @@ address. | poll-interval : int : no -: Periodically (in seconds) update the signal, rx+tx bitrate, and - ul+dl speed tags. +: Periodically (in milliseconds) update the signal, rx+tx bitrate, and + ul+dl speed tags. Setting it to 0 disables updates. Cannot be less + than 500ms. # EXAMPLES diff --git a/examples/configurations/laptop.conf b/examples/configurations/laptop.conf index dd7fbcb..87f3d1c 100644 --- a/examples/configurations/laptop.conf +++ b/examples/configurations/laptop.conf @@ -181,7 +181,7 @@ bar: state == up && ipv4 != "": {string: {text: , font: *awesome}} - network: name: wlp2s0 - poll-interval: 1 + poll-interval: 1000 content: map: default: {string: {text: , font: *awesome, foreground: ffffff66}} diff --git a/modules/network.c b/modules/network.c index 17358a6..423f1b9 100644 --- a/modules/network.c +++ b/modules/network.c @@ -34,6 +34,8 @@ #define UNUSED __attribute__((unused)) +static const long min_poll_interval = 500; + struct rt_stats_msg { struct rtmsg rth; struct rtnl_link_stats64 stats; @@ -79,10 +81,10 @@ struct private { uint32_t rx_bitrate; uint32_t tx_bitrate; - uint64_t ul_speed; + double ul_speed; uint64_t ul_bits; - uint64_t dl_speed; + double dl_speed; uint64_t dl_bits; }; @@ -1120,15 +1122,16 @@ static void handle_stats(struct module *mod, struct rt_stats_msg *msg) { struct private *m = mod->private; - uint64_t ul_bits = msg->stats.tx_bytes*8; - uint64_t dl_bits = msg->stats.rx_bytes*8; + uint64_t ul_bits = msg->stats.tx_bytes * 8; + uint64_t dl_bits = msg->stats.rx_bytes * 8; + + const double poll_interval_secs = (double)m->poll_interval / 1000.; + + if (m->ul_bits != 0) + m->ul_speed = (double)(ul_bits - m->ul_bits) / poll_interval_secs; + if (m->dl_bits != 0) + m->dl_speed = (double)(dl_bits - m->dl_bits) / poll_interval_secs; - if (m->ul_bits != 0) { - m->ul_speed = (ul_bits - m->ul_bits) / m->poll_interval; - } - if (m->dl_bits != 0) { - m->dl_speed = (dl_bits - m->dl_bits) / m->poll_interval; - } m->ul_bits = ul_bits; m->dl_bits = dl_bits; } @@ -1336,9 +1339,12 @@ run(struct module *mod) goto out; } + const long secs = m->poll_interval / 1000; + const long msecs = m->poll_interval % 1000; + struct itimerspec poll_time = { - .it_value = {.tv_sec = m->poll_interval}, - .it_interval = {.tv_sec = m->poll_interval}, + .it_value = {.tv_sec = secs, .tv_nsec = msecs * 1000000}, + .it_interval = {.tv_sec = secs, .tv_nsec = msecs * 1000000}, }; if (timerfd_settime(timer_fd, 0, &poll_time, NULL) < 0) { @@ -1485,12 +1491,27 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) poll != NULL ? yml_value_as_int(poll) : 0); } +static bool +conf_verify_poll_interval(keychain_t *chain, const struct yml_node *node) +{ + if (!conf_verify_unsigned(chain, node)) + return false; + + if (yml_value_as_int(node) < min_poll_interval) { + LOG_ERR("%s: interval value cannot be less than %ldms", + conf_err_prefix(chain, node), min_poll_interval); + return false; + } + + return true; +} + static bool verify_conf(keychain_t *chain, const struct yml_node *node) { static const struct attr_info attrs[] = { {"name", true, &conf_verify_string}, - {"poll-interval", false, &conf_verify_unsigned}, + {"poll-interval", false, &conf_verify_poll_interval}, MODULE_COMMON_ATTRS, }; From c4f820e486318dfebade944a77f3c554cf7d3f1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 22 Dec 2022 12:06:25 +0100 Subject: [PATCH 462/611] =?UTF-8?q?module/script:=20poll-interval:=20conve?= =?UTF-8?q?rt=20value=20from=20=E2=80=98seconds=E2=80=99=20to=20=E2=80=98m?= =?UTF-8?q?illiseconds=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/yambar-modules-script.5.scd | 4 ++-- modules/script.c | 28 ++++++++++++++++++++++++---- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/doc/yambar-modules-script.5.scd b/doc/yambar-modules-script.5.scd index 17ac34e..ec91c7e 100644 --- a/doc/yambar-modules-script.5.scd +++ b/doc/yambar-modules-script.5.scd @@ -78,8 +78,8 @@ User defined. | poll-interval : integer : no -: Number of seconds between each script run. If unset, continuous mode - is used. +: Number of milliseconds between each script run. If unset, or set to + 0, continuous mode is used. # EXAMPLES diff --git a/modules/script.c b/modules/script.c index 1c349a1..2b97590 100644 --- a/modules/script.c +++ b/modules/script.c @@ -22,6 +22,8 @@ #include "../module.h" #include "../plugin.h" +static const long min_poll_interval = 500; + struct private { char *path; size_t argc; @@ -574,7 +576,7 @@ run(struct module *mod) break; if (m->aborted) break; - if (m->poll_interval < 0) + if (m->poll_interval <= 0) break; struct timeval now; @@ -583,7 +585,10 @@ run(struct module *mod) break; } - struct timeval poll_interval = {.tv_sec = m->poll_interval}; + struct timeval poll_interval = { + .tv_sec = m->poll_interval / 1000, + .tv_usec = (m->poll_interval % 1000) * 1000, + }; struct timeval timeout; timeradd(&now, &poll_interval, &timeout); @@ -670,7 +675,7 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) return script_new( yml_value_as_string(path), argc, argv, - poll_interval != NULL ? yml_value_as_int(poll_interval) : -1, + poll_interval != NULL ? yml_value_as_int(poll_interval) : 0, conf_to_particle(c, inherited)); } @@ -695,13 +700,28 @@ conf_verify_args(keychain_t *chain, const struct yml_node *node) return conf_verify_list(chain, node, &conf_verify_string); } +static bool +conf_verify_poll_interval(keychain_t *chain, const struct yml_node *node) +{ + if (!conf_verify_unsigned(chain, node)) + return false; + + if (yml_value_as_int(node) < min_poll_interval) { + LOG_ERR("%s: interval value cannot be less than %ldms", + conf_err_prefix(chain, node), min_poll_interval); + return false; + } + + return true; +} + static bool verify_conf(keychain_t *chain, const struct yml_node *node) { static const struct attr_info attrs[] = { {"path", true, &conf_verify_path}, {"args", false, &conf_verify_args}, - {"poll-interval", false, &conf_verify_unsigned}, + {"poll-interval", false, &conf_verify_poll_interval}, MODULE_COMMON_ATTRS, }; From d26d3953f16f01d751ca2fc90fc1b398bbb118a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 22 Dec 2022 12:09:28 +0100 Subject: [PATCH 463/611] changelog: batter/network/script: poll-interval: seconds -> milliseconds --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 196202a..bc6f849 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,10 @@ * disk-io: `interval` renamed to `poll-interval` * mem: `interval` renamed to `poll-interval` +* battery/network/script: `poll-interval` unit changed from seconds to + milliseconds ([#244][244]). + +[244]: https://codeberg.org/dnkl/yambar/issues/244 ### Deprecated From 310c07b03dad62cef7e079463881672de69508ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 26 Dec 2022 19:54:32 +0100 Subject: [PATCH 464/611] =?UTF-8?q?module/battery:=20using=20a=20static=20?= =?UTF-8?q?buffer=20in=20readline=5Ffrom=5Ffd()=20isn=E2=80=99t=20thread?= =?UTF-8?q?=20safe?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 1 + modules/battery.c | 24 +++++++++++++----------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bc6f849..0590b13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ plugins ([#239][239]). * Documentation for the `cpu` module; `interval` has been renamed to `poll-interval` ([#241][241]). +* battery: module was not thread safe. [239]: https://codeberg.org/dnkl/yambar/issues/239 [241]: https://codeberg.org/dnkl/yambar/issues/241 diff --git a/modules/battery.c b/modules/battery.c index 7d31aa3..87c0283 100644 --- a/modules/battery.c +++ b/modules/battery.c @@ -150,20 +150,18 @@ content(struct module *mod) } static const char * -readline_from_fd(int fd) +readline_from_fd(int fd, size_t sz, char buf[static sz]) { - static char buf[4096]; - - ssize_t sz = read(fd, buf, sizeof(buf) - 1); + ssize_t bytes = read(fd, buf, sz - 1); lseek(fd, 0, SEEK_SET); - if (sz < 0) { + if (bytes < 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[bytes] = '\0'; + for (ssize_t i = bytes - 1; i >= 0 && buf[i] == '\n'; bytes--) buf[i] = '\0'; return buf; @@ -172,7 +170,8 @@ readline_from_fd(int fd) static long readint_from_fd(int fd) { - const char *s = readline_from_fd(fd); + char buf[512]; + const char *s = readline_from_fd(fd, sizeof(buf), buf); if (s == NULL) return 0; @@ -189,6 +188,8 @@ readint_from_fd(int fd) static bool initialize(struct private *m) { + char line_buf[512]; + int pw_fd = open("/sys/class/power_supply", O_RDONLY); if (pw_fd < 0) { LOG_ERRNO("/sys/class/power_supply"); @@ -210,7 +211,7 @@ initialize(struct private *m) m->battery, strerror(errno)); m->manufacturer = NULL; } else { - m->manufacturer = strdup(readline_from_fd(fd)); + m->manufacturer = strdup(readline_from_fd(fd, sizeof(line_buf), line_buf)); close(fd); } } @@ -222,7 +223,7 @@ initialize(struct private *m) m->battery, strerror(errno)); m->model = NULL; } else { - m->model = strdup(readline_from_fd(fd)); + m->model = strdup(readline_from_fd(fd, sizeof(line_buf), line_buf)); close(fd); } } @@ -338,7 +339,8 @@ update_status(struct module *mod) long current = current_fd >= 0 ? readint_from_fd(current_fd) : -1; long time_to_empty = time_to_empty_fd >= 0 ? readint_from_fd(time_to_empty_fd) : -1; - const char *status = readline_from_fd(status_fd); + char buf[512]; + const char *status = readline_from_fd(status_fd, sizeof(buf), buf); if (status_fd >= 0) close(status_fd); From e4edbd26c6c15fd55864704316ea616267696b6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 23 Dec 2022 11:10:37 +0100 Subject: [PATCH 465/611] modules: change min poll interval from 500ms to 250ms --- CHANGELOG.md | 1 + doc/yambar-modules-battery.5.scd | 2 +- doc/yambar-modules-cpu.5.scd | 2 +- doc/yambar-modules-disk-io.5.scd | 2 +- doc/yambar-modules-mem.5.scd | 2 +- doc/yambar-modules-network.5.scd | 2 +- modules/battery.c | 2 +- modules/cpu.c | 2 +- modules/disk-io.c | 2 +- modules/mem.c | 2 +- modules/network.c | 2 +- modules/script.c | 2 +- 12 files changed, 12 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0590b13..a99a9c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ * mem: `interval` renamed to `poll-interval` * battery/network/script: `poll-interval` unit changed from seconds to milliseconds ([#244][244]). +* all modules: minimum poll interval changed from 500ms to 250ms. [244]: https://codeberg.org/dnkl/yambar/issues/244 diff --git a/doc/yambar-modules-battery.5.scd b/doc/yambar-modules-battery.5.scd index 045b3fa..ef7a6bd 100644 --- a/doc/yambar-modules-battery.5.scd +++ b/doc/yambar-modules-battery.5.scd @@ -60,7 +60,7 @@ the state *unknown* under other conditions. : How often, in milliseconds, to poll for capacity changes (default=*60000*). Set to `0` to disable polling (*warning*: many batteries do not support asynchronous reporting). Cannot be less - than 500ms. + than 250ms. # EXAMPLES diff --git a/doc/yambar-modules-cpu.5.scd b/doc/yambar-modules-cpu.5.scd index 400b2dd..090ccdd 100644 --- a/doc/yambar-modules-cpu.5.scd +++ b/doc/yambar-modules-cpu.5.scd @@ -32,7 +32,7 @@ total CPU usage. : int : no : Refresh interval of the CPU usage stats in milliseconds - (default=500). Cannot be less then 500ms. + (default=500). Cannot be less then 250ms. # EXAMPLES diff --git a/doc/yambar-modules-disk-io.5.scd b/doc/yambar-modules-disk-io.5.scd index 6f6c7dc..5203316 100644 --- a/doc/yambar-modules-disk-io.5.scd +++ b/doc/yambar-modules-disk-io.5.scd @@ -39,7 +39,7 @@ currently present in the machine. : int : no : Refresh interval of disk's stats in milliseconds (default=500). - Cannot be less then 500ms. + Cannot be less then 250ms. # EXAMPLES diff --git a/doc/yambar-modules-mem.5.scd b/doc/yambar-modules-mem.5.scd index 7d72256..fc0a9eb 100644 --- a/doc/yambar-modules-mem.5.scd +++ b/doc/yambar-modules-mem.5.scd @@ -34,7 +34,7 @@ mem - This module provides the memory usage : string : no : Refresh interval of the memory usage stats in milliseconds - (default=500). Cannot be less then 500ms. + (default=500). Cannot be less then 250ms. # EXAMPLES diff --git a/doc/yambar-modules-network.5.scd b/doc/yambar-modules-network.5.scd index a07a87f..8b8b507 100644 --- a/doc/yambar-modules-network.5.scd +++ b/doc/yambar-modules-network.5.scd @@ -74,7 +74,7 @@ address. : no : Periodically (in milliseconds) update the signal, rx+tx bitrate, and ul+dl speed tags. Setting it to 0 disables updates. Cannot be less - than 500ms. + than 250ms. # EXAMPLES diff --git a/modules/battery.c b/modules/battery.c index 87c0283..db00add 100644 --- a/modules/battery.c +++ b/modules/battery.c @@ -20,7 +20,7 @@ #include "../config-verify.h" #include "../plugin.h" -static const long min_poll_interval = 500; +static const long min_poll_interval = 250; static const long default_poll_interval = 60 * 1000; enum state { STATE_FULL, STATE_NOTCHARGING, STATE_CHARGING, STATE_DISCHARGING, STATE_UNKNOWN }; diff --git a/modules/cpu.c b/modules/cpu.c index a18cc9b..455d45d 100644 --- a/modules/cpu.c +++ b/modules/cpu.c @@ -21,7 +21,7 @@ #include "../particles/dynlist.h" #include "../plugin.h" -static const long min_poll_interval = 500; +static const long min_poll_interval = 250; struct cpu_stats { uint32_t *prev_cores_idle; diff --git a/modules/disk-io.c b/modules/disk-io.c index 2142b56..7bf48e8 100644 --- a/modules/disk-io.c +++ b/modules/disk-io.c @@ -17,7 +17,7 @@ #include "../particles/dynlist.h" #include "../plugin.h" -static const long min_poll_interval = 500; +static const long min_poll_interval = 250; struct device_stats { char *name; diff --git a/modules/mem.c b/modules/mem.c index 54f7f83..3a2eda7 100644 --- a/modules/mem.c +++ b/modules/mem.c @@ -18,7 +18,7 @@ #include "../log.h" #include "../plugin.h" -static const long min_poll_interval = 500; +static const long min_poll_interval = 250; struct private { diff --git a/modules/network.c b/modules/network.c index 423f1b9..6773fcf 100644 --- a/modules/network.c +++ b/modules/network.c @@ -34,7 +34,7 @@ #define UNUSED __attribute__((unused)) -static const long min_poll_interval = 500; +static const long min_poll_interval = 250; struct rt_stats_msg { struct rtmsg rth; diff --git a/modules/script.c b/modules/script.c index 2b97590..f0cc9a7 100644 --- a/modules/script.c +++ b/modules/script.c @@ -22,7 +22,7 @@ #include "../module.h" #include "../plugin.h" -static const long min_poll_interval = 500; +static const long min_poll_interval = 250; struct private { char *path; From 02d281df58032c29e13f508d5241f68d1a29c845 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Hern=C3=A1ndez=20Hern=C3=A1ndez?= Date: Sat, 24 Dec 2022 13:31:17 -0600 Subject: [PATCH 466/611] module/dwl: correctly handle the title Uupdate the title when process a new batch of info instead of call strtok_r and looping until `string` is NULL. This fixes an issue where only the last part of the title was kept. --- CHANGELOG.md | 2 ++ modules/dwl.c | 13 +++++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a99a9c1..4420f7b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,9 +32,11 @@ * Documentation for the `cpu` module; `interval` has been renamed to `poll-interval` ([#241][241]). * battery: module was not thread safe. +* dwl module reporting only the last part of the title ([#251][251]) [239]: https://codeberg.org/dnkl/yambar/issues/239 [241]: https://codeberg.org/dnkl/yambar/issues/241 +[251]: https://codeberg.org/dnkl/yambar/pulls/251 ### Security diff --git a/modules/dwl.c b/modules/dwl.c index 00b4cb8..5fa1023 100644 --- a/modules/dwl.c +++ b/modules/dwl.c @@ -168,9 +168,15 @@ process_line(char *line, struct module *module) } /* action */ else if (index == 2) { - if (strcmp(string, "title") == 0) + if (strcmp(string, "title") == 0) { line_mode = LINE_MODE_TITLE; - else if (strcmp(string, "fullscreen") == 0) + /* Update the title here, to avoid allocate and free memory on + * every iteration (the line is separated by spaces, then we + * join it again) a bit suboptimal, isn't it?) */ + free(private->title); + private->title = strdup(save_pointer); + break; + } else if (strcmp(string, "fullscreen") == 0) line_mode = LINE_MODE_FULLSCREEN; else if (strcmp(string, "floating") == 0) line_mode = LINE_MODE_FLOATING; @@ -222,8 +228,7 @@ process_line(char *line, struct module *module) } else switch (line_mode) { case LINE_MODE_TITLE: - free(private->title); - private->title = strdup(string); + assert(false); /* unreachable */ break; case LINE_MODE_FULLSCREEN: private->fullscreen = (strcmp(string, "0") != 0); From 73ccafdadee1116d65993d2f07fdeb718be6f371 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 28 Dec 2022 15:09:25 +0100 Subject: [PATCH 467/611] module/i3: fix regression in handling of persistent workspaces MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit bbd2394601918653f8f21cad57f5694f9b3da26a added support for ‘rename’ and ‘move’ events. In order to do that, it changed how workspace events are matched against our internal workspace list: from string comparing the workspace name, to checking i3/sway’s workspace ID parameter. This introduced a regression in our handling of persistent workspaces. A persistent workspace is one that isn’t removed from the bar when it’s deleted (empty, and switched away from) by i3/sway. This concept doesn’t exist in i3/sway, but is something we’ve added. Put simple, the way we do this is be keeping the workspace in our list, even when i3/sway tells us it has been deleted. However, at this point the workspace doesn’t have an ID anymore. And the same is true at startup; when we initialize the persistent workspaces, we only have their names. Not their IDs (since the workspaces don’t actually exist yet). To this the regression, we need to: a) fallback to looking up workspaces by name. That is, if we fail to find one with a matching ID, try again using the workspace name. For _this_ to match, we also required the matched workspace to be a persistent workspace, with an ID < 0 (which essentially means the workspace doesn’t exist yet). b) reset the ID to -1 when a persistent workspace is "deleted". Closes #253 --- CHANGELOG.md | 3 +++ modules/i3.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 59 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4420f7b..6f88a09 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,10 +33,13 @@ `poll-interval` ([#241][241]). * battery: module was not thread safe. * dwl module reporting only the last part of the title ([#251][251]) +* i3/sway: regression; persistent workspaces shown twice + ([#253][253]). [239]: https://codeberg.org/dnkl/yambar/issues/239 [241]: https://codeberg.org/dnkl/yambar/issues/241 [251]: https://codeberg.org/dnkl/yambar/pulls/251 +[253]: https://codeberg.org/dnkl/yambar/issues/253 ### Security diff --git a/modules/i3.c b/modules/i3.c index f0aa137..96e215a 100644 --- a/modules/i3.c +++ b/modules/i3.c @@ -150,12 +150,19 @@ workspace_from_json(const struct json_object *json, struct workspace *ws) } static void -workspace_free(struct workspace *ws) +workspace_free_persistent(struct workspace *ws) { - free(ws->name); ws->name = NULL; free(ws->output); ws->output = NULL; free(ws->window.title); ws->window.title = NULL; free(ws->window.application); ws->window.application = NULL; + ws->id = -1; +} + +static void +workspace_free(struct workspace *ws) +{ + workspace_free_persistent(ws); + free(ws->name); ws->name = NULL; } static void @@ -250,6 +257,17 @@ workspace_lookup(struct private *m, int id) return NULL; } +static struct workspace * +workspace_lookup_by_name(struct private *m, const char *name) +{ + tll_foreach(m->workspaces, it) { + struct workspace *ws = &it->item; + if (strcmp(ws->name, name) == 0) + return ws; + } + return NULL; +} + static bool handle_get_version_reply(int sock, int type, const struct json_object *json, void *_m) { @@ -290,6 +308,35 @@ workspace_update_or_add(struct private *m, const struct json_object *ws_json) const int id = json_object_get_int(_id); struct workspace *already_exists = workspace_lookup(m, id); + if (already_exists == NULL) { + /* + * No workspace with this ID. + * + * Try looking it up again, but this time using the name. If + * we get a match, check if it’s an empty, persistent + * workspace, and if so, use it. + * + * This is necessary, since empty, persistent workspaces don’t + * exist in the i3/Sway server, and thus we don’t _have_ an + * ID. + */ + struct json_object *_name; + if (json_object_object_get_ex(ws_json, "name", &_name)) { + const char *name = json_object_get_string(_name); + if (name != NULL) { + struct workspace *maybe_persistent = + workspace_lookup_by_name(m, name); + + if (maybe_persistent != NULL && + maybe_persistent->persistent && + maybe_persistent->id < 0) + { + already_exists = maybe_persistent; + } + } + } + } + if (already_exists != NULL) { bool persistent = already_exists->persistent; assert(persistent); @@ -381,9 +428,8 @@ handle_workspace_event(int sock, int type, const struct json_object *json, void if (!ws->persistent) workspace_del(m, current_id); else { - workspace_free(ws); + workspace_free_persistent(ws); ws->empty = true; - assert(ws->persistent); } } @@ -697,6 +743,7 @@ run(struct module *mod) } struct workspace ws = { + .id = -1, .name = strdup(name_as_string), .name_as_int = name_as_int, .persistent = true, @@ -783,6 +830,9 @@ content(struct module *mod) /* Lookup content template for workspace. Fall back to default * template if this workspace doesn't have a specific * template */ + if (ws->name == NULL) { + LOG_ERR("%d %d", ws->name_as_int, ws->id); + } template = ws_content_for_name(m, ws->name); if (template == NULL) { LOG_DBG("no ws template for %s, using default template", ws->name); @@ -794,9 +844,9 @@ content(struct module *mod) ws->visible ? ws->focused ? "focused" : "unfocused" : "invisible"; - LOG_DBG("%s: visible=%s, focused=%s, urgent=%s, empty=%s, state=%s, " + LOG_DBG("name=%s (name-as-int=%d): visible=%s, focused=%s, urgent=%s, empty=%s, state=%s, " "application=%s, title=%s, mode=%s", - ws->name, + ws->name, ws->name_as_int, ws->visible ? "yes" : "no", ws->focused ? "yes" : "no", ws->urgent ? "yes" : "no", From 146759bd962d849f6de424bdcbe74769c5551fde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Gibrowski=20Fa=C3=A9?= Date: Thu, 22 Dec 2022 16:48:27 -0300 Subject: [PATCH 468/611] implement field width tag format option This implements the possibility of specifying padding for numeric tags. Both space and zero padding is supported. --- CHANGELOG.md | 7 ++++ doc/yambar-tags.5.scd | 10 +++++ tag.c | 87 ++++++++++++++++++++++++++++++------------- 3 files changed, 79 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f88a09..381737b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,12 @@ ## Unreleased ### Added + +* field width tag format option ([#246][246]) + +[246]: https://codeberg.org/dnkl/yambar/issues/246 + + ### Changed * disk-io: `interval` renamed to `poll-interval` @@ -45,6 +51,7 @@ ### Security ### Contributors +* Leonardo Gibrowski Faé (Horus) ## 1.9.0 diff --git a/doc/yambar-tags.5.scd b/doc/yambar-tags.5.scd index 488fc77..922ea9f 100644 --- a/doc/yambar-tags.5.scd +++ b/doc/yambar-tags.5.scd @@ -60,10 +60,20 @@ be used. :[ *Kind* :[ *Description* :< *Applies to* +| [0][.] +: format +: The width reserved to the field. The leading '0' is optional and + indicates zero padding, as opposed to space padding. The trailing + '.' is also optional +: Numeric tags (integer and floats) | . : format : How many decimals to print : Float tags +| [0][.] +: format +: Combined version of the two previous formatters +: N: numeric tags, M: float tags | hex : format : Renders a tag's value in hex diff --git a/tag.c b/tag.c index 4e72c2e..c6419ac 100644 --- a/tag.c +++ b/tag.c @@ -6,6 +6,7 @@ #include #include #include +#include #define LOG_MODULE "tag" #define LOG_ENABLE_DBG 1 @@ -427,13 +428,18 @@ sbuf_append(struct sbuf *s1, const char *s2) sbuf_append_at_most(s1, s2, strlen(s2)); } -bool -is_number(const char *str) { - while (*str != '\0') { - if (!isdigit(*str)) - return false; - ++str; - } +// stores the number in "*value" on success +static bool +is_number(const char *str, int *value) +{ + errno = 0; + + char *end; + int v = strtol(str, &end, 10); + if (errno != 0 || *end != '\0') + return false; + + *value = v; return true; } @@ -519,8 +525,10 @@ tags_expand_template(const char *template, const struct tag_set *tags) VALUE_UNIT, } kind = VALUE_VALUE; + int digits = 0; int decimals = 2; - char *float_fmt_end; + bool zero_pad = false; + char *point = NULL; for (size_t i = 0; i < MAX_TAG_ARGS; i++) { if (tag_args[i] == NULL) @@ -549,8 +557,31 @@ tags_expand_template(const char *template, const struct tag_set *tags) kind = VALUE_MAX; else if (strcmp(tag_args[i], "unit") == 0) kind = VALUE_UNIT; - else if (tag_args[i][0] == '.' && is_number(tag_args[i] + 1)) - decimals = strtol(tag_args[i] + 1, &float_fmt_end, 10); + else if (is_number(tag_args[i], &digits)) // i.e.: "{tag:3}" + zero_pad = tag_args[i][0] == '0'; + else if ((point = strchr(tag_args[i], '.')) != NULL) { + *point = '\0'; + + const char *digits_str = tag_args[i]; + const char *decimals_str = point + 1; + + if (digits_str[0] != '\0') { // guards against i.e. "{tag:.3}" + if (!is_number(digits_str, &digits)) { + LOG_WARN( + "tag `%s`: invalid field width formatter. Ignoring...", + tag_name); + } + } + + if (decimals_str[0] != '\0') { // guards against i.e. "{tag:3.}" + if (!is_number(decimals_str, &decimals)) { + LOG_WARN( + "tag `%s`: invalid decimals formatter. Ignoring...", + tag_name); + } + } + zero_pad = digits_str[0] == '0'; + } else LOG_WARN("invalid tag formatter: %s", tag_args[i]); } @@ -561,8 +592,9 @@ tags_expand_template(const char *template, const struct tag_set *tags) switch (format) { case FMT_DEFAULT: { if (tag->type(tag) == TAG_TYPE_FLOAT){ + const char* fmt = zero_pad ? "%0*.*f" : "%*.*f"; char str[24]; - snprintf(str, sizeof(str), "%.*f", decimals, tag->as_float(tag)); + snprintf(str, sizeof(str), fmt, digits, decimals, tag->as_float(tag)); sbuf_append(&formatted, str); } else { sbuf_append(&formatted, tag->as_string(tag)); @@ -572,9 +604,11 @@ tags_expand_template(const char *template, const struct tag_set *tags) case FMT_HEX: case FMT_OCT: { + const char* fmt = format == FMT_HEX ? + zero_pad ? "%0*lx" : "%*lx" : + zero_pad ? "%0*lo" : "%*lo"; char str[24]; - snprintf(str, sizeof(str), format == FMT_HEX ? "%lx" : "%lo", - tag->as_int(tag)); + snprintf(str, sizeof(str), fmt, digits, tag->as_int(tag)); sbuf_append(&formatted, str); break; } @@ -584,8 +618,9 @@ tags_expand_template(const char *template, const struct tag_set *tags) const long max = tag->max(tag); const long cur = tag->as_int(tag); + const char* fmt = zero_pad ? "%0*lu" : "%*lu"; char str[4]; - snprintf(str, sizeof(str), "%lu", (cur - min) * 100 / (max - min)); + snprintf(str, sizeof(str), fmt, digits, (cur - min) * 100 / (max - min)); sbuf_append(&formatted, str); break; } @@ -606,10 +641,13 @@ tags_expand_template(const char *template, const struct tag_set *tags) 1; char str[24]; - if (tag->type(tag) == TAG_TYPE_FLOAT) - snprintf(str, sizeof(str), "%.*f", decimals, tag->as_float(tag) / (double)divider); - else - snprintf(str, sizeof(str), "%lu", tag->as_int(tag) / divider); + if (tag->type(tag) == TAG_TYPE_FLOAT) { + const char* fmt = zero_pad ? "%0*.*f" : "%*.*f"; + snprintf(str, sizeof(str), fmt, digits, decimals, tag->as_float(tag) / (double)divider); + } else { + const char* fmt = zero_pad ? "%0*lu" : "%*lu"; + snprintf(str, sizeof(str), fmt, digits, tag->as_int(tag) / divider); + } sbuf_append(&formatted, str); break; } @@ -624,13 +662,12 @@ tags_expand_template(const char *template, const struct tag_set *tags) const char *fmt; switch (format) { - case FMT_DEFAULT: fmt = "%ld"; break; - case FMT_HEX: fmt = "%lx"; break; - case FMT_OCT: fmt = "%lo"; break; - + case FMT_DEFAULT: fmt = zero_pad ? "%0*ld" : "%*ld"; break; + case FMT_HEX: fmt = zero_pad ? "%0*lx" : "%*lx"; break; + case FMT_OCT: fmt = zero_pad ? "%0*lo" : "%*lo"; break; case FMT_PERCENT: value = (value - min) * 100 / (max - min); - fmt = "%lu"; + fmt = zero_pad ? "%0*lu" : "%*lu"; break; case FMT_KBYTE: @@ -648,13 +685,13 @@ tags_expand_template(const char *template, const struct tag_set *tags) format == FMT_GIBYTE ? 1000 * 1000 * 1000 : 1; value /= divider; - fmt = "%lu"; + fmt = zero_pad ? "%0*lu" : "%*lu"; break; } } char str[24]; - snprintf(str, sizeof(str), fmt, value); + snprintf(str, sizeof(str), fmt, digits, value); sbuf_append(&formatted, str); break; } From 11bef7dd08596d11b7a449886c29feddb9cf33ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 2 Jan 2023 11:39:19 +0100 Subject: [PATCH 469/611] =?UTF-8?q?doc:=20tags:=20re-arrange=20columns=20i?= =?UTF-8?q?n=20=E2=80=98formatting=E2=80=99=20table?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Put the ‘description’ column last. Since the last column is expanded to fill the screen, and the tags’ descriptions can be fairly long, it makes sense to put the description column last. --- doc/yambar-tags.5.scd | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/doc/yambar-tags.5.scd b/doc/yambar-tags.5.scd index 922ea9f..91f7616 100644 --- a/doc/yambar-tags.5.scd +++ b/doc/yambar-tags.5.scd @@ -58,53 +58,53 @@ be used. [[ *Formatter* :[ *Kind* -:[ *Description* -:< *Applies to* +:[ *Applies to* +:< *Description* | [0][.] : format +: Numeric tags (integer and floats) : The width reserved to the field. The leading '0' is optional and indicates zero padding, as opposed to space padding. The trailing '.' is also optional -: Numeric tags (integer and floats) | . : format -: How many decimals to print : Float tags +: How many decimals to print | [0][.] : format -: Combined version of the two previous formatters : N: numeric tags, M: float tags +: Combined version of the two previous formatters | hex : format -: Renders a tag's value in hex : All tag types +: Renders a tag's value in hex | oct : format -: Renders a tag's value in octal : All tag types +: Renders a tag's value in octal | % : format -: Renders a range tag's value as a percentage value : Range tags +: Renders a range tag's value as a percentage value | kb, mb, gb : format +: All tag types : Renders a tag's value (in decimal) divided by 1000, 1000^2 or 1000^3. Note: no unit suffix is appended) -: All tag types | kib, mib, gib : format -: Same as *kb*, *mb* and *gb*, but divide by 1024^n instead of 1000^n. : All tag types +: Same as *kb*, *mb* and *gb*, but divide by 1024^n instead of 1000^n. | min : selector -: Renders a range tag's minimum value : Range tags +: Renders a range tag's minimum value | max : selector -: Renders a range tag's maximum value : Range tags +: Renders a range tag's maximum value | unit : selector -: Renders a realtime tag's unit (e.g. "s", or "ms") : Realtime tags +: Renders a realtime tag's unit (e.g. "s", or "ms") From 38a1d0b57cc690bf892f640238c772a59aa278ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 2 Jan 2023 11:51:43 +0100 Subject: [PATCH 470/611] doc: tags: add a couple of formatting examples --- doc/yambar-tags.5.scd | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/doc/yambar-tags.5.scd b/doc/yambar-tags.5.scd index 91f7616..b778154 100644 --- a/doc/yambar-tags.5.scd +++ b/doc/yambar-tags.5.scd @@ -108,3 +108,23 @@ be used. : Realtime tags : Renders a realtime tag's unit (e.g. "s", or "ms") +# EXAMPLES + +- A numeric (float or int) tag with at least 3 digits, zero-padded if + necessary: + +``` +{tag:03} +``` + +- A float tag with 2 decimals: + +``` +{tag:.2} +``` + +- A "byte count" tag in gigabytes: + +``` +{tag:gib}GB +``` From 0f3894bf63708151df4ebcd4a5ac975cf731d401 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 2 Jan 2023 12:19:17 +0100 Subject: [PATCH 471/611] tag: handle width formatter on ints when no other formatting options are used MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For example: in {cpu:3}, the ‘3’ were ignored, assuming ‘cpu’ was an int tag. --- tag.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/tag.c b/tag.c index c6419ac..b098eb7 100644 --- a/tag.c +++ b/tag.c @@ -591,14 +591,28 @@ tags_expand_template(const char *template, const struct tag_set *tags) case VALUE_VALUE: switch (format) { case FMT_DEFAULT: { - if (tag->type(tag) == TAG_TYPE_FLOAT){ + switch (tag->type(tag)) { + case TAG_TYPE_FLOAT: { const char* fmt = zero_pad ? "%0*.*f" : "%*.*f"; char str[24]; snprintf(str, sizeof(str), fmt, digits, decimals, tag->as_float(tag)); sbuf_append(&formatted, str); - } else { - sbuf_append(&formatted, tag->as_string(tag)); + break; } + + case TAG_TYPE_INT: { + const char* fmt = zero_pad ? "%0*ld" : "%*ld"; + char str[24]; + snprintf(str, sizeof(str), fmt, digits, tag->as_int(tag)); + sbuf_append(&formatted, str); + break; + } + + default: + sbuf_append(&formatted, tag->as_string(tag)); + break; + } + break; } From d09d88b60b02efd1207ac8f0b8a744b771045832 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 2 Jan 2023 13:52:39 +0100 Subject: [PATCH 472/611] ci: drop gitlab CI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We’re no longer mirroring to gitlab. --- .gitlab-ci.yml | 114 ------------------------------------------------- 1 file changed, 114 deletions(-) delete mode 100644 .gitlab-ci.yml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml deleted file mode 100644 index dd1db7b..0000000 --- a/.gitlab-ci.yml +++ /dev/null @@ -1,114 +0,0 @@ -image: alpine:latest - -stages: - - info - - build - -variables: - GIT_SUBMODULE_STRATEGY: normal - -before_script: - - apk update - - apk add musl-dev eudev-libs eudev-dev linux-headers meson ninja gcc scdoc - - apk add pixman-dev freetype-dev fontconfig-dev - - apk add libxcb-dev xcb-util-wm-dev xcb-util-cursor-dev yaml-dev - - apk add wayland-dev wayland-protocols wlroots-dev - - apk add json-c-dev libmpdclient-dev alsa-lib-dev pulseaudio-dev pipewire-dev - - apk add ttf-dejavu - - apk add git - - apk add flex bison - -versions: - stage: info - script: - - meson --version - - ninja --version - - cc --version - -debug: - stage: build - script: - - cd subprojects - - git clone https://codeberg.org/dnkl/fcft.git - - cd .. - - apk add gcovr - - mkdir -p bld/debug - - cd bld/debug - - meson --buildtype=debug -Db_coverage=true ../.. - - ninja -k0 - - meson test --print-errorlogs - - ninja coverage-html - - mv meson-logs/coveragereport ../../coverage - - ninja coverage-text - - tail -2 meson-logs/coverage.txt - artifacts: - paths: - - coverage - coverage: '/^TOTAL.*\s+(\d+\%)$/' - -# valgrind: -# stage: build -# script: -# - apk add valgrind -# - mkdir -p bld/debug -# - cd bld/debug -# - meson --buildtype=debug ../.. -# - ninja -k0 -# - meson test --verbose --wrapper "valgrind --leak-check=full --show-leak-kinds=all --error-exitcode=3" - -release: - stage: build - script: - - cd subprojects - - git clone https://codeberg.org/dnkl/fcft.git - - cd .. - - mkdir -p bld/release - - cd bld/release - - meson --buildtype=minsize ../../ - - ninja -k0 - - meson test --print-errorlogs - -x11_only: - stage: build - script: - - cd subprojects - - git clone https://codeberg.org/dnkl/fcft.git - - cd .. - - mkdir -p bld/debug - - cd bld/debug - - meson --buildtype=debug -Dbackend-x11=enabled -Dbackend-wayland=disabled ../../ - - ninja -k0 - - meson test --print-errorlogs - -wayland_only: - stage: build - script: - - cd subprojects - - git clone https://codeberg.org/dnkl/fcft.git - - cd .. - - mkdir -p bld/debug - - cd bld/debug - - meson --buildtype=debug -Dbackend-x11=disabled -Dbackend-wayland=enabled ../../ - - ninja -k0 - - meson test --print-errorlogs - -plugins_as_shared_modules: - stage: build - script: - - cd subprojects - - git clone https://codeberg.org/dnkl/fcft.git - - cd .. - - mkdir -p bld/debug - - cd bld/debug - - meson --buildtype=debug -Dcore-plugins-as-shared-libraries=true ../../ - - ninja -k0 - - meson test --print-errorlogs - -codespell: - image: alpine:latest - stage: build - script: - - apk add python3 - - apk add py3-pip - - pip install codespell - - codespell README.md CHANGELOG.md *.c *.h doc/*.scd From f75168796ad6f361eb35c4335918468afe7cd8c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 2 Jan 2023 14:08:21 +0100 Subject: [PATCH 473/611] module/pipewire: handle failures when trying to connect to pipewire * Replace asserts() with error logs * Handle not being connected in content() * Return from run() with an error --- modules/pipewire.c | 49 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 43 insertions(+), 6 deletions(-) diff --git a/modules/pipewire.c b/modules/pipewire.c index b70fdff..0cc641d 100644 --- a/modules/pipewire.c +++ b/modules/pipewire.c @@ -739,20 +739,32 @@ pipewire_init(struct module *module) /* Main loop */ data->loop = pw_main_loop_new(NULL); - assert(data->loop != NULL); + if (data->loop == NULL) { + LOG_ERR("failed to instantiate main loop"); + goto err; + } /* Context */ data->context = pw_context_new(pw_main_loop_get_loop(data->loop), NULL, 0); - assert(data->context != NULL); + if (data->context == NULL) { + LOG_ERR("failed to instantiate pipewire context"); + goto err; + } /* Core */ data->core = pw_context_connect(data->context, NULL, 0); - assert(data->core); + if (data->core == NULL) { + LOG_ERR("failed to connect to pipewire"); + goto err; + } pw_core_add_listener(data->core, &data->core_listener, &core_events, data); /* Registry */ data->registry = pw_core_get_registry(data->core, PW_VERSION_REGISTRY, 0); - assert(data->registry); + if (data->registry == NULL) { + LOG_ERR("failed to get core registry"); + goto err; + } pw_registry_add_listener(data->registry, &data->registry_listener, ®istry_events, data); /* Sync */ @@ -767,11 +779,26 @@ pipewire_init(struct module *module) data->node_data_source.is_sink = false; return data; + +err: + if (data->registry != NULL) + pw_proxy_destroy((struct pw_proxy *)data->registry); + if (data->core != NULL) + pw_core_disconnect(data->core); + if (data->context != NULL) + pw_context_destroy(data->context); + if (data->loop != NULL) + pw_main_loop_destroy(data->loop); + free(data); + return NULL; } static void pipewire_deinit(struct data *data) { + if (data == NULL) + return; + struct node *node = NULL; spa_list_consume(node, &data->node_list, link) node_free(node, data); @@ -828,6 +855,9 @@ content(struct module *module) { struct private *private = module->private; + if (private->data == NULL) + return dynlist_exposable_new(NULL, 0, 0, 0); + mtx_lock(&module->lock); struct exposable *exposables[2]; @@ -837,7 +867,9 @@ content(struct module *module) /* sink */ output_informations - = (private->data->target_sink == NULL ? &output_informations_null : &private->sink_informations); + = (private->data->target_sink == NULL + ? &output_informations_null + : &private->sink_informations); struct tag_set sink_tag_set = (struct tag_set){ .tags = (struct tag *[]){ @@ -856,7 +888,9 @@ content(struct module *module) /* source */ output_informations - = (private->data->target_source == NULL ? &output_informations_null : &private->source_informations); + = (private->data->target_source == NULL + ? &output_informations_null + : &private->source_informations); struct tag_set source_tag_set = (struct tag_set){ .tags = (struct tag *[]){ @@ -888,6 +922,9 @@ static int run(struct module *module) { struct private *private = module->private; + if (private->data == NULL) + return 1; + struct pw_loop *pw_loop = pw_main_loop_get_loop(private->data->loop); struct pollfd pollfds[] = { /* abort_fd */ From 134ae847dc22fbe8fa7e6881d4d510c97293b581 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 12 Jan 2023 18:15:16 +0100 Subject: [PATCH 474/611] =?UTF-8?q?module/river:=20add=20support=20for=20?= =?UTF-8?q?=E2=80=98layout=E2=80=99=20events?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 3 +- doc/yambar-modules-river.5.scd | 12 +++--- external/river-status-unstable-v1.xml | 19 ++++++++- modules/river.c | 55 ++++++++++++++++++++++++++- 4 files changed, 79 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 381737b..14bd53c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,8 @@ ## Unreleased ### Added -* field width tag format option ([#246][246]) +* Field width tag format option ([#246][246]) +* river: support for ‘layout’ events. [246]: https://codeberg.org/dnkl/yambar/issues/246 diff --git a/doc/yambar-modules-river.5.scd b/doc/yambar-modules-river.5.scd index 7f2920f..3bf3b61 100644 --- a/doc/yambar-modules-river.5.scd +++ b/doc/yambar-modules-river.5.scd @@ -54,14 +54,16 @@ once for all 32 river tags. This means you probably want to use a :< *Description* | seat : string -: The name of the seat (*title* particle only, see CONFIGURATION) +: The name of the seat. | title : string -: The seat's focused view's title (*title* particle only, see CONFIGURATION) +: The seat's focused view's title. | mode : string -: The seat's current mode (entered with e.g. *riverctl enter-mode foobar*) - +: The seat's current mode (entered with e.g. *riverctl enter-mode foobar*). +| layout +: string +: Current layout of the output currently focused by the seat. # CONFIGURATION @@ -90,7 +92,7 @@ once for all 32 river tags. This means you probably want to use a bar: left: - river: - title: {string: { text: "{seat} - {title} ({mode})" }} + title: {string: { text: "{seat} - {title} ({layout}/{mode})" }} content: map: conditions: diff --git a/external/river-status-unstable-v1.xml b/external/river-status-unstable-v1.xml index 6a74256..e9629dd 100644 --- a/external/river-status-unstable-v1.xml +++ b/external/river-status-unstable-v1.xml @@ -16,7 +16,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - + A global factory for objects that receive status information specific to river. It could be used to implement, for example, a status bar. @@ -47,7 +47,7 @@ - + This interface allows clients to receive information about the current windowing state of an output. @@ -83,6 +83,21 @@ + + + + Sent once on binding the interface should a layout name exist and again + whenever the name changes. + + + + + + + Sent when the current layout name has been removed without a new one + being set, for example when the active layout generator disconnects. + + diff --git a/modules/river.c b/modules/river.c index 059a44e..2273166 100644 --- a/modules/river.c +++ b/modules/river.c @@ -32,6 +32,9 @@ struct output { uint32_t occupied; uint32_t focused; uint32_t urgent; + + /* Layout */ + char *layout; }; struct seat { @@ -154,14 +157,19 @@ content(struct module *mod) size_t i = 32; tll_foreach(m->seats, it) { const struct seat *seat = &it->item; + const char *layout = + seat->output != NULL && seat->output->layout != NULL + ? seat->output->layout + : ""; struct tag_set tags = { .tags = (struct tag *[]){ tag_new_string(mod, "seat", seat->name), tag_new_string(mod, "title", seat->title), tag_new_string(mod, "mode", seat->mode), + tag_new_string(mod, "layout", layout), }, - .count = 3, + .count = 4, }; tag_parts[i++] = m->title->instantiate(m->title, &tags); @@ -193,6 +201,7 @@ output_destroy(struct output *output) seat->output = NULL; } free(output->name); + free(output->layout); if (output->status != NULL) zriver_output_status_v1_destroy(output->status); if (output->xdg_output != NULL) @@ -270,11 +279,53 @@ urgent_tags(void *data, struct zriver_output_status_v1 *zriver_output_status_v1, mod->bar->refresh(mod->bar); } +#if defined(ZRIVER_OUTPUT_STATUS_V1_LAYOUT_NAME_SINCE_VERSION) +static void +layout_name(void *data, + struct zriver_output_status_v1 *zriver_output_status_v1, + const char *name) +{ + struct output *output = data; + struct module *mod = output->m->mod; + + mtx_lock(&mod->lock); + { + free(output->layout); + output->layout = name != NULL ? strdup(name) : NULL; + } + mtx_unlock(&mod->lock); + mod->bar->refresh(mod->bar); +} +#endif + +#if defined(ZRIVER_OUTPUT_STATUS_V1_LAYOUT_NAME_CLEAR_SINCE_VERSION) +static void +layout_name_clear(void *data, + struct zriver_output_status_v1 *zriver_output_status_v1) +{ + struct output *output = data; + struct module *mod = output->m->mod; + + mtx_lock(&mod->lock); + { + free(output->layout); + output->layout = NULL; + } + mtx_unlock(&mod->lock); + mod->bar->refresh(mod->bar); +} +#endif static const struct zriver_output_status_v1_listener river_status_output_listener = { .focused_tags = &focused_tags, .view_tags = &view_tags, .urgent_tags = &urgent_tags, +#if defined(ZRIVER_OUTPUT_STATUS_V1_LAYOUT_NAME_SINCE_VERSION) + .layout_name = &layout_name, +#endif +#if defined(ZRIVER_OUTPUT_STATUS_V1_LAYOUT_NAME_CLEAR_SINCE_VERSION) + .layout_name_clear = &layout_name_clear, +#endif }; static void @@ -599,7 +650,7 @@ handle_global(void *data, struct wl_registry *registry, return; m->status_manager = wl_registry_bind( - registry, name, &zriver_status_manager_v1_interface, min(version, 3)); + registry, name, &zriver_status_manager_v1_interface, min(version, 4)); mtx_lock(&m->mod->lock); tll_foreach(m->outputs, it) From 10fde4dd0a20114323e8370195ed18483617a235 Mon Sep 17 00:00:00 2001 From: Ogromny Date: Mon, 9 Jan 2023 10:07:41 +0100 Subject: [PATCH 475/611] modules/pipewire: use roundf instead of ceilf for more accuracy --- modules/pipewire.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/pipewire.c b/modules/pipewire.c index 0cc641d..ee8359a 100644 --- a/modules/pipewire.c +++ b/modules/pipewire.c @@ -478,8 +478,8 @@ node_events_param(void *userdata, __attribute__((unused)) int seq, __attribute__ total += values[i]; float base_volume = total / n_values; - output_informations->linear_volume = ceilf(base_volume * 100); - output_informations->cubic_volume = ceilf(cbrtf(base_volume) * 100); + output_informations->linear_volume = roundf(base_volume * 100); + output_informations->cubic_volume = roundf(cbrtf(base_volume) * 100); } } From bdbcc0100ae6515d168bfd27e3de9650d4494451 Mon Sep 17 00:00:00 2001 From: Ogromny Date: Fri, 13 Jan 2023 10:19:54 +0100 Subject: [PATCH 476/611] modules/pipewire: change type of volume from uint8 to uint16 --- modules/pipewire.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/pipewire.c b/modules/pipewire.c index ee8359a..2300e7c 100644 --- a/modules/pipewire.c +++ b/modules/pipewire.c @@ -35,8 +35,8 @@ struct output_informations { /* informations */ bool muted; - uint8_t linear_volume; /* classic volume */ - uint8_t cubic_volume; /* volume a la pulseaudio */ + uint16_t linear_volume; /* classic volume */ + uint16_t cubic_volume; /* volume a la pulseaudio */ char *name; char *description; char *form_factor; /* headset, headphone, speaker, ..., can be null */ From 7773a17d576e525b02932cf66871ca477681ef45 Mon Sep 17 00:00:00 2001 From: Ogromny Date: Fri, 13 Jan 2023 14:40:58 +0100 Subject: [PATCH 477/611] CHANGELOG.md: add issue #262 --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 14bd53c..bbd5f36 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,11 +42,13 @@ * dwl module reporting only the last part of the title ([#251][251]) * i3/sway: regression; persistent workspaces shown twice ([#253][253]). +* pipewire: use roundf instead of ceilf for more accuracy ([#262][262]) [239]: https://codeberg.org/dnkl/yambar/issues/239 [241]: https://codeberg.org/dnkl/yambar/issues/241 [251]: https://codeberg.org/dnkl/yambar/pulls/251 [253]: https://codeberg.org/dnkl/yambar/issues/253 +[262]: https://codeberg.org/dnkl/yambar/issues/262 ### Security From 5da51210deaab93b01c0c05e65871aab8b7594f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Hern=C3=A1ndez=20Hern=C3=A1ndez?= Date: Mon, 16 Jan 2023 19:53:21 -0600 Subject: [PATCH 478/611] module/dwl: allow specify the name of tags --- CHANGELOG.md | 2 + doc/yambar-modules-dwl.5.scd | 17 ++++++-- modules/dwl.c | 75 ++++++++++++++++++++++++++++-------- 3 files changed, 75 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 14bd53c..e06242a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,8 +15,10 @@ * Field width tag format option ([#246][246]) * river: support for ‘layout’ events. +* dwl: support for specifying name of tags ([#256][256]) [246]: https://codeberg.org/dnkl/yambar/issues/246 +[256]: https://codeberg.org/dnkl/yambar/pulls/256 ### Changed diff --git a/doc/yambar-modules-dwl.5.scd b/doc/yambar-modules-dwl.5.scd index be34de1..60656f4 100644 --- a/doc/yambar-modules-dwl.5.scd +++ b/doc/yambar-modules-dwl.5.scd @@ -7,7 +7,7 @@ dwl - This module provides information about dwl tags, and information. This module provides a map of each tags present in dwl. -Each tags has its _id_, its status (_selected_, _empty_, _urgent_) +Each tags has its _id_, its _name_, its status (_selected_, _empty_, _urgent_) and the global data like _title_, _fullscreen_, _floating_, _selmon_, and _layout_). The tags start a 1. For needs where you only want information about the global data and not the _tags_, @@ -29,6 +29,9 @@ Running multiple instances at the same time may result in | id : int : Dwl tag id. +| name +: string +: The name of the tag (defaults to _id_ if not set). | selected : bool : True if the tag is currently selected. @@ -64,6 +67,10 @@ Running multiple instances at the same time may result in : int : yes : The number of defined tags in the dwl `config.def.h`. +| name-of-tags +: list +: false +: The name of the tags (must have the same length that _number-of-tags_). | dwl-info-filename : string : yes @@ -77,16 +84,18 @@ bar: - dwl: number-of-tags: 9 dwl-info-filename: "/home/ogromny/dwl_info" + name-of-tags: [ , , , , , , , ,  ] content: list: items: - map: conditions: - selected: {string: {text: "-> {id}"}} - ~empty: {string: {text: "{id}"}} - urgent: {string: {text: "=> {id} <="}} # default tag id == 0: {string: {text: "{layout} {title}"}} + + selected: {string: {text: "-> {name}"}} + ~empty: {string: {text: "{name}"}} + urgent: {string: {text: "=> {name} <="}} ``` # SEE ALSO diff --git a/modules/dwl.c b/modules/dwl.c index 5fa1023..df3a000 100644 --- a/modules/dwl.c +++ b/modules/dwl.c @@ -18,6 +18,7 @@ struct dwl_tag { int id; + char *name; bool selected; bool empty; bool urgent; @@ -51,13 +52,20 @@ enum LINE_MODE { LINE_MODE_LAYOUT, }; +static void +free_dwl_tag(struct dwl_tag *tag) +{ + free(tag->name); + free(tag); +} + static void destroy(struct module *module) { struct private *private = module->private; private->label->destroy(private->label); - tll_free_and_free(private->tags, free); + tll_free_and_free(private->tags, free_dwl_tag); free(private->dwl_info_filename); free(private->title); free(private->layout); @@ -91,11 +99,12 @@ content(struct module *module) tag_new_bool(module, "selmon", private->selmon), tag_new_string(module, "layout", private->layout), tag_new_int(module, "id", it->item->id), + tag_new_string(module, "name", it->item->name), tag_new_bool(module, "selected", it->item->selected), tag_new_bool(module, "empty", it->item->empty), tag_new_bool(module, "urgent", it->item->urgent), }, - .count = 9, + .count = 10, }; exposable[i++] = private->label->instantiate(private->label, &tags); tag_set_destroy(&tags); @@ -110,11 +119,12 @@ content(struct module *module) tag_new_bool(module, "selmon", private->selmon), tag_new_string(module, "layout", private->layout), tag_new_int(module, "id", 0), + tag_new_string(module, "name", "0"), tag_new_bool(module, "selected", false), tag_new_bool(module, "empty", true), tag_new_bool(module, "urgent", false), }, - .count = 9, + .count = 10, }; exposable[i++] = private->label->instantiate(private->label, &tags); tag_set_destroy(&tags); @@ -124,7 +134,7 @@ content(struct module *module) } static struct dwl_tag * -dwl_tag_find_or_create(struct private *private, uint32_t id) +dwl_tag_from_id(struct private *private, uint32_t id) { tll_foreach(private->tags, it) { @@ -132,11 +142,8 @@ dwl_tag_find_or_create(struct private *private, uint32_t id) return it->item; } - /* No need to order the tag, `print_status` from dwl already orders tags */ - struct dwl_tag *dwl_tag = calloc(1, sizeof(struct dwl_tag)); - dwl_tag->id = id; - tll_push_back(private->tags, dwl_tag); - return dwl_tag; + assert(false); /* unreachable */ + return NULL; } static void @@ -219,7 +226,7 @@ process_line(char *line, struct module *module) for (size_t id = 1; id <= private->number_of_tags; ++id) { uint32_t mask = 1 << (id - 1); - struct dwl_tag *dwl_tag = dwl_tag_find_or_create(private, id); + struct dwl_tag *dwl_tag = dwl_tag_from_id(private, id); dwl_tag->selected = mask & selected; dwl_tag->empty = !(mask & occupied); dwl_tag->urgent = mask & urgent; @@ -420,13 +427,30 @@ run(struct module *module) } static struct module * -dwl_new(struct particle *label, int number_of_tags, char const *dwl_info_filename) +dwl_new(struct particle *label, int number_of_tags, + struct yml_node const *name_of_tags, char const *dwl_info_filename) { struct private *private = calloc(1, sizeof(struct private)); private->label = label; private->number_of_tags = number_of_tags; private->dwl_info_filename = strdup(dwl_info_filename); + struct yml_list_iter list = {0}; + if (name_of_tags) + list = yml_list_iter(name_of_tags); + + for (int i = 1; i <= number_of_tags; i++) { + struct dwl_tag *dwl_tag = calloc(1, sizeof(struct dwl_tag)); + dwl_tag->id = i; + if (list.node) { + dwl_tag->name = strdup(yml_value_as_string(list.node)); + yml_list_next(&list); + } else if (asprintf(&dwl_tag->name, "%d", i) < 0) { + LOG_ERRNO("asprintf"); + } + tll_push_back(private->tags, dwl_tag); + } + struct module *module = module_common_new(); module->private = private; module->run = &run; @@ -442,10 +466,21 @@ from_conf(struct yml_node const *node, struct conf_inherit inherited) { struct yml_node const *content = yml_get_value(node, "content"); struct yml_node const *number_of_tags = yml_get_value(node, "number-of-tags"); + struct yml_node const *name_of_tags = yml_get_value(node, "name-of-tags"); struct yml_node const *dwl_info_filename = yml_get_value(node, "dwl-info-filename"); return dwl_new(conf_to_particle(content, inherited), yml_value_as_int(number_of_tags), - yml_value_as_string(dwl_info_filename)); + name_of_tags, yml_value_as_string(dwl_info_filename)); +} + +static bool +verify_names(keychain_t *keychain, const struct yml_node *node) +{ + if (!yml_is_list(node)) { + LOG_ERR("%s: %s is not a list", conf_err_prefix(keychain, node), yml_value_as_string(node)); + return false; + } + return conf_verify_list(keychain, node, &conf_verify_string); } static bool @@ -454,6 +489,7 @@ verify_conf(keychain_t *keychain, struct yml_node const *node) static struct attr_info const attrs[] = { {"number-of-tags", true, &conf_verify_unsigned}, + {"name-of-tags", false, &verify_names}, {"dwl-info-filename", true, &conf_verify_string}, MODULE_COMMON_ATTRS, }; @@ -463,10 +499,19 @@ verify_conf(keychain_t *keychain, struct yml_node const *node) /* No need to check whether is `number_of_tags` is a int * because `conf_verify_unsigned` already did it */ - struct yml_node const *key = yml_get_key(node, "number-of-tags"); + struct yml_node const *ntags_key = yml_get_key(node, "number-of-tags"); struct yml_node const *value = yml_get_value(node, "number-of-tags"); - if (yml_value_as_int(value) == 0) { - LOG_ERR("%s: %s must not be 0", conf_err_prefix(keychain, key), yml_value_as_string(key)); + int number_of_tags = yml_value_as_int(value); + if (number_of_tags == 0) { + LOG_ERR("%s: %s must not be 0", conf_err_prefix(keychain, ntags_key), yml_value_as_string(ntags_key)); + return false; + } + + struct yml_node const *key = yml_get_key(node, "name-of-tags"); + value = yml_get_value(node, "name-of-tags"); + if (value && yml_list_length(value) != number_of_tags) { + LOG_ERR("%s: %s must have the same number of elements that %s", conf_err_prefix(keychain, key), + yml_value_as_string(key), yml_value_as_string(ntags_key)); return false; } From 8ccd79ad087a913256cb8a759ff86989dee7754d Mon Sep 17 00:00:00 2001 From: Oleg Hahm Date: Fri, 3 Mar 2023 00:45:47 +0100 Subject: [PATCH 479/611] modules/network: do not use IPv6 link-local Probably you don't want to see your IPv6 link-local address but rather a global one. --- CHANGELOG.md | 2 ++ modules/network.c | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 507275c..bfbf6c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,8 +28,10 @@ * battery/network/script: `poll-interval` unit changed from seconds to milliseconds ([#244][244]). * all modules: minimum poll interval changed from 500ms to 250ms. +* network: do not use IPv6 link-local ([#281][281]) [244]: https://codeberg.org/dnkl/yambar/issues/244 +[281]: https://codeberg.org/dnkl/yambar/pulls/281 ### Deprecated diff --git a/modules/network.c b/modules/network.c index 6773fcf..7166e24 100644 --- a/modules/network.c +++ b/modules/network.c @@ -150,7 +150,8 @@ content(struct module *mod) if (it->item.family == AF_INET) inet_ntop(AF_INET, &it->item.addr.ipv4, ipv4_str, sizeof(ipv4_str)); else if (it->item.family == AF_INET6) - inet_ntop(AF_INET6, &it->item.addr.ipv6, ipv6_str, sizeof(ipv6_str)); + if(!IN6_IS_ADDR_LINKLOCAL(&it->item.addr.ipv6)) + inet_ntop(AF_INET6, &it->item.addr.ipv6, ipv6_str, sizeof(ipv6_str)); } struct tag_set tags = { From f21db9cacad472619391e1b0a2c1df96cc68c5bd Mon Sep 17 00:00:00 2001 From: Armin Fisslthaler Date: Fri, 24 Mar 2023 17:45:52 +0100 Subject: [PATCH 480/611] i3: add "native" sway/i3 sort mode This adds a sort mode for workspaces which corresponds to the default behavior in sway/i3. --- modules/i3.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/modules/i3.c b/modules/i3.c index 96e215a..65ee332 100644 --- a/modules/i3.c +++ b/modules/i3.c @@ -21,7 +21,7 @@ #include "i3-ipc.h" #include "i3-common.h" -enum sort_mode {SORT_NONE, SORT_ASCENDING, SORT_DESCENDING}; +enum sort_mode {SORT_NONE, SORT_NATIVE, SORT_ASCENDING, SORT_DESCENDING}; struct ws_content { char *name; @@ -185,6 +185,21 @@ workspace_add(struct private *m, struct workspace ws) tll_push_back(m->workspaces, ws); return; + case SORT_NATIVE: + if (ws.name_as_int >= 0) { + tll_foreach(m->workspaces, it) { + if (it->item.name_as_int < 0) + continue; + if (it->item.name_as_int > ws.name_as_int) { + tll_insert_before(m->workspaces, it, ws); + return; + } + } + }; + + tll_push_back(m->workspaces, ws); + return; + case SORT_ASCENDING: if (ws.name_as_int >= 0) { tll_foreach(m->workspaces, it) { @@ -974,6 +989,7 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) enum sort_mode sort_mode = sort_value == NULL ? SORT_NONE : strcmp(sort_value, "none") == 0 ? SORT_NONE : + strcmp(sort_value, "native") == 0 ? SORT_NATIVE : strcmp(sort_value, "ascending") == 0 ? SORT_ASCENDING : SORT_DESCENDING; const size_t persistent_count = @@ -1041,7 +1057,7 @@ static bool verify_sort(keychain_t *chain, const struct yml_node *node) { return conf_verify_enum( - chain, node, (const char *[]){"none", "ascending", "descending"}, 3); + chain, node, (const char *[]){"none", "native", "ascending", "descending"}, 4); } static bool From 3ec6fa1bc74f8c9dae43003e49cad8c9c59976ca Mon Sep 17 00:00:00 2001 From: Armin Fisslthaler Date: Fri, 24 Mar 2023 18:59:49 +0100 Subject: [PATCH 481/611] i3: update man page to include `native` sorting --- doc/yambar-modules-i3.5.scd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/yambar-modules-i3.5.scd b/doc/yambar-modules-i3.5.scd index 37d54bf..296a2da 100644 --- a/doc/yambar-modules-i3.5.scd +++ b/doc/yambar-modules-i3.5.scd @@ -67,7 +67,7 @@ with the _application_ and _title_ tags to replace the X11-only | sort : enum : no -: How to sort the list of workspaces; one of _none_, _ascending_ or _descending_, defaults to _none_. +: How to sort the list of workspaces; one of _none_, _native_, _ascending_ or _descending_, defaults to _none_. Use _native_ to sort numbered workspaces only. | strip-workspace-numbers : bool : no From daeb59e0219ed0baf617efbf158161e6c1c32aef Mon Sep 17 00:00:00 2001 From: Armin Fisslthaler Date: Fri, 24 Mar 2023 19:00:21 +0100 Subject: [PATCH 482/611] i3: add `native` sorting to changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bfbf6c6..6e20c4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ * Field width tag format option ([#246][246]) * river: support for ‘layout’ events. * dwl: support for specifying name of tags ([#256][256]) +* i3/sway: extend option `sort`; use `native` to sort numbered workspaces only. [246]: https://codeberg.org/dnkl/yambar/issues/246 [256]: https://codeberg.org/dnkl/yambar/pulls/256 From 963b9d47eefbbba1a9f2b6b6435ac13d05565e9f Mon Sep 17 00:00:00 2001 From: Yutaro Ohno Date: Thu, 23 Mar 2023 21:23:45 +0900 Subject: [PATCH 483/611] modules/dwl: handle the appid status dwl added an "appid" field as output status [1]. We currently don't handle this field, and thus output warnings that say "UNKNOWN action". Handle the "appid" field correctly and expose a value of this field to users. Also, suppress the warnings. Link: https://github.com/djpohly/dwl/commit/7f9a21247613a0d8ba6575869220e29071a40c64 [1] --- CHANGELOG.md | 2 ++ doc/yambar-modules-dwl.5.scd | 5 ++++- modules/dwl.c | 15 +++++++++++++-- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e20c4b..762c37b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,9 +17,11 @@ * river: support for ‘layout’ events. * dwl: support for specifying name of tags ([#256][256]) * i3/sway: extend option `sort`; use `native` to sort numbered workspaces only. +* modules/dwl: handle the appid status ([#284][284]) [246]: https://codeberg.org/dnkl/yambar/issues/246 [256]: https://codeberg.org/dnkl/yambar/pulls/256 +[284]: https://codeberg.org/dnkl/yambar/pulls/284 ### Changed diff --git a/doc/yambar-modules-dwl.5.scd b/doc/yambar-modules-dwl.5.scd index 60656f4..289955a 100644 --- a/doc/yambar-modules-dwl.5.scd +++ b/doc/yambar-modules-dwl.5.scd @@ -8,7 +8,7 @@ dwl - This module provides information about dwl tags, and information. This module provides a map of each tags present in dwl. Each tags has its _id_, its _name_, its status (_selected_, _empty_, _urgent_) -and the global data like _title_, _fullscreen_, _floating_, +and the global data like _title_, _appid_, _fullscreen_, _floating_, _selmon_, and _layout_). The tags start a 1. For needs where you only want information about the global data and not the _tags_, there is a tag with the id _0_ that contains only the global data. @@ -44,6 +44,9 @@ Running multiple instances at the same time may result in | title : string : The currently focused window's title. +| appid +: string +: The currently focused window's application id. | fullscreen : bool : True if there is a fullscreen window in the current tag. diff --git a/modules/dwl.c b/modules/dwl.c index df3a000..a9a5bab 100644 --- a/modules/dwl.c +++ b/modules/dwl.c @@ -35,6 +35,7 @@ struct private /* dwl data */ char *title; + char *appid; bool fullscreen; bool floating; bool selmon; @@ -45,6 +46,7 @@ struct private enum LINE_MODE { LINE_MODE_0, LINE_MODE_TITLE, + LINE_MODE_APPID, LINE_MODE_FULLSCREEN, LINE_MODE_FLOATING, LINE_MODE_SELMON, @@ -94,6 +96,7 @@ content(struct module *module) struct tag_set tags = { .tags = (struct tag*[]){ tag_new_string(module, "title", private->title), + tag_new_string(module, "appid", private->appid), tag_new_bool(module, "fullscreen", private->fullscreen), tag_new_bool(module, "floating", private->floating), tag_new_bool(module, "selmon", private->selmon), @@ -104,7 +107,7 @@ content(struct module *module) tag_new_bool(module, "empty", it->item->empty), tag_new_bool(module, "urgent", it->item->urgent), }, - .count = 10, + .count = 11, }; exposable[i++] = private->label->instantiate(private->label, &tags); tag_set_destroy(&tags); @@ -114,6 +117,7 @@ content(struct module *module) struct tag_set tags = { .tags = (struct tag*[]){ tag_new_string(module, "title", private->title), + tag_new_string(module, "appid", private->appid), tag_new_bool(module, "fullscreen", private->fullscreen), tag_new_bool(module, "floating", private->floating), tag_new_bool(module, "selmon", private->selmon), @@ -124,7 +128,7 @@ content(struct module *module) tag_new_bool(module, "empty", true), tag_new_bool(module, "urgent", false), }, - .count = 10, + .count = 11, }; exposable[i++] = private->label->instantiate(private->label, &tags); tag_set_destroy(&tags); @@ -183,6 +187,12 @@ process_line(char *line, struct module *module) free(private->title); private->title = strdup(save_pointer); break; + } else if (strcmp(string, "appid") == 0) { + line_mode = LINE_MODE_APPID; + /* Update the appid here, same as the title. */ + free(private->appid); + private->appid = strdup(save_pointer); + break; } else if (strcmp(string, "fullscreen") == 0) line_mode = LINE_MODE_FULLSCREEN; else if (strcmp(string, "floating") == 0) @@ -235,6 +245,7 @@ process_line(char *line, struct module *module) } else switch (line_mode) { case LINE_MODE_TITLE: + case LINE_MODE_APPID: assert(false); /* unreachable */ break; case LINE_MODE_FULLSCREEN: From 5e3859f2183f9dfa53a490ac2a6b748843e52e8c Mon Sep 17 00:00:00 2001 From: tiosgz Date: Sat, 8 Apr 2023 20:06:07 +0000 Subject: [PATCH 484/611] module/network: allow poll-interval == 0 Apparently the possibility to disable it was missed when the interval was migrated to use milliseconds. --- modules/network.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/network.c b/modules/network.c index 7166e24..0cc8cb6 100644 --- a/modules/network.c +++ b/modules/network.c @@ -1498,7 +1498,8 @@ conf_verify_poll_interval(keychain_t *chain, const struct yml_node *node) if (!conf_verify_unsigned(chain, node)) return false; - if (yml_value_as_int(node) < min_poll_interval) { + int interval = yml_value_as_int(node); + if (interval > 0 && interval < min_poll_interval) { LOG_ERR("%s: interval value cannot be less than %ldms", conf_err_prefix(chain, node), min_poll_interval); return false; From 971361b046eeb78aa8a7028802e2bf95f4edf107 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 9 Apr 2023 09:27:31 +0200 Subject: [PATCH 485/611] yaml: keep original value when anchor and target node both defines the same key When merging an anchor into a target yaml node, and both the target node and the anchor defines the same key(s), keep the value from the target node. Closes #286 --- CHANGELOG.md | 3 +++ yml.c | 26 ++++++++++++++++---------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 762c37b..48373d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,12 +50,15 @@ * i3/sway: regression; persistent workspaces shown twice ([#253][253]). * pipewire: use roundf instead of ceilf for more accuracy ([#262][262]) +* Crash when a yaml anchor has a value to already exists in the target + yaml node ([#286][286]). [239]: https://codeberg.org/dnkl/yambar/issues/239 [241]: https://codeberg.org/dnkl/yambar/issues/241 [251]: https://codeberg.org/dnkl/yambar/pulls/251 [253]: https://codeberg.org/dnkl/yambar/issues/253 [262]: https://codeberg.org/dnkl/yambar/issues/262 +[286]: https://codeberg.org/dnkl/yambar/issues/286 ### Security diff --git a/yml.c b/yml.c index b3f3d42..a81bdad 100644 --- a/yml.c +++ b/yml.c @@ -237,12 +237,15 @@ post_process(struct yml_node *node, char **error) .key = vv_it->item.key, .value = vv_it->item.value, }; - /* TODO: handle this. Is it an error? Or - * should we replace the existing key/value - * pair */ - assert(!dict_has_key(node, vv_it->item.key)); - tll_push_back(node->dict.pairs, p); + if (dict_has_key(node, vv_it->item.key)) { + /* Prefer value in target dictionary, over the + * value from the anchor */ + yml_destroy(vv_it->item.key); + yml_destroy(vv_it->item.value); + } else { + tll_push_back(node->dict.pairs, p); + } } /* Destroy list, but don't free (since its nodes @@ -274,11 +277,14 @@ post_process(struct yml_node *node, char **error) .value = v_it->item.value, }; - /* TODO: handle this. Is it an error? Or should we - * replace the existing key/value pair */ - assert(!dict_has_key(node, v_it->item.key)); - - tll_push_back(node->dict.pairs, p); + if (dict_has_key(node, v_it->item.key)) { + /* Prefer value in target dictionary, over the + * value from the anchor */ + yml_destroy(v_it->item.key); + yml_destroy(v_it->item.value); + } else { + tll_push_back(node->dict.pairs, p); + } } /* Destroy list here, *without* freeing nodes (since From 9218ef234cc07303ecd930db42e96cc4ce41b871 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 13 Jun 2023 17:07:22 +0200 Subject: [PATCH 486/611] pkgbuild: add changelog --- PKGBUILD | 1 + PKGBUILD.wayland-only | 1 + 2 files changed, 2 insertions(+) diff --git a/PKGBUILD b/PKGBUILD index 256706c..6ca4ecd 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -2,6 +2,7 @@ pkgname=yambar pkgver=1.9.0 pkgrel=1 pkgdesc="Simplistic and highly configurable status panel for X and Wayland" +changelog=CHANGELOG.md arch=('x86_64' 'aarch64') url=https://codeberg.org/dnkl/yambar license=(mit) diff --git a/PKGBUILD.wayland-only b/PKGBUILD.wayland-only index b64ae25..385ac0b 100644 --- a/PKGBUILD.wayland-only +++ b/PKGBUILD.wayland-only @@ -20,6 +20,7 @@ depends=( 'pipewire' 'fcft>=3.0.0' 'fcft<4.0.0') source=() +changelog=CHANGELOG.md pkgver() { cd ../.git &> /dev/null && git describe --tags --long | sed 's/^v//;s/\([^-]*-g\)/r\1/;s/-/./g' || From 7c5ea4fed6c05e5caaf221fd2064b297b2244654 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 4 Jul 2023 11:35:47 +0200 Subject: [PATCH 487/611] =?UTF-8?q?particle/map:=20make=20local=20function?= =?UTF-8?q?s=20=E2=80=98static=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- particles/map.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/particles/map.c b/particles/map.c index 82a1ee0..5912f9c 100644 --- a/particles/map.c +++ b/particles/map.c @@ -13,7 +13,7 @@ #include "map.h" -bool +static bool int_condition(const long tag_value, const long cond_value, enum map_op op) { switch (op) { @@ -28,7 +28,7 @@ int_condition(const long tag_value, const long cond_value, enum map_op op) } } -bool +static bool float_condition(const double tag_value, const double cond_value, enum map_op op) { switch (op) { @@ -43,7 +43,7 @@ float_condition(const double tag_value, const double cond_value, enum map_op op) } } -bool +static bool str_condition(const char* tag_value, const char* cond_value, enum map_op op) { switch (op) { @@ -58,7 +58,7 @@ str_condition(const char* tag_value, const char* cond_value, enum map_op op) } } -bool +static bool eval_comparison(const struct map_condition* map_cond, const struct tag_set *tags) { const struct tag *tag = tag_for_name(tags, map_cond->tag); @@ -115,7 +115,7 @@ eval_comparison(const struct map_condition* map_cond, const struct tag_set *tags return false; } -bool +static bool eval_map_condition(const struct map_condition* map_cond, const struct tag_set *tags) { switch(map_cond->op) From d236b9c1b929262e2ae3215425aa698f2eb5b3e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 4 Jul 2023 11:36:35 +0200 Subject: [PATCH 488/611] particle/map: make eval_map_condition() more readable --- particles/map.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/particles/map.c b/particles/map.c index 5912f9c..1c1cc9a 100644 --- a/particles/map.c +++ b/particles/map.c @@ -118,12 +118,20 @@ eval_comparison(const struct map_condition* map_cond, const struct tag_set *tags static bool eval_map_condition(const struct map_condition* map_cond, const struct tag_set *tags) { - switch(map_cond->op) - { - case MAP_OP_NOT: return !eval_map_condition(map_cond->cond1, tags); - case MAP_OP_AND: return eval_map_condition(map_cond->cond1, tags) && eval_map_condition(map_cond->cond2, tags); - case MAP_OP_OR: return eval_map_condition(map_cond->cond1, tags) || eval_map_condition(map_cond->cond2, tags); - default: return eval_comparison(map_cond, tags); + switch(map_cond->op) { + case MAP_OP_NOT: + return !eval_map_condition(map_cond->cond1, tags); + + case MAP_OP_AND: + return eval_map_condition(map_cond->cond1, tags) && + eval_map_condition(map_cond->cond2, tags); + + case MAP_OP_OR: + return eval_map_condition(map_cond->cond1, tags) || + eval_map_condition(map_cond->cond2, tags); + + default: + return eval_comparison(map_cond, tags); } } From 08f5f444eb91006a2d457e12b9860d45b5117a3c Mon Sep 17 00:00:00 2001 From: David Bimmler Date: Fri, 7 Jul 2023 11:20:36 +0200 Subject: [PATCH 489/611] battery: correct time_to_empty calculation The kernel docs state that time_to_empty contains the estimation of remaining time in seconds. Hence, calculate estimate minutes and hours from that in a more correct way. Signed-off-by: David Bimmler --- CHANGELOG.md | 1 + modules/battery.c | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 48373d6..cd36c33 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,6 +52,7 @@ * pipewire: use roundf instead of ceilf for more accuracy ([#262][262]) * Crash when a yaml anchor has a value to already exists in the target yaml node ([#286][286]). +* battery: Fix time conversion in battery estimation ([#303][303]). [239]: https://codeberg.org/dnkl/yambar/issues/239 [241]: https://codeberg.org/dnkl/yambar/issues/241 diff --git a/modules/battery.c b/modules/battery.c index db00add..2b04d65 100644 --- a/modules/battery.c +++ b/modules/battery.c @@ -86,8 +86,9 @@ content(struct module *mod) unsigned long minutes; if (m->time_to_empty >= 0) { - hours = m->time_to_empty / 60; - minutes = m->time_to_empty % 60; + minutes = m->time_to_empty / 60; + hours = minutes / 60; + minutes = minutes % 60; } else if (m->energy_full >= 0 && m->charge && m->power >= 0) { unsigned long energy = m->state == STATE_CHARGING ? m->energy_full - m->energy : m->energy; From b694fc15833258b4277420ac13f97b28ca5f4e77 Mon Sep 17 00:00:00 2001 From: David Bimmler Date: Fri, 7 Jul 2023 17:27:31 +0200 Subject: [PATCH 490/611] battery: also show time_to_full The kernel also provides time_to_full, also in seconds, so let's use that too. Both time_to_empty and time_to_full have 0 as their default value, hence only consider them for the estimate if they are positive (instead of non-negative). Signed-off-by: David Bimmler --- CHANGELOG.md | 1 + modules/battery.c | 16 +++++++++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cd36c33..fc05f95 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ * dwl: support for specifying name of tags ([#256][256]) * i3/sway: extend option `sort`; use `native` to sort numbered workspaces only. * modules/dwl: handle the appid status ([#284][284]) +* battery: also show estimation for time to full ([#303][303]). [246]: https://codeberg.org/dnkl/yambar/issues/246 [256]: https://codeberg.org/dnkl/yambar/pulls/256 diff --git a/modules/battery.c b/modules/battery.c index 2b04d65..c7e7d58 100644 --- a/modules/battery.c +++ b/modules/battery.c @@ -44,6 +44,7 @@ struct private { long charge; long current; long time_to_empty; + long time_to_full; }; static void @@ -85,10 +86,14 @@ content(struct module *mod) unsigned long hours; unsigned long minutes; - if (m->time_to_empty >= 0) { + if (m->time_to_empty > 0) { minutes = m->time_to_empty / 60; hours = minutes / 60; minutes = minutes % 60; + } else if (m->time_to_full > 0) { + minutes = m->time_to_full / 60; + hours = minutes / 60; + minutes = minutes % 60; } else if (m->energy_full >= 0 && m->charge && m->power >= 0) { unsigned long energy = m->state == STATE_CHARGING ? m->energy_full - m->energy : m->energy; @@ -332,6 +337,7 @@ update_status(struct module *mod) int charge_fd = openat(base_dir_fd, "charge_now", O_RDONLY); int current_fd = openat(base_dir_fd, "current_now", O_RDONLY); int time_to_empty_fd = openat(base_dir_fd, "time_to_empty_now", O_RDONLY); + int time_to_full_fd = openat(base_dir_fd, "time_to_full_now", O_RDONLY); long capacity = readint_from_fd(capacity_fd); long energy = energy_fd >= 0 ? readint_from_fd(energy_fd) : -1; @@ -339,6 +345,7 @@ update_status(struct module *mod) long charge = charge_fd >= 0 ? readint_from_fd(charge_fd) : -1; long current = current_fd >= 0 ? readint_from_fd(current_fd) : -1; long time_to_empty = time_to_empty_fd >= 0 ? readint_from_fd(time_to_empty_fd) : -1; + long time_to_full = time_to_full_fd >= 0 ? readint_from_fd(time_to_full_fd) : -1; char buf[512]; const char *status = readline_from_fd(status_fd, sizeof(buf), buf); @@ -357,6 +364,8 @@ update_status(struct module *mod) close(current_fd); if (time_to_empty_fd >= 0) close(time_to_empty_fd); + if (time_to_full_fd >= 0) + close(time_to_full_fd); if (base_dir_fd >= 0) close(base_dir_fd); @@ -381,8 +390,8 @@ update_status(struct module *mod) } LOG_DBG("capacity: %ld, energy: %ld, power: %ld, charge=%ld, current=%ld, " - "time-to-empty: %ld", capacity, energy, power, charge, current, - time_to_empty); + "time-to-empty: %ld, time-to-full: %ld", capacity, energy, power, + charge, current, time_to_empty, time_to_full); mtx_lock(&mod->lock); m->state = state; @@ -392,6 +401,7 @@ update_status(struct module *mod) m->charge = charge; m->current = current; m->time_to_empty = time_to_empty; + m->time_to_full = time_to_full; mtx_unlock(&mod->lock); return true; } From a342e036adc631189b0d075cd9879cb77e1aa914 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 9 Jul 2023 10:56:30 +0200 Subject: [PATCH 491/611] =?UTF-8?q?module/battery:=20don=E2=80=99t=20reset?= =?UTF-8?q?=20poll=20timeout=20on=20irrelevant=20udev=20notifications?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We may receive udev notifications for the power-supply subsystem, that aren’t for us. Before this patch, this would result in a new poll() call, with timeout being set to m->poll_interval again. That is, the poll timeout was being reset. In theory, this means we could end up in a situation where the battery status is never updated. This patch fixes it by tracking how much time is left of the poll interval. The interval is reset either when the timeout has occurred, or when we receive an udev notification that _is_ for us. Hopefully closes #305 --- CHANGELOG.md | 4 +++ modules/battery.c | 88 ++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 79 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fc05f95..30c9d47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,6 +54,9 @@ * Crash when a yaml anchor has a value to already exists in the target yaml node ([#286][286]). * battery: Fix time conversion in battery estimation ([#303][303]). +* battery: poll timeout being reset when receiving irrelevant udev + notification (leading to battery status never updating, in worst + case) ([#305][305]). [239]: https://codeberg.org/dnkl/yambar/issues/239 [241]: https://codeberg.org/dnkl/yambar/issues/241 @@ -61,6 +64,7 @@ [253]: https://codeberg.org/dnkl/yambar/issues/253 [262]: https://codeberg.org/dnkl/yambar/issues/262 [286]: https://codeberg.org/dnkl/yambar/issues/286 +[305]: https://codeberg.org/dnkl/yambar/issues/305 ### Security diff --git a/modules/battery.c b/modules/battery.c index c7e7d58..a83f30e 100644 --- a/modules/battery.c +++ b/modules/battery.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -13,13 +14,15 @@ #include #define LOG_MODULE "battery" -#define LOG_ENABLE_DBG 0 +#define LOG_ENABLE_DBG 1 #include "../log.h" #include "../bar/bar.h" #include "../config.h" #include "../config-verify.h" #include "../plugin.h" +#define max(x, y) ((x) > (y) ? (x) : (y)) + static const long min_poll_interval = 250; static const long default_poll_interval = 60 * 1000; @@ -47,6 +50,22 @@ struct private { long time_to_full; }; +static void +timespec_sub(const struct timespec *a, const struct timespec *b, + struct timespec *res) +{ + const long one_sec_in_ns = 1000000000; + + res->tv_sec = a->tv_sec - b->tv_sec; + res->tv_nsec = a->tv_nsec - b->tv_nsec; + + /* tv_nsec may be negative */ + if (res->tv_nsec < 0) { + res->tv_sec--; + res->tv_nsec += one_sec_in_ns; + } +} + static void destroy(struct module *mod) { @@ -439,14 +458,25 @@ run(struct module *mod) bar->refresh(bar); + int timeout_left_ms = m->poll_interval; + while (true) { struct pollfd fds[] = { {.fd = mod->abort_fd, .events = POLLIN}, {.fd = udev_monitor_get_fd(mon), .events = POLLIN}, }; - if (poll(fds, sizeof(fds) / sizeof(fds[0]), - m->poll_interval > 0 ? m->poll_interval : -1) < 0) - { + + int timeout = m->poll_interval > 0 ? timeout_left_ms : -1; + + struct timespec time_before_poll; + if (clock_gettime(CLOCK_BOOTTIME, &time_before_poll) < 0) { + LOG_ERRNO("failed to get current time"); + break; + } + + const int poll_ret = poll(fds, sizeof(fds) / sizeof(fds[0]), timeout); + + if (poll_ret < 0) { if (errno == EINTR) continue; @@ -459,21 +489,53 @@ run(struct module *mod) break; } + bool udev_for_us = false; + if (fds[1].revents & POLLIN) { struct udev_device *dev = udev_monitor_receive_device(mon); - if (dev == NULL) - continue; + if (dev != NULL) { + const char *sysname = udev_device_get_sysname(dev); + udev_for_us = + sysname != NULL && strcmp(sysname, m->battery) == 0; - const char *sysname = udev_device_get_sysname(dev); - bool is_us = sysname != NULL && strcmp(sysname, m->battery) == 0; - udev_device_unref(dev); + if (!udev_for_us) { + LOG_DBG("udev notification not for us (%s != %s)", + m->battery, sysname != sysname ? sysname : "NULL"); + } - if (!is_us) - continue; + udev_device_unref(dev); + } } - if (update_status(mod)) - bar->refresh(bar); + if (udev_for_us || poll_ret == 0) { + if (update_status(mod)) + bar->refresh(bar); + } + + if (poll_ret == 0 || udev_for_us) { + LOG_DBG("resetting timeout-left to %ldms", m->poll_interval); + timeout_left_ms = m->poll_interval; + } else { + struct timespec time_after_poll; + if (clock_gettime(CLOCK_BOOTTIME, &time_after_poll) < 0) { + LOG_ERRNO("failed to get current time"); + break; + } + + struct timespec timeout_consumed; + timespec_sub(&time_after_poll, &time_before_poll, &timeout_consumed); + + const int timeout_consumed_ms = + timeout_consumed.tv_sec * 1000 + timeout_consumed.tv_nsec / 1000000; + + LOG_DBG("timeout-left before: %dms, consumed: %dms, updated: %dms", + timeout_left_ms, timeout_consumed_ms, + max(timeout_left_ms - timeout_consumed_ms, 0)); + + timeout_left_ms -= timeout_consumed_ms; + if (timeout_left_ms < 0) + timeout_left_ms = 0; + } } out: From 6220a07aafb6766d26d395bae061ef38d620f43e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 9 Jul 2023 11:17:14 +0200 Subject: [PATCH 492/611] module/battery: debug log when updating due to udev notification --- modules/battery.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/battery.c b/modules/battery.c index a83f30e..90737a3 100644 --- a/modules/battery.c +++ b/modules/battery.c @@ -501,7 +501,8 @@ run(struct module *mod) if (!udev_for_us) { LOG_DBG("udev notification not for us (%s != %s)", m->battery, sysname != sysname ? sysname : "NULL"); - } + } else + LOG_DBG("triggering update due to udev notification"); udev_device_unref(dev); } From f948b9f8f9b5a57c14994534ac06ffe102a7c302 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 9 Jul 2023 11:17:54 +0200 Subject: [PATCH 493/611] module/battery: regression: allow poll-interval == 0 The regression was introduced after 1.9.0, hence no changelog entry. --- modules/battery.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/battery.c b/modules/battery.c index 90737a3..f838c8e 100644 --- a/modules/battery.c +++ b/modules/battery.c @@ -586,7 +586,9 @@ conf_verify_poll_interval(keychain_t *chain, const struct yml_node *node) if (!conf_verify_unsigned(chain, node)) return false; - if (yml_value_as_int(node) < min_poll_interval) { + const long value = yml_value_as_int(node); + + if (value != 0 && value < min_poll_interval) { LOG_ERR("%s: interval value cannot be less than %ldms", conf_err_prefix(chain, node), min_poll_interval); return false; From d6e7710a7ebd0be1f2dba677394f5b30b3e52a4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 11 Jul 2023 10:26:28 +0200 Subject: [PATCH 494/611] particle: on-click: tilde expansion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We now do tilde expansion of the *first* argument in on-click handlers. That is: ~/bin/foobar.sh ~/arg1 is expanded to $HOME/bin/foobar.sh ~/arg1 (meaning, the handler will most likely *not* do what you’d expect) Related to #307 --- CHANGELOG.md | 2 ++ config-verify.c | 44 ++++++++++++++++++++++++++++++-------- config.c | 37 +++++++++++++++++++++++++++----- doc/yambar-particles.5.scd | 9 ++++---- particle.c | 4 ++-- particle.h | 2 +- 6 files changed, 77 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 30c9d47..8ff897d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,10 +19,12 @@ * i3/sway: extend option `sort`; use `native` to sort numbered workspaces only. * modules/dwl: handle the appid status ([#284][284]) * battery: also show estimation for time to full ([#303][303]). +* on-click: tilde expansion ([#307][307]) [246]: https://codeberg.org/dnkl/yambar/issues/246 [256]: https://codeberg.org/dnkl/yambar/pulls/256 [284]: https://codeberg.org/dnkl/yambar/pulls/284 +[307]: https://codeberg.org/dnkl/yambar/issues/307 ### Changed diff --git a/config-verify.c b/config-verify.c index a099ef7..9b4fad7 100644 --- a/config-verify.c +++ b/config-verify.c @@ -174,22 +174,47 @@ conf_verify_dict(keychain_t *chain, const struct yml_node *node, return true; } +static bool +verify_on_click_path(keychain_t *chain, const struct yml_node *node) +{ + if (!conf_verify_string(chain, node)) + return false; + +#if 1 + /* We allow non-absolute paths in on-click handlers */ + return true; +#else + const char *path = yml_value_as_string(node); + + const bool is_absolute = path[0] == '/'; + const bool is_tilde = path[0] == '~' && path[1] == '/'; + + if (!is_absolute && !is_tilde) { + LOG_ERR("%s: path must be either absolute, or begin with '~/", + conf_err_prefix(chain, node)); + return false; + } + + return true; +#endif +} + bool conf_verify_on_click(keychain_t *chain, const struct yml_node *node) { /* on-click: */ const char *s = yml_value_as_string(node); if (s != NULL) - return true; + return verify_on_click_path(chain, node); static const struct attr_info info[] = { - {"left", false, &conf_verify_string}, - {"middle", false, &conf_verify_string}, - {"right", false, &conf_verify_string}, - {"wheel-up", false, &conf_verify_string}, - {"wheel-down", false, &conf_verify_string}, - {"previous", false, &conf_verify_string}, - {"next", false, &conf_verify_string}, + {"left", false, &verify_on_click_path}, + {"middle", false, &verify_on_click_path}, + {"right", false, &verify_on_click_path}, + {"wheel-up", false, &verify_on_click_path}, + {"wheel-down", false, &verify_on_click_path}, + {"previous", false, &verify_on_click_path}, + {"next", false, &verify_on_click_path}, {NULL, false, NULL}, }; @@ -306,7 +331,8 @@ conf_verify_particle_dictionary(keychain_t *chain, const struct yml_node *node) const char *particle_name = yml_value_as_string(particle); if (particle_name == NULL) { - LOG_ERR("%s: particle name must be a string", conf_err_prefix(chain, particle)); + LOG_ERR("%s: particle name must be a string", + conf_err_prefix(chain, particle)); return false; } diff --git a/config.c b/config.c index 3d32678..debf837 100644 --- a/config.c +++ b/config.c @@ -211,20 +211,47 @@ conf_to_particle(const struct yml_node *node, struct conf_inherit inherited) int right = margin != NULL ? yml_value_as_int(margin) : right_margin != NULL ? yml_value_as_int(right_margin) : 0; - const char *on_click_templates[MOUSE_BTN_COUNT] = {NULL}; + char *on_click_templates[MOUSE_BTN_COUNT] = {NULL}; if (on_click != NULL) { - const char *legacy = yml_value_as_string(on_click); + const char *yml_legacy = yml_value_as_string(on_click); + + if (yml_legacy != NULL) { + char *legacy = NULL; + + if (yml_legacy[0] == '~' && yml_legacy[1] == '/') { + const char *home_dir = getenv("HOME"); + + if (home_dir != NULL) + asprintf(&legacy, "%s/%s", home_dir, yml_legacy + 2); + + if (legacy == NULL) + legacy = strdup(yml_legacy); + } else + legacy = strdup(yml_legacy); - if (legacy != NULL) on_click_templates[MOUSE_BTN_LEFT] = legacy; + } - if (yml_is_dict(on_click)) { + else if (yml_is_dict(on_click)) { for (struct yml_dict_iter it = yml_dict_iter(on_click); it.key != NULL; yml_dict_next(&it)) { const char *key = yml_value_as_string(it.key); - const char *template = yml_value_as_string(it.value); + const char *yml_template = yml_value_as_string(it.value); + + char *template = NULL; + + if (yml_template[0] == '~' && yml_template[1] == '/') { + const char *home_dir = getenv("HOME"); + + if (home_dir != NULL) + asprintf(&template, "%s/%s", home_dir, yml_template + 2); + + if (template == NULL) + template = strdup(yml_template); + } else + template = strdup(yml_template); if (strcmp(key, "left") == 0) on_click_templates[MOUSE_BTN_LEFT] = template; diff --git a/doc/yambar-particles.5.scd b/doc/yambar-particles.5.scd index adf4485..5dc858b 100644 --- a/doc/yambar-particles.5.scd +++ b/doc/yambar-particles.5.scd @@ -44,10 +44,11 @@ following attributes are supported by all particles: | on-click : associative array/string : no -: When set to a string, executes the string as a command when the particle - is left-clicked. Tags can be used. Note that the string is *not* - executed in a shell. The same applies to all attributes associated with - it, below. +: When set to a string, executes the string as a command when the + particle is left-clicked. Tags can be used. Note that the string is + *not* executed in a shell. Environment variables are not expanded. + *~/* is expanded, but only in the first argument. The same applies + to all attributes associated with it, below. | on-click.left : string : no diff --git a/particle.c b/particle.c index fe1d138..2035d9a 100644 --- a/particle.c +++ b/particle.c @@ -30,7 +30,7 @@ particle_default_destroy(struct particle *particle) struct particle * particle_common_new(int left_margin, int right_margin, - const char **on_click_templates, + char **on_click_templates, struct fcft_font *font, enum font_shaping font_shaping, pixman_color_t foreground, struct deco *deco) { @@ -46,7 +46,7 @@ particle_common_new(int left_margin, int right_margin, for (size_t i = 0; i < MOUSE_BTN_COUNT; i++) { if (on_click_templates[i] != NULL) { p->have_on_click_template = true; - p->on_click_templates[i] = strdup(on_click_templates[i]); + p->on_click_templates[i] = on_click_templates[i]; } } } diff --git a/particle.h b/particle.h index c92c5fc..a2b55a8 100644 --- a/particle.h +++ b/particle.h @@ -64,7 +64,7 @@ struct exposable { }; struct particle *particle_common_new( - int left_margin, int right_margin, const char *on_click_templates[], + int left_margin, int right_margin, char *on_click_templates[], struct fcft_font *font, enum font_shaping font_shaping, pixman_color_t foreground, struct deco *deco); From 8e4d7f04e4c0353020151b6a7a721374c619878a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 11 Jul 2023 08:27:41 +0200 Subject: [PATCH 495/611] =?UTF-8?q?module/script:=20path:=20expand=20?= =?UTF-8?q?=E2=80=98~=E2=80=99=20to=20the=20user=E2=80=99s=20$HOME=20direc?= =?UTF-8?q?tory?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #307 --- CHANGELOG.md | 1 + doc/yambar-modules-script.5.scd | 3 ++- modules/script.c | 35 +++++++++++++++++++++++++++------ 3 files changed, 32 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ff897d..2b7eca8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ * modules/dwl: handle the appid status ([#284][284]) * battery: also show estimation for time to full ([#303][303]). * on-click: tilde expansion ([#307][307]) +* script: tilde expansion of `path` ([#307][307]). [246]: https://codeberg.org/dnkl/yambar/issues/246 [256]: https://codeberg.org/dnkl/yambar/pulls/256 diff --git a/doc/yambar-modules-script.5.scd b/doc/yambar-modules-script.5.scd index ec91c7e..d27a006 100644 --- a/doc/yambar-modules-script.5.scd +++ b/doc/yambar-modules-script.5.scd @@ -70,7 +70,8 @@ User defined. | path : string : yes -: Path to script/binary to execute. Must be an absolute path. +: Path to script/binary to execute. Must either be an absolute path, + or start with *~/*. | args : list of strings : no diff --git a/modules/script.c b/modules/script.c index f0cc9a7..63928a6 100644 --- a/modules/script.c +++ b/modules/script.c @@ -631,11 +631,11 @@ run(struct module *mod) } static struct module * -script_new(const char *path, size_t argc, const char *const argv[static argc], +script_new(char *path, size_t argc, const char *const argv[static argc], int poll_interval, struct particle *_content) { struct private *m = calloc(1, sizeof(*m)); - m->path = strdup(path); + m->path = path; m->content = _content; m->argc = argc; m->argv = malloc(argc * sizeof(m->argv[0])); @@ -655,7 +655,7 @@ script_new(const char *path, size_t argc, const char *const argv[static argc], static struct module * from_conf(const struct yml_node *node, struct conf_inherit inherited) { - const struct yml_node *path = yml_get_value(node, "path"); + const struct yml_node *path_node = yml_get_value(node, "path"); const struct yml_node *args = yml_get_value(node, "args"); const struct yml_node *c = yml_get_value(node, "content"); const struct yml_node *poll_interval = yml_get_value(node, "poll-interval"); @@ -673,8 +673,26 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) } } + const char *yml_path = yml_value_as_string(path_node); + char *path = NULL; + + if (yml_path[0] == '~' && yml_path[1] == '/') { + const char *home_dir = getenv("HOME"); + + if (home_dir == NULL) { + LOG_ERRNO("failed to expand '~"); + return NULL; + } + + if (asprintf(&path, "%s/%s", home_dir, yml_path + 2) < 0) { + LOG_ERRNO("failed to expand '~"); + return NULL; + } + } else + path = strdup(yml_path); + return script_new( - yml_value_as_string(path), argc, argv, + path, argc, argv, poll_interval != NULL ? yml_value_as_int(poll_interval) : 0, conf_to_particle(c, inherited)); } @@ -686,8 +704,13 @@ conf_verify_path(keychain_t *chain, const struct yml_node *node) return false; const char *path = yml_value_as_string(node); - if (strlen(path) < 1 || path[0] != '/') { - LOG_ERR("%s: path must be absolute", conf_err_prefix(chain, node)); + + const bool is_tilde = path[0] == '~' && path[1] == '/'; + const bool is_absolute = path[0] == '/'; + + if (!is_tilde && !is_absolute) { + LOG_ERR("%s: path must either be absolute, or begin with '~/'", + conf_err_prefix(chain, node)); return false; } From f923261fecf8147dc0fde23a833ca19627287e32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 11 Jul 2023 12:40:14 +0200 Subject: [PATCH 496/611] =?UTF-8?q?config:=20don=E2=80=99t=20ignore=20aspr?= =?UTF-8?q?intf()=20return=20value?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/config.c b/config.c index debf837..2ad305f 100644 --- a/config.c +++ b/config.c @@ -222,7 +222,8 @@ conf_to_particle(const struct yml_node *node, struct conf_inherit inherited) const char *home_dir = getenv("HOME"); if (home_dir != NULL) - asprintf(&legacy, "%s/%s", home_dir, yml_legacy + 2); + if (asprintf(&legacy, "%s/%s", home_dir, yml_legacy + 2) < 0) + legacy = NULL; if (legacy == NULL) legacy = strdup(yml_legacy); @@ -246,7 +247,8 @@ conf_to_particle(const struct yml_node *node, struct conf_inherit inherited) const char *home_dir = getenv("HOME"); if (home_dir != NULL) - asprintf(&template, "%s/%s", home_dir, yml_template + 2); + if (asprintf(&template, "%s/%s", home_dir, yml_template + 2) < 0) + template = NULL; if (template == NULL) template = strdup(yml_template); From 5ac7d51e8a1f376278c7b2ed84b50522717b06cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 14 Jul 2023 08:34:16 +0200 Subject: [PATCH 497/611] changelog: minor formatting changes --- CHANGELOG.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b7eca8..3855751 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -53,9 +53,10 @@ * dwl module reporting only the last part of the title ([#251][251]) * i3/sway: regression; persistent workspaces shown twice ([#253][253]). -* pipewire: use roundf instead of ceilf for more accuracy ([#262][262]) -* Crash when a yaml anchor has a value to already exists in the target - yaml node ([#286][286]). +* pipewire: use `roundf()` instead of `ceilf()` for more accuracy + ([#262][262]) +* Crash when a yaml anchor has a value that already exists in the + target yaml node ([#286][286]). * battery: Fix time conversion in battery estimation ([#303][303]). * battery: poll timeout being reset when receiving irrelevant udev notification (leading to battery status never updating, in worst From 1b23e727704bd96cfb46bdec84520743bdaac13e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 14 Jul 2023 09:02:32 +0200 Subject: [PATCH 498/611] changelog: prepare for 1.10.0 --- CHANGELOG.md | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3855751..416a617 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -* [Unreleased](#unreleased) +* [1.10.0](#1-10-0) * [1.9.0](#1-9-0) * [1.8.0](#1-8-0) * [1.7.0](#1-7-0) @@ -10,7 +10,8 @@ * [1.5.0](#1-5-0) -## Unreleased +## 1.10.0 + ### Added * Field width tag format option ([#246][246]) @@ -41,8 +42,6 @@ [281]: https://codeberg.org/dnkl/yambar/pulls/281 -### Deprecated -### Removed ### Fixed * Build failures for certain combinations of enabled and disabled @@ -71,10 +70,19 @@ [305]: https://codeberg.org/dnkl/yambar/issues/305 -### Security ### Contributors * Leonardo Gibrowski Faé (Horus) +* Armin Fisslthaler +* Ben Brown +* David Bimmler +* Leonardo Hernández Hernández +* Ogromny +* Oleg Hahm +* Stanislav Ochotnický +* tiosgz +* Yutaro Ohno + ## 1.9.0 From c4e094de3e531675bae9ff76d7e6f0dfdcaecae9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 14 Jul 2023 09:03:02 +0200 Subject: [PATCH 499/611] meson+pkgbuild: bump version to 1.10.0 --- PKGBUILD | 2 +- PKGBUILD.wayland-only | 2 +- meson.build | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/PKGBUILD b/PKGBUILD index 6ca4ecd..5aaea5d 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -1,5 +1,5 @@ pkgname=yambar -pkgver=1.9.0 +pkgver=1.10.0 pkgrel=1 pkgdesc="Simplistic and highly configurable status panel for X and Wayland" changelog=CHANGELOG.md diff --git a/PKGBUILD.wayland-only b/PKGBUILD.wayland-only index 385ac0b..4d11120 100644 --- a/PKGBUILD.wayland-only +++ b/PKGBUILD.wayland-only @@ -1,5 +1,5 @@ pkgname=yambar-wayland -pkgver=1.9.0 +pkgver=1.10.0 pkgrel=1 pkgdesc="Simplistic and highly configurable status panel for Wayland" arch=('x86_64' 'aarch64') diff --git a/meson.build b/meson.build index 763b412..fcb647a 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('yambar', 'c', - version: '1.9.0', + version: '1.10.0', license: 'MIT', meson_version: '>=0.59.0', default_options: ['c_std=c18', From 5db61745a4bf863f3852f4ee3e0f9e3d76a2d073 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 14 Jul 2023 09:04:32 +0200 Subject: [PATCH 500/611] =?UTF-8?q?changelog:=20add=20new=20=E2=80=98unrel?= =?UTF-8?q?eased=E2=80=99=20section?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 416a617..c94058a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # Changelog +* [Unreleased](#unreleased) * [1.10.0](#1-10-0) * [1.9.0](#1-9-0) * [1.8.0](#1-8-0) @@ -10,6 +11,16 @@ * [1.5.0](#1-5-0) +## Unreleased +### Added +### Changed +### Deprecated +### Removed +### Fixed +### Security +### Contributors + + ## 1.10.0 ### Added From e1fc3a0e29b608110f0965a19f35d85fb3df03e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 14 Jul 2023 12:52:19 +0200 Subject: [PATCH 501/611] =?UTF-8?q?tag:=20explicitly=20initialize=20?= =?UTF-8?q?=E2=80=98fmt=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes the following compiler warning/error: In file included from /usr/include/stdio.h:906, from ../tag.c:6: In function ‘snprintf’, inlined from ‘tags_expand_template’ at ../tag.c:708:13: /usr/include/bits/stdio2.h:54:10: error: ‘fmt’ may be used uninitialized [-Werror=maybe-uninitialized] 54 | return __builtin___snprintf_chk (__s, __n, __USE_FORTIFY_LEVEL - 1, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 55 | __glibc_objsize (__s), __fmt, | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 56 | __va_arg_pack ()); | ~~~~~~~~~~~~~~~~~ ../tag.c: In function ‘tags_expand_template’: ../tag.c:677:25: note: ‘fmt’ was declared here 677 | const char *fmt; | ^~~ cc1: all warnings being treated as errors Closes #311 --- tag.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tag.c b/tag.c index b098eb7..0f44d7e 100644 --- a/tag.c +++ b/tag.c @@ -674,7 +674,7 @@ tags_expand_template(const char *template, const struct tag_set *tags) const long max = tag->max(tag); long value = kind == VALUE_MIN ? min : max; - const char *fmt; + const char *fmt = NULL; switch (format) { case FMT_DEFAULT: fmt = zero_pad ? "%0*ld" : "%*ld"; break; case FMT_HEX: fmt = zero_pad ? "%0*lx" : "%*lx"; break; @@ -704,6 +704,8 @@ tags_expand_template(const char *template, const struct tag_set *tags) } } + assert(fmt != NULL); + char str[24]; snprintf(str, sizeof(str), fmt, digits, value); sbuf_append(&formatted, str); From 42cef9373e27d10275a1e14fc5fbbbc7f15cffab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 14 Jul 2023 12:54:23 +0200 Subject: [PATCH 502/611] =?UTF-8?q?changelog:=20"=E2=80=98fmt=E2=80=99=20m?= =?UTF-8?q?ay=20be=20used=20uninitialized"=20compiler=20warning?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c94058a..73d0100 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,12 @@ ### Deprecated ### Removed ### Fixed + +* Compiler error _‘fmt’ may be used uninitialized_ ([#311][311]). + +[311]: https://codeberg.org/dnkl/yambar/issues/311 + + ### Security ### Contributors From 78f7b60e1308d9f6c879b5cbae2dadba4fa88762 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 24 Jul 2023 17:13:19 +0200 Subject: [PATCH 503/611] particle/map: non-greedy matching of quotes Flex regexps are greedy. This means '"foo" || "bar"' will return 'foo" || "bar', which is obviously wrong. Use "start conditions" to implement non-greedy matching. Closes #302 --- CHANGELOG.md | 3 +++ particles/map.l | 56 ++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 73d0100..faa616a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,8 +19,11 @@ ### Fixed * Compiler error _‘fmt’ may be used uninitialized_ ([#311][311]). +* map: conditions failing to match when they contain multiple, quoted + tag values ([#302][302]). [311]: https://codeberg.org/dnkl/yambar/issues/311 +[302]: https://codeberg.org/dnkl/yambar/issues/302 ### Security diff --git a/particles/map.l b/particles/map.l index 7a5ebc8..d34f086 100644 --- a/particles/map.l +++ b/particles/map.l @@ -2,13 +2,67 @@ #include #include "map.h" #include "map.tab.h" +void yyerror(const char *s); %} %option warn nodefault nounput noinput noyywrap + char *quoted = NULL; + size_t quote_len = 0; + +%x QUOTE + %% + [[:alnum:]_-]+ yylval.str = strdup(yytext); return WORD; -\".*\" yylval.str = strndup(yytext + 1, strlen(yytext) - 2); return STRING; + +\" { + BEGIN(QUOTE); + quoted = calloc(1, sizeof(quoted[0])); +} + +[^\\\"]* { + /* printf("CAT: %s\n", yytext); */ + const size_t yy_length = strlen(yytext); + quoted = realloc(quoted, quote_len + yy_length + 1); + strcat(quoted, yytext); + quote_len += yy_length; +} + +\\\" { + /* printf("escaped quote\n"); */ + quoted = realloc(quoted, quote_len + 1 + 1); + strcat(quoted, "\""); + quote_len++; +} + +\\. { + /* printf("CAT: %s\n", yytext); */ + const size_t yy_length = strlen(yytext); + quoted = realloc(quoted, quote_len + yy_length + 1); + strcat(quoted, yytext); + quote_len += yy_length; +} + +\\ { + /* quoted string that ends with a backslash: "string\ */ + quoted = realloc(quoted, quote_len + 1 + 1); + strcat(quoted, "\\"); + quote_len++; +} + +\" { + /* printf("QUOTED=%s\n", quoted); */ + yylval.str = strdup(quoted); + + free(quoted); + quoted = NULL; + quote_len = 0; + + BEGIN(INITIAL); + return STRING; +} + == yylval.op = MAP_OP_EQ; return CMP_OP; != yylval.op = MAP_OP_NE; return CMP_OP; \<= yylval.op = MAP_OP_LE; return CMP_OP; From 9a111a52f5312fe2bd7d9dbd5326d52895a165ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 18 Aug 2023 16:49:18 +0200 Subject: [PATCH 504/611] ci: 'pipeline' -> 'steps' --- .woodpecker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.woodpecker.yml b/.woodpecker.yml index 058b08a..0b0cd6a 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -1,4 +1,4 @@ -pipeline: +steps: codespell: when: branch: From 7fbc1f2c44482c56f1aacf00d74c20645db10a20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 8 Oct 2023 11:12:08 +0200 Subject: [PATCH 505/611] bar/wayland: seal memfd --- bar/wayland.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/bar/wayland.c b/bar/wayland.c index edbf0db..2b1148b 100644 --- a/bar/wayland.c +++ b/bar/wayland.c @@ -937,6 +937,17 @@ get_buffer(struct wayland_backend *backend) goto err; } +#if defined(MEMFD_CREATE) + /* Seal file - we no longer allow any kind of resizing */ + /* TODO: wayland mmaps(PROT_WRITE), for some unknown reason, hence we cannot use F_SEAL_FUTURE_WRITE */ + if (fcntl(pool_fd, F_ADD_SEALS, + F_SEAL_GROW | F_SEAL_SHRINK | /*F_SEAL_FUTURE_WRITE |*/ F_SEAL_SEAL) < 0) + { + LOG_ERRNO("failed to seal SHM backing memory file"); + /* This is not a fatal error */ + } +#endif + pool = wl_shm_create_pool(backend->shm, pool_fd, size); if (pool == NULL) { LOG_ERR("failed to create SHM pool"); From cbd3bebb040d3baa442e396ad2c8b02baad449b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 8 Oct 2023 11:12:15 +0200 Subject: [PATCH 506/611] bar/wayland: create memfd with MFD_NOEXEC_SEAL --- bar/wayland.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/bar/wayland.c b/bar/wayland.c index 2b1148b..eb770da 100644 --- a/bar/wayland.c +++ b/bar/wayland.c @@ -28,6 +28,12 @@ #include "private.h" +#if defined(MFD_NOEXEC_SEAL) + #define YAMBAR_MFD_FLAGS (MFD_CLOEXEC | MFD_ALLOW_SEALING | MFD_NOEXEC_SEAL) +#else + #define YAMBAR_MFD_FLAGS (MFD_CLOEXEC | MFD_ALLOW_SEALING) +#endif + struct buffer { bool busy; size_t width; @@ -907,7 +913,7 @@ get_buffer(struct wayland_backend *backend) /* Backing memory for SHM */ #if defined(MEMFD_CREATE) - pool_fd = memfd_create("yambar-wayland-shm-buffer-pool", MFD_CLOEXEC); + pool_fd = memfd_create("yambar-wayland-shm-buffer-pool", YAMBAR_MFD_FLAGS); #elif defined(__FreeBSD__) // memfd_create on FreeBSD 13 is SHM_ANON without sealing support pool_fd = shm_open(SHM_ANON, O_RDWR | O_CLOEXEC, 0600); From 89e74139f59ce789a3c3686da5eba3e6a26f1c73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 13 Oct 2023 16:34:02 +0200 Subject: [PATCH 507/611] bar: wayland: shm: try with MFD_NOEXEC_SEAL first, then without MFD_NOEXEC_SEAL is only supported on kernels 6.3 and later. If we were compiled on linux >= 6.3, but run on linux < 6.3, we'd exit with an error, due to memfd_create() failing with EINVAL. This patch fixes the problem by first trying to call memfd_create() *with* MFD_NOEXEC_SEAL, and if that fails with EINVAL, we try again without it. --- bar/wayland.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/bar/wayland.c b/bar/wayland.c index eb770da..e95e032 100644 --- a/bar/wayland.c +++ b/bar/wayland.c @@ -28,10 +28,8 @@ #include "private.h" -#if defined(MFD_NOEXEC_SEAL) - #define YAMBAR_MFD_FLAGS (MFD_CLOEXEC | MFD_ALLOW_SEALING | MFD_NOEXEC_SEAL) -#else - #define YAMBAR_MFD_FLAGS (MFD_CLOEXEC | MFD_ALLOW_SEALING) +#if !defined(MFD_NOEXEC_SEAL) + #define MFD_NOEXEC_SEAL 0 #endif struct buffer { @@ -913,7 +911,19 @@ get_buffer(struct wayland_backend *backend) /* Backing memory for SHM */ #if defined(MEMFD_CREATE) - pool_fd = memfd_create("yambar-wayland-shm-buffer-pool", YAMBAR_MFD_FLAGS); + /* + * Older kernels reject MFD_NOEXEC_SEAL with EINVAL. Try first + * *with* it, and if that fails, try again *without* it. + */ + errno = 0; + pool_fd = memfd_create( + "yambar-wayland-shm-buffer-pool", + MFD_CLOEXEC | MFD_ALLOW_SEALING | MFD_NOEXEC_SEAL); + + if (pool_fd < 0 && errno == EINVAL) { + pool_fd = memfd_create( + "yambar-wayland-shm-buffer-pool", MFD_CLOEXEC | MFD_ALLOW_SEALING); + } #elif defined(__FreeBSD__) // memfd_create on FreeBSD 13 is SHM_ANON without sealing support pool_fd = shm_open(SHM_ANON, O_RDWR | O_CLOEXEC, 0600); From 14550440dde33328d94e0bec0929fe758cdc924f Mon Sep 17 00:00:00 2001 From: oob <> Date: Sun, 27 Aug 2023 12:48:56 +0000 Subject: [PATCH 508/611] Minor documentation update --- README.md | 3 ++- doc/meson.build | 1 + doc/yambar-modules-network.5.scd | 2 +- doc/yambar-modules.5.scd | 12 ++++++++++++ 4 files changed, 16 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 759f667..ca90ae9 100644 --- a/README.md +++ b/README.md @@ -87,6 +87,7 @@ Available modules: * mem * mpd * network +* pipewire * pulse * removables * river @@ -106,7 +107,7 @@ mkdir -p bld/release && cd bld/release Second, configure the build (if you intend to install it globally, you might also want `--prefix=/usr`): ```sh -meson --buildtype=release ../.. +meson setup --buildtype=release .. ``` Optionally, explicitly disable a backend (or enable, if you want a diff --git a/doc/meson.build b/doc/meson.build index 0b4e13a..e5728ab 100644 --- a/doc/meson.build +++ b/doc/meson.build @@ -36,6 +36,7 @@ if plugin_mpd_enabled endif if plugin_i3_enabled plugin_pages += ['yambar-modules-i3.5.scd'] + plugin_pages += ['yambar-modules-sway.5.scd'] endif if plugin_label_enabled plugin_pages += ['yambar-modules-label.5.scd'] diff --git a/doc/yambar-modules-network.5.scd b/doc/yambar-modules-network.5.scd index 8b8b507..2aefc89 100644 --- a/doc/yambar-modules-network.5.scd +++ b/doc/yambar-modules-network.5.scd @@ -73,7 +73,7 @@ address. : int : no : Periodically (in milliseconds) update the signal, rx+tx bitrate, and - ul+dl speed tags. Setting it to 0 disables updates. Cannot be less + ul+dl speed tags (default=0). Setting it to 0 disables updates. Cannot be less than 250ms. diff --git a/doc/yambar-modules.5.scd b/doc/yambar-modules.5.scd index e8c388c..765d06f 100644 --- a/doc/yambar-modules.5.scd +++ b/doc/yambar-modules.5.scd @@ -142,14 +142,26 @@ Available modules have their own pages: *yambar-modules-clock*(5) +*yambar-modules-cpu*(5) + +*yambar-modules-disk-io*(5) + +*yambar-modules-dwl*(5) + +*yambar-modules-foreign-toplevel*(5) + *yambar-modules-i3*(5) *yambar-modules-label*(5) +*yambar-modules-mem*(5) + *yambar-modules-mpd*(5) *yambar-modules-network*(5) +*yambar-modules-pipewire*(5) + *yambar-modules-pulse*(5) *yambar-modules-removables*(5) From 60671da2caef8a4b32f626021f5918aab6963160 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Hern=C3=A1ndez=20Hern=C3=A1ndez?= Date: Fri, 15 Dec 2023 22:36:51 -0600 Subject: [PATCH 509/611] lowercase DWL (dwl is the preferred form) --- doc/yambar-modules-dwl.5.scd | 2 +- meson.build | 2 +- meson_options.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/yambar-modules-dwl.5.scd b/doc/yambar-modules-dwl.5.scd index 289955a..1562e56 100644 --- a/doc/yambar-modules-dwl.5.scd +++ b/doc/yambar-modules-dwl.5.scd @@ -28,7 +28,7 @@ Running multiple instances at the same time may result in :< *Description* | id : int -: Dwl tag id. +: dwl tag id. | name : string : The name of the tag (defaults to _id_ if not set). diff --git a/meson.build b/meson.build index fcb647a..f0369cd 100644 --- a/meson.build +++ b/meson.build @@ -174,7 +174,7 @@ summary( 'Clock': plugin_clock_enabled, 'CPU monitoring': plugin_cpu_enabled, 'Disk I/O monitoring': plugin_disk_io_enabled, - 'DWL (dwm for Wayland)': plugin_dwl_enabled, + 'dwl (dwm for Wayland)': plugin_dwl_enabled, 'Foreign toplevel (window tracking for Wayland)': plugin_foreign_toplevel_enabled, 'Memory monitoring': plugin_mem_enabled, 'Music Player Daemon (MPD)': plugin_mpd_enabled, diff --git a/meson_options.txt b/meson_options.txt index 03c0ead..a9aac05 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -19,7 +19,7 @@ option('plugin-cpu', type: 'feature', value: 'auto', option('plugin-disk-io', type: 'feature', value: 'auto', description: 'Disk I/O support') option('plugin-dwl', type: 'feature', value: 'auto', - description: 'DWL (dwm for wayland) support') + description: 'dwl (dwm for wayland) support') option('plugin-foreign-toplevel', type: 'feature', value: 'auto', description: 'Foreign toplevel (window tracking for Wayland) support') option('plugin-mem', type: 'feature', value: 'auto', From 1283160e170737d9e71cfa69e8a709e2296d0e40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=A4in=C3=B6=20M=C3=A4kel=C3=A4?= Date: Fri, 29 Dec 2023 11:05:20 +0200 Subject: [PATCH 510/611] bar/wayland: Reset last_mapped_monitor on enter If the surface enters an output, there's no need for last_mapped_monitor, and it must be reset to fulfill the asserts in other parts of the code. This makes yambar no longer crash when it is hidden by an opaque window multiple times on a compositor using wlroots' scene tree. --- bar/wayland.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bar/wayland.c b/bar/wayland.c index e95e032..2b1e0a5 100644 --- a/bar/wayland.c +++ b/bar/wayland.c @@ -1339,6 +1339,9 @@ surface_enter(void *data, struct wl_surface *wl_surface, { struct wayland_backend *backend = data; + free(backend->last_mapped_monitor); + backend->last_mapped_monitor = NULL; + tll_foreach(backend->monitors, it) { struct monitor *mon = &it->item; From d5823bcc4c41af1a009559d3aaa1973cbe3184ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 3 Jan 2024 15:15:20 +0100 Subject: [PATCH 511/611] changelog: fixed: crash when hidden by an opaque window --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index faa616a..3c620f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ * Compiler error _‘fmt’ may be used uninitialized_ ([#311][311]). * map: conditions failing to match when they contain multiple, quoted tag values ([#302][302]). +* Crash when hidden by an opaque window. [311]: https://codeberg.org/dnkl/yambar/issues/311 [302]: https://codeberg.org/dnkl/yambar/issues/302 From 176ed4a6f38c4bd878915ef65cc07b7d45dec8de Mon Sep 17 00:00:00 2001 From: kotyk Date: Tue, 25 Jul 2023 16:22:52 +0000 Subject: [PATCH 512/611] particles/string: rewrite truncation code, show three dots only for max>3 --- particles/string.c | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/particles/string.c b/particles/string.c index e11ec45..186d50e 100644 --- a/particles/string.c +++ b/particles/string.c @@ -180,20 +180,10 @@ instantiate(const struct particle *particle, const struct tag_set *tags) /* Truncate, if necessary */ if (p->max_len > 0 && chars > p->max_len) { - size_t end = p->max_len; - if (end >= 1) { - /* "allocate" room for three dots at the end */ - end -= 1; - } - - if (p->max_len > 1) { - wtext[end] = U'…'; - wtext[end + 1] = U'\0'; - chars = end + 1; - } else { - wtext[end] = U'\0'; - chars = 0; - } + chars = p->max_len; + if (p->max_len > 3) + wtext[p->max_len - 1] = U'…'; + wtext[p->max_len] = U'\0'; } e->kern_x = calloc(chars, sizeof(e->kern_x[0])); From e54e8635e0bd29e73e437d2cb966b92dd399e39f Mon Sep 17 00:00:00 2001 From: Sertonix Date: Tue, 2 Jan 2024 13:28:40 +0100 Subject: [PATCH 513/611] main: change default log level to warning --- CHANGELOG.md | 3 +++ completions/zsh/_yambar | 2 +- doc/yambar.1.scd | 2 +- main.c | 4 ++-- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c620f2..8579644 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,9 @@ ## Unreleased ### Added ### Changed + +* log-level: default to `warning` + ### Deprecated ### Removed ### Fixed diff --git a/completions/zsh/_yambar b/completions/zsh/_yambar index 65b3100..90b1117 100644 --- a/completions/zsh/_yambar +++ b/completions/zsh/_yambar @@ -8,6 +8,6 @@ _arguments \ '(-c --config)'{-c,--config}'[alternative configuration file]:filename:_files' \ '(-C --validate)'{-C,--validate}'[verify configuration then quit]' \ '(-p --print-pid)'{-p,--print-pid}'[print PID to this file or FD when up and running]:pidfile:_files' \ - '(-d --log-level)'{-d,--log-level}'[log level (info)]:loglevel:(info warning error none)' \ + '(-d --log-level)'{-d,--log-level}'[log level (warning)]:loglevel:(info warning error none)' \ '(-l --log-colorize)'{-l,--log-colorize}'[enable or disable colorization of log output on stderr]:logcolor:(never always auto)' \ '(-s --log-no-syslog)'{-s,--log-no-syslog}'[disable syslog logging]' diff --git a/doc/yambar.1.scd b/doc/yambar.1.scd index a34f13c..549b980 100644 --- a/doc/yambar.1.scd +++ b/doc/yambar.1.scd @@ -29,7 +29,7 @@ yambar - modular status panel for X11 and Wayland *-d*,*--log-level*={*info*,*warning*,*error*,*none*} Log level, used both for log output on stderr as well as - syslog. Default: _info_. + syslog. Default: _warning_. *-l*,*--log-colorize*=[{*never*,*always*,*auto*}] Enables or disables colorization of log output on stderr. diff --git a/main.c b/main.c index 7aab81a..9604631 100644 --- a/main.c +++ b/main.c @@ -131,7 +131,7 @@ print_usage(const char *prog_name) " -c,--config=FILE alternative configuration file\n" " -C,--validate verify configuration then quit\n" " -p,--print-pid=FILE|FD print PID to file or FD\n" - " -d,--log-level={info|warning|error|none} log level (info)\n" + " -d,--log-level={info|warning|error|none} log level (warning)\n" " -l,--log-colorize=[never|always|auto] enable/disable colorization of log output on stderr\n" " -s,--log-no-syslog disable syslog logging\n" " -v,--version show the version number and quit\n"); @@ -197,7 +197,7 @@ main(int argc, char *const *argv) char *config_path = NULL; enum bar_backend backend = BAR_BACKEND_AUTO; - enum log_class log_level = LOG_CLASS_INFO; + enum log_class log_level = LOG_CLASS_WARNING; enum log_colorize log_colorize = LOG_COLORIZE_AUTO; bool log_syslog = true; From a943def94e5cc8194ab700fb1d13e37ea588dbbf Mon Sep 17 00:00:00 2001 From: Jordan Isaacs Date: Wed, 15 Nov 2023 01:37:51 -0800 Subject: [PATCH 514/611] battery scale and discharge smoothing --- doc/yambar-modules-battery.5.scd | 10 ++++ modules/battery.c | 98 +++++++++++++++++++++++++++----- 2 files changed, 95 insertions(+), 13 deletions(-) diff --git a/doc/yambar-modules-battery.5.scd b/doc/yambar-modules-battery.5.scd index ef7a6bd..99a7708 100644 --- a/doc/yambar-modules-battery.5.scd +++ b/doc/yambar-modules-battery.5.scd @@ -61,6 +61,16 @@ the state *unknown* under other conditions. (default=*60000*). Set to `0` to disable polling (*warning*: many batteries do not support asynchronous reporting). Cannot be less than 250ms. +| battery-scale +: int +: no +: How much to scale down the battery charge amount. Some batteries + report too high resulting in bad discharge estimates +| smoothing-secs +: int +: no +: How many seconds to perform smoothing over for battery discharge + estimates. # EXAMPLES diff --git a/modules/battery.c b/modules/battery.c index f838c8e..1ea0f58 100644 --- a/modules/battery.c +++ b/modules/battery.c @@ -5,6 +5,7 @@ #include #include #include +#include #include @@ -12,9 +13,10 @@ #include #include +#include #define LOG_MODULE "battery" -#define LOG_ENABLE_DBG 1 +#define LOG_ENABLE_DBG 0 #include "../log.h" #include "../bar/bar.h" #include "../config.h" @@ -25,13 +27,22 @@ static const long min_poll_interval = 250; static const long default_poll_interval = 60 * 1000; +static const long one_sec_in_ns = 1000000000; enum state { STATE_FULL, STATE_NOTCHARGING, STATE_CHARGING, STATE_DISCHARGING, STATE_UNKNOWN }; +struct current_state { + long ema; + long current; + struct timespec time; +}; + struct private { struct particle *label; long poll_interval; + int battery_scale; + long smoothing_scale; char *battery; char *manufacturer; char *model; @@ -45,16 +56,53 @@ struct private { long energy; long power; long charge; - long current; + struct current_state ema_current; long time_to_empty; long time_to_full; }; +int64_t difftimespec_ns(const struct timespec after, const struct timespec before) +{ + return ((int64_t)after.tv_sec - (int64_t)before.tv_sec) * (int64_t)one_sec_in_ns + + ((int64_t)after.tv_nsec - (int64_t)before.tv_nsec); +} + +// Linear Exponential Moving Average (unevenly spaced time series) +// http://www.eckner.com/papers/Algorithms%20for%20Unevenly%20Spaced%20Time%20Series.pdf +// Adapted from: https://github.com/andreas50/utsAlgorithms/blob/master/ema.c +void +ema_linear(struct current_state *state, struct current_state curr, long tau) +{ + double w, w2, tmp; + + if (state->current == -1) { + *state = curr; + return; + } + + long time = difftimespec_ns(curr.time, state->time); + tmp = time / (double)tau; + w = exp(-tmp); + if (tmp > 1e-6) { + w2 = (1 - w) / tmp; + } else { + // Use taylor expansion for numerical stability + w2 = 1 - tmp/2 + tmp*tmp/6 - tmp*tmp*tmp/24; + } + + double ema = state->ema * w + curr.current * (1 - w2) + state->current * (w2 - w); + + state->ema = ema; + state->current = curr.current; + state->time = curr.time; + + LOG_DBG("ema current: %ld", (long)ema); +} + static void timespec_sub(const struct timespec *a, const struct timespec *b, struct timespec *res) { - const long one_sec_in_ns = 1000000000; res->tv_sec = a->tv_sec - b->tv_sec; res->tv_nsec = a->tv_nsec - b->tv_nsec; @@ -127,15 +175,15 @@ content(struct module *mod) hours = hours_as_float; minutes = (hours_as_float - (double)hours) * 60; - } else if (m->charge_full >= 0 && m->charge >= 0 && m->current >= 0) { + } else if (m->charge_full >= 0 && m->charge >= 0 && m->ema_current.current >= 0) { unsigned long charge = m->state == STATE_CHARGING ? m->charge_full - m->charge : m->charge; double hours_as_float; if (m->state == STATE_FULL || m->state == STATE_NOTCHARGING) hours_as_float = 0.0; - else if (m->current > 0) - hours_as_float = (double)charge / m->current; + else if (m->ema_current.current > 0) + hours_as_float = (double)charge / m->ema_current.current; else hours_as_float = 99.0; @@ -291,7 +339,7 @@ initialize(struct private *m) goto err; } - m->charge_full_design = readint_from_fd(fd); + m->charge_full_design = readint_from_fd(fd) / m->battery_scale; close(fd); } @@ -302,7 +350,7 @@ initialize(struct private *m) goto err; } - m->charge_full = readint_from_fd(fd); + m->charge_full = readint_from_fd(fd) / m->battery_scale; close(fd); } } else { @@ -366,6 +414,10 @@ update_status(struct module *mod) long time_to_empty = time_to_empty_fd >= 0 ? readint_from_fd(time_to_empty_fd) : -1; long time_to_full = time_to_full_fd >= 0 ? readint_from_fd(time_to_full_fd) : -1; + if (charge >= -1) { + charge /= m->battery_scale; + } + char buf[512]; const char *status = readline_from_fd(status_fd, sizeof(buf), buf); @@ -409,16 +461,23 @@ update_status(struct module *mod) } LOG_DBG("capacity: %ld, energy: %ld, power: %ld, charge=%ld, current=%ld, " - "time-to-empty: %ld, time-to-full: %ld", capacity, energy, power, - charge, current, time_to_empty, time_to_full); + "time-to-empty: %ld, time-to-full: %ld", capacity, + energy, power, charge, current, time_to_empty, time_to_full); mtx_lock(&mod->lock); + if (m->state != state) { + m->ema_current = (struct current_state){-1, 0, (struct timespec){0, 0}}; + } m->state = state; m->capacity = capacity; m->energy = energy; m->power = power; m->charge = charge; - m->current = current; + if (current != -1) { + struct timespec t; + clock_gettime(CLOCK_MONOTONIC, &t); + ema_linear(&m->ema_current, (struct current_state){current, current, t}, m->smoothing_scale); + } m->time_to_empty = time_to_empty; m->time_to_full = time_to_full; mtx_unlock(&mod->lock); @@ -548,13 +607,16 @@ out: } static struct module * -battery_new(const char *battery, struct particle *label, long poll_interval_msecs) +battery_new(const char *battery, struct particle *label, long poll_interval_msecs, int battery_scale, long smoothing_secs) { struct private *m = calloc(1, sizeof(*m)); m->label = label; m->poll_interval = poll_interval_msecs; + m->battery_scale = battery_scale; + m->smoothing_scale = smoothing_secs * one_sec_in_ns; m->battery = strdup(battery); m->state = STATE_UNKNOWN; + m->ema_current = (struct current_state){ -1, 0, (struct timespec){0, 0} }; struct module *mod = module_common_new(); mod->private = m; @@ -571,13 +633,21 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) const struct yml_node *c = yml_get_value(node, "content"); const struct yml_node *name = yml_get_value(node, "name"); const struct yml_node *poll_interval = yml_get_value(node, "poll-interval"); + const struct yml_node *battery_scale = yml_get_value(node, "battery-scale"); + const struct yml_node *smoothing_secs = yml_get_value(node, "smoothing-secs"); return battery_new( yml_value_as_string(name), conf_to_particle(c, inherited), (poll_interval != NULL ? yml_value_as_int(poll_interval) - : default_poll_interval)); + : default_poll_interval), + (battery_scale != NULL + ? yml_value_as_int(battery_scale) + : 1), + (smoothing_secs != NULL + ? yml_value_as_int(smoothing_secs) + : 100)); } static bool @@ -603,6 +673,8 @@ verify_conf(keychain_t *chain, const struct yml_node *node) static const struct attr_info attrs[] = { {"name", true, &conf_verify_string}, {"poll-interval", false, &conf_verify_poll_interval}, + {"battery-scale", false, &conf_verify_unsigned}, + {"smoothing-secs", false, &conf_verify_unsigned}, MODULE_COMMON_ATTRS, }; From 4d46f258547917847e677ee0535469611df5bb94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 3 Jan 2024 15:28:32 +0100 Subject: [PATCH 515/611] doc: battery: document defaults for battery-scale and smoothing-secs --- doc/yambar-modules-battery.5.scd | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/yambar-modules-battery.5.scd b/doc/yambar-modules-battery.5.scd index 99a7708..aab2106 100644 --- a/doc/yambar-modules-battery.5.scd +++ b/doc/yambar-modules-battery.5.scd @@ -65,12 +65,12 @@ the state *unknown* under other conditions. : int : no : How much to scale down the battery charge amount. Some batteries - report too high resulting in bad discharge estimates + report too high resulting in bad discharge estimates. Default=1. | smoothing-secs : int : no : How many seconds to perform smoothing over for battery discharge - estimates. + estimates. Default=100s. # EXAMPLES From 3a3a711b698bb855df62f0ac7ef259f8c2324169 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 3 Jan 2024 15:29:06 +0100 Subject: [PATCH 516/611] changelog: battery: smoothing + scaling --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8579644..85a7469 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,12 @@ ## Unreleased ### Added + +* battery: current smoothing, for improved discharge estimates. +* battery: scale option, for batteries that report 'charge' at a + different scale than 'current'. + + ### Changed * log-level: default to `warning` From 9365580539bee8cbeee43e212f055cd7313c1104 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 3 Jan 2024 15:30:03 +0100 Subject: [PATCH 517/611] module: battery: style --- modules/battery.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/battery.c b/modules/battery.c index 1ea0f58..c947649 100644 --- a/modules/battery.c +++ b/modules/battery.c @@ -61,7 +61,8 @@ struct private { long time_to_full; }; -int64_t difftimespec_ns(const struct timespec after, const struct timespec before) +static int64_t +difftimespec_ns(const struct timespec after, const struct timespec before) { return ((int64_t)after.tv_sec - (int64_t)before.tv_sec) * (int64_t)one_sec_in_ns + ((int64_t)after.tv_nsec - (int64_t)before.tv_nsec); @@ -70,7 +71,7 @@ int64_t difftimespec_ns(const struct timespec after, const struct timespec befor // Linear Exponential Moving Average (unevenly spaced time series) // http://www.eckner.com/papers/Algorithms%20for%20Unevenly%20Spaced%20Time%20Series.pdf // Adapted from: https://github.com/andreas50/utsAlgorithms/blob/master/ema.c -void +static void ema_linear(struct current_state *state, struct current_state curr, long tau) { double w, w2, tmp; From cdee55afed6eee4e2618926d096d1322b58e2962 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 3 Jan 2024 15:39:04 +0100 Subject: [PATCH 518/611] bar: wayland: update bar size + refresh in output_done() This ensures the bar's size (width) is updated when the screen resolution (and/or scale) is changed. Note that we already handled scale changes. This logic has been moved from output_scale() to output_done(). Closes #330 --- CHANGELOG.md | 3 +++ bar/wayland.c | 28 ++++++++++++++-------------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 85a7469..01bd423 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,9 +31,12 @@ * map: conditions failing to match when they contain multiple, quoted tag values ([#302][302]). * Crash when hidden by an opaque window. +* Bar not resizing itself when the screen resolution is changed + ([#330][330]). [311]: https://codeberg.org/dnkl/yambar/issues/311 [302]: https://codeberg.org/dnkl/yambar/issues/302 +[330]: https://codeberg.org/dnkl/yambar/issues/330 ### Security diff --git a/bar/wayland.c b/bar/wayland.c index 2b1e0a5..22ca3e2 100644 --- a/bar/wayland.c +++ b/bar/wayland.c @@ -475,13 +475,24 @@ output_mode(void *data, struct wl_output *wl_output, uint32_t flags, { } +static bool update_size(struct wayland_backend *backend); +static void refresh(const struct bar *_bar); + static void output_done(void *data, struct wl_output *wl_output) { -} + struct monitor *mon = data; -static bool update_size(struct wayland_backend *backend); -static void refresh(const struct bar *_bar); + if (mon->backend->monitor == mon) { + int old_scale = mon->backend->scale; + int old_width = mon->backend->width; + + update_size(mon->backend); + + if (mon->backend->scale != old_scale || mon->backend->width != old_width) + refresh(mon->backend->bar); + } +} static void output_scale(void *data, struct wl_output *wl_output, int32_t factor) @@ -491,14 +502,6 @@ output_scale(void *data, struct wl_output *wl_output, int32_t factor) return; mon->scale = factor; - - if (mon->backend->monitor == mon) { - int old_scale = mon->backend->scale; - update_size(mon->backend); - - if (mon->backend->scale != old_scale) - refresh(mon->backend->bar); - } } #if defined(WL_OUTPUT_NAME_SINCE_VERSION) @@ -1058,9 +1061,6 @@ update_size(struct wayland_backend *backend) assert(backend->surface != NULL); - if (backend->scale == scale) - return true; - backend->scale = scale; int height = bar->height_with_border; From 26bf62a8996801b32dc4c541ef6fc02158d16e60 Mon Sep 17 00:00:00 2001 From: rdbo Date: Thu, 4 Jan 2024 08:21:05 +0000 Subject: [PATCH 519/611] fixed meson setup directory on readme Signed-off-by: rdbo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ca90ae9..2887f53 100644 --- a/README.md +++ b/README.md @@ -107,7 +107,7 @@ mkdir -p bld/release && cd bld/release Second, configure the build (if you intend to install it globally, you might also want `--prefix=/usr`): ```sh -meson setup --buildtype=release .. +meson setup --buildtype=release ../.. ``` Optionally, explicitly disable a backend (or enable, if you want a From e1f78a16ab4649fe0f9d4000463e5fc271927443 Mon Sep 17 00:00:00 2001 From: Delgan Date: Wed, 3 Jan 2024 16:21:49 +0100 Subject: [PATCH 520/611] Add new "quality" tag to "network" module --- CHANGELOG.md | 1 + doc/yambar-modules-network.5.scd | 5 ++++- modules/network.c | 13 ++++++++++++- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 01bd423..38f2c57 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ * battery: current smoothing, for improved discharge estimates. * battery: scale option, for batteries that report 'charge' at a different scale than 'current'. +* network: new `quality` tag (Wi-Fi only). ### Changed diff --git a/doc/yambar-modules-network.5.scd b/doc/yambar-modules-network.5.scd index 2aefc89..44605e2 100644 --- a/doc/yambar-modules-network.5.scd +++ b/doc/yambar-modules-network.5.scd @@ -45,6 +45,9 @@ address. | signal : int : Signal strength, in dBm (Wi-Fi only) +| quality +: range +: Quality of the signal, in percent (Wi-Fi only) | rx-bitrate : int : RX bitrate, in bits/s @@ -72,7 +75,7 @@ address. | poll-interval : int : no -: Periodically (in milliseconds) update the signal, rx+tx bitrate, and +: Periodically (in milliseconds) update the signal, quality, rx+tx bitrate, and ul+dl speed tags (default=0). Setting it to 0 disables updates. Cannot be less than 250ms. diff --git a/modules/network.c b/modules/network.c index 0cc8cb6..3336c23 100644 --- a/modules/network.c +++ b/modules/network.c @@ -154,6 +154,16 @@ content(struct module *mod) inet_ntop(AF_INET6, &it->item.addr.ipv6, ipv6_str, sizeof(ipv6_str)); } + int quality = 0; + if (m->signal_strength_dbm != 0) { + if (m->signal_strength_dbm <= -100) + quality = 0; + else if (m->signal_strength_dbm >= -50) + quality = 100; + else + quality = 2 * (m->signal_strength_dbm + 100); + } + struct tag_set tags = { .tags = (struct tag *[]){ tag_new_string(mod, "name", m->iface), @@ -165,12 +175,13 @@ content(struct module *mod) tag_new_string(mod, "ipv6", ipv6_str), tag_new_string(mod, "ssid", m->ssid), tag_new_int(mod, "signal", m->signal_strength_dbm), + tag_new_int_range(mod, "quality", quality, 0, 100), tag_new_int(mod, "rx-bitrate", m->rx_bitrate), tag_new_int(mod, "tx-bitrate", m->tx_bitrate), tag_new_float(mod, "dl-speed", m->dl_speed), tag_new_float(mod, "ul-speed", m->ul_speed), }, - .count = 13, + .count = 14, }; mtx_unlock(&mod->lock); From bdc4fbe8e71491c16a7b22a9373bec1015b9b4e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 4 Jan 2024 13:57:17 +0100 Subject: [PATCH 521/611] doc: pipewire: describe the 'content' config option --- doc/yambar-modules-pipewire.5.scd | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/doc/yambar-modules-pipewire.5.scd b/doc/yambar-modules-pipewire.5.scd index 0f7a40b..be94489 100644 --- a/doc/yambar-modules-pipewire.5.scd +++ b/doc/yambar-modules-pipewire.5.scd @@ -39,8 +39,16 @@ pipewire - Monitors pipewire for volume, mute/unmute, device change # CONFIGURATION -No additional attributes supported, only the generic ones (see -*GENERIC CONFIGURATION* in *yambar-modules*(5)) +[[ *Name* +:[ *Type* +:[ *Req* +:< *Description* +| content +: particle +: yes +: Unlike other modules, _content_ is a template particle that will be + expanded twice (i.e. into a list of two elements). The first + element is the 'sink', and the second element the 'source'. # EXAMPLES From 8b1fa136867cb9af78e0767974f246cfb2977a9d Mon Sep 17 00:00:00 2001 From: steovd Date: Wed, 3 Jan 2024 17:25:46 +0100 Subject: [PATCH 522/611] main: allow reading alternative config from pipe --- CHANGELOG.md | 2 ++ main.c | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 38f2c57..72f7eb7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,8 @@ ### Changed * log-level: default to `warning` +* Read alternative config from pipes (e.g. `--config /dev/stdin`) + ([#340][340]). ### Deprecated ### Removed diff --git a/main.c b/main.c index 9604631..3a8b9f7 100644 --- a/main.c +++ b/main.c @@ -223,8 +223,8 @@ main(int argc, char *const *argv) if (stat(optarg, &st) == -1) { fprintf(stderr, "%s: invalid configuration file: %s\n", optarg, strerror(errno)); return EXIT_FAILURE; - } else if (!S_ISREG(st.st_mode)) { - fprintf(stderr, "%s: invalid configuration file: not a regular file\n", + } else if (!S_ISREG(st.st_mode) && !S_ISFIFO(st.st_mode)) { + fprintf(stderr, "%s: invalid configuration file: neither a regular file nor a pipe\n", optarg); return EXIT_FAILURE; } From cb2561a72cd14afa6d49a9cd2fb3c8984b251c2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 4 Jan 2024 16:33:59 +0100 Subject: [PATCH 523/611] changelog: move 'reading config from pipe' from changed to added --- CHANGELOG.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 72f7eb7..ef54b1d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,13 +18,15 @@ * battery: scale option, for batteries that report 'charge' at a different scale than 'current'. * network: new `quality` tag (Wi-Fi only). +* Read alternative config from pipes (e.g. `--config /dev/stdin`) + ([#340][340]). + +[340]: https://codeberg.org/dnkl/yambar/pulls/340 ### Changed * log-level: default to `warning` -* Read alternative config from pipes (e.g. `--config /dev/stdin`) - ([#340][340]). ### Deprecated ### Removed From bbbf2b601e2f0bf9676ff300861803711ce5c44c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 4 Jan 2024 16:35:05 +0100 Subject: [PATCH 524/611] main: S_ISFIFO() matches both pipes and FIFOs --- CHANGELOG.md | 4 ++-- main.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ef54b1d..4e18535 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,8 +18,8 @@ * battery: scale option, for batteries that report 'charge' at a different scale than 'current'. * network: new `quality` tag (Wi-Fi only). -* Read alternative config from pipes (e.g. `--config /dev/stdin`) - ([#340][340]). +* Read alternative config from pipes and FIFOs (e.g. `--config + /dev/stdin`) ([#340][340]). [340]: https://codeberg.org/dnkl/yambar/pulls/340 diff --git a/main.c b/main.c index 3a8b9f7..5d9df44 100644 --- a/main.c +++ b/main.c @@ -224,7 +224,7 @@ main(int argc, char *const *argv) fprintf(stderr, "%s: invalid configuration file: %s\n", optarg, strerror(errno)); return EXIT_FAILURE; } else if (!S_ISREG(st.st_mode) && !S_ISFIFO(st.st_mode)) { - fprintf(stderr, "%s: invalid configuration file: neither a regular file nor a pipe\n", + fprintf(stderr, "%s: invalid configuration file: neither a regular file nor a pipe or FIFO\n", optarg); return EXIT_FAILURE; } From 9d90848291e37f8c97c2c175059fa0806421d1c6 Mon Sep 17 00:00:00 2001 From: Yiyu Zhou Date: Sat, 13 Jan 2024 00:24:58 -0500 Subject: [PATCH 525/611] style: remove trailing spaces --- examples/configurations/river-tags.conf | 18 +++++++++--------- examples/scripts/dwl-tags.sh | 1 - examples/scripts/pacman.sh | 11 +++++------ 3 files changed, 14 insertions(+), 16 deletions(-) diff --git a/examples/configurations/river-tags.conf b/examples/configurations/river-tags.conf index 54289e6..28bc0b9 100644 --- a/examples/configurations/river-tags.conf +++ b/examples/configurations/river-tags.conf @@ -15,20 +15,20 @@ bar: anchors: - base: &river_base left-margin: 10 - right-margin: 13 + right-margin: 13 default: {string: {text: , font: *hack}} conditions: - id == 1: {string: {text: ﳐ, font: *hack}} - id == 2: {string: {text: , font: *hack}} - id == 3: {string: {text: , font: *hack}} - id == 4: {string: {text: , font: *hack}} - id == 5: {string: {text: , font: *hack}} - id == 10: {string: {text: "scratchpad", font: *hack}} - id == 11: {string: {text: "work", font: *hack}} + id == 1: {string: {text: ﳐ, font: *hack}} + id == 2: {string: {text: , font: *hack}} + id == 3: {string: {text: , font: *hack}} + id == 4: {string: {text: , font: *hack}} + id == 5: {string: {text: , font: *hack}} + id == 10: {string: {text: "scratchpad", font: *hack}} + id == 11: {string: {text: "work", font: *hack}} content: map: - on-click: + on-click: left: sh -c "riverctl set-focused-tags $((1 << ({id} - 1)))" right: sh -c "riverctl toggle-focused-tags $((1 << ({id} -1)))" middle: sh -c "riverctl toggle-view-tags $((1 << ({id} -1)))" diff --git a/examples/scripts/dwl-tags.sh b/examples/scripts/dwl-tags.sh index 7b70fff..b1dbe4c 100755 --- a/examples/scripts/dwl-tags.sh +++ b/examples/scripts/dwl-tags.sh @@ -136,4 +136,3 @@ done unset -v output title layout activetags selectedtags unset -v tags name - diff --git a/examples/scripts/pacman.sh b/examples/scripts/pacman.sh index a04b7a9..83e2a3f 100755 --- a/examples/scripts/pacman.sh +++ b/examples/scripts/pacman.sh @@ -15,13 +15,13 @@ # Exemples configuration: # - script: # path: /absolute/path/to/pacman.sh -# args: [] +# args: [] # content: { string: { text: "{pacman} + {aur} = {pkg}" } } # # To display a message when there is no update: # - script: # path: /absolute/path/to/pacman.sh -# args: [] +# args: [] # content: # map: # default: { string: { text: "{pacman} + {aur} = {pkg}" } } @@ -47,9 +47,9 @@ while true; do # Change interval # NUMBER[SUFFIXE] # Possible suffix: - # "s" seconds / "m" minutes / "h" hours / "d" days + # "s" seconds / "m" minutes / "h" hours / "d" days interval="1h" - + # Change your aur manager aur_helper="paru" @@ -62,7 +62,7 @@ while true; do else aur_num=$("${aur_helper}" -Qmu | wc -l) fi - + pkg_num=$(( pacman_num + aur_num )) printf -- '%s\n' "pacman|int|${pacman_num}" @@ -76,4 +76,3 @@ done unset -v interval aur_helper pacman_num aur_num pkg_num unset -f _err - From 195ac5d1cd0aad9b96f06347d9e7d374e91d099b Mon Sep 17 00:00:00 2001 From: Delgan Date: Sat, 27 Jan 2024 21:12:08 +0100 Subject: [PATCH 526/611] Fix incorrect empty/title state of i3 workspaces --- CHANGELOG.md | 2 ++ modules/i3.c | 41 ++++++++++++++++------------------------- 2 files changed, 18 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e18535..c75d270 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,10 +38,12 @@ * Crash when hidden by an opaque window. * Bar not resizing itself when the screen resolution is changed ([#330][330]). +* i3/sway: incorrect empty/title state of workspaces ([#343][343]). [311]: https://codeberg.org/dnkl/yambar/issues/311 [302]: https://codeberg.org/dnkl/yambar/issues/302 [330]: https://codeberg.org/dnkl/yambar/issues/330 +[343]: https://codeberg.org/dnkl/yambar/issues/343 ### Security diff --git a/modules/i3.c b/modules/i3.c index 65ee332..a5af4f8 100644 --- a/modules/i3.c +++ b/modules/i3.c @@ -569,12 +569,11 @@ handle_window_event(int sock, int type, const struct json_object *json, void *_m } const char *change_str = json_object_get_string(change); - bool is_new = strcmp(change_str, "new") == 0; bool is_focus = strcmp(change_str, "focus") == 0; bool is_close = strcmp(change_str, "close") == 0; bool is_title = strcmp(change_str, "title") == 0; - if (!is_new && !is_focus && !is_close && !is_title) + if (!is_focus && !is_close && !is_title) return true; mtx_lock(&mod->lock); @@ -591,27 +590,6 @@ handle_window_event(int sock, int type, const struct json_object *json, void *_m assert(focused == 1); assert(ws != NULL); - if (is_close) { - free(ws->window.title); - free(ws->window.application); - - ws->window.id = -1; - ws->window.title = ws->window.application = NULL; - ws->window.pid = -1; - - /* May not be true, but e.g. a subsequent “focus” event will - * reset it... */ - ws->empty = true; - - m->dirty = true; - mtx_unlock(&mod->lock); - return true; - - } - - /* Non-close event - thus workspace cannot be empty */ - ws->empty = false; - struct json_object *container, *id, *name; if (!json_object_object_get_ex(json, "container", &container) || !json_object_object_get_ex(container, "id", &id) || @@ -622,8 +600,21 @@ handle_window_event(int sock, int type, const struct json_object *json, void *_m return false; } - if (is_title && ws->window.id != json_object_get_int(id)) { - /* Ignore title changed event if it's not current window */ + if ((is_close || is_title) && ws->window.id != json_object_get_int(id)) { + /* Ignore close event and title changed event if it's not current window */ + mtx_unlock(&mod->lock); + return true; + } + + if (is_close) { + free(ws->window.title); + free(ws->window.application); + + ws->window.id = -1; + ws->window.title = ws->window.application = NULL; + ws->window.pid = -1; + + m->dirty = true; mtx_unlock(&mod->lock); return true; } From aeeef4f236f838becca52acea6d5797fdcf61e11 Mon Sep 17 00:00:00 2001 From: Delgan Date: Sun, 4 Feb 2024 16:11:35 +0100 Subject: [PATCH 527/611] Fix "mem" values updated while it should not Closes #352 --- CHANGELOG.md | 2 ++ modules/mem.c | 21 ++++++++++++++------- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c75d270..dd2bfff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,11 +39,13 @@ * Bar not resizing itself when the screen resolution is changed ([#330][330]). * i3/sway: incorrect empty/title state of workspaces ([#343][343]). +* mem: state updated on each bar redraw ([#352][352]). [311]: https://codeberg.org/dnkl/yambar/issues/311 [302]: https://codeberg.org/dnkl/yambar/issues/302 [330]: https://codeberg.org/dnkl/yambar/issues/330 [343]: https://codeberg.org/dnkl/yambar/issues/343 +[352]: https://codeberg.org/dnkl/yambar/issues/352 ### Security diff --git a/modules/mem.c b/modules/mem.c index 3a2eda7..6a196c6 100644 --- a/modules/mem.c +++ b/modules/mem.c @@ -24,6 +24,8 @@ struct private { struct particle *label; uint16_t interval; + uint64_t mem_free; + uint64_t mem_total; }; static void @@ -79,15 +81,12 @@ static struct exposable * content(struct module *mod) { const struct private *p = mod->private; - uint64_t mem_free = 0; - uint64_t mem_used = 0; - uint64_t mem_total = 0; - if (!get_mem_stats(&mem_free, &mem_total)) { - LOG_ERR("unable to retrieve the memory stats"); - } + mtx_lock(&mod->lock); - mem_used = mem_total - mem_free; + const uint64_t mem_free = p->mem_free; + const uint64_t mem_total = p->mem_total; + const uint64_t mem_used = mem_total - mem_free; double percent_used = ((double)mem_used * 100) / (mem_total + 1); double percent_free = ((double)mem_free * 100) / (mem_total + 1); @@ -102,6 +101,7 @@ content(struct module *mod) struct exposable *exposable = p->label->instantiate(p->label, &tags); tag_set_destroy(&tags); + mtx_unlock(&mod->lock); return exposable; } @@ -127,6 +127,13 @@ run(struct module *mod) if (fds[0].revents & POLLIN) break; + mtx_lock(&mod->lock); + p->mem_free = 0; + p->mem_total = 0; + if (!get_mem_stats(&p->mem_free, &p->mem_total)) { + LOG_ERR("unable to retrieve the memory stats"); + } + mtx_unlock(&mod->lock); bar->refresh(bar); } From f2d25c8341d4d24bc3444d1719c7dce051e2702f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 5 Feb 2024 12:52:40 +0100 Subject: [PATCH 528/611] script: fix buffer resize bug If the amount of data coming in is more than we can hold in our buffer, we resized the buffer by doubling its size. However, there were two(!) issues here: * If this was the first resize, the buffer size was set to 1024. This may not be enough (i.e. there may be more than 1024 bytes to process). * In all other cases, the buffer size was doubled. However, there is still no guarantee the buffer is large enough. Fix by looping until the buffer *is* large enough. --- CHANGELOG.md | 1 + modules/script.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dd2bfff..f1d6b0b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,7 @@ ([#330][330]). * i3/sway: incorrect empty/title state of workspaces ([#343][343]). * mem: state updated on each bar redraw ([#352][352]). +* script: buffer overflow when reading large amounts of data. [311]: https://codeberg.org/dnkl/yambar/issues/311 [302]: https://codeberg.org/dnkl/yambar/issues/302 diff --git a/modules/script.c b/modules/script.c index 63928a6..0932cb2 100644 --- a/modules/script.c +++ b/modules/script.c @@ -298,7 +298,7 @@ data_received(struct module *mod, const char *data, size_t len) { struct private *m = mod->private; - if (len > m->recv_buf.sz - m->recv_buf.idx) { + while (len > m->recv_buf.sz - m->recv_buf.idx) { size_t new_sz = m->recv_buf.sz == 0 ? 1024 : m->recv_buf.sz * 2; char *new_buf = realloc(m->recv_buf.data, new_sz); From 89ae7bd74338206d8f14bb03082061275c76dd99 Mon Sep 17 00:00:00 2001 From: Haden Collins Date: Thu, 14 Mar 2024 16:14:53 -0500 Subject: [PATCH 529/611] Handle `reload` workspace events from sway correctly Closes #361 --- modules/i3.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/modules/i3.c b/modules/i3.c index a5af4f8..d62db5e 100644 --- a/modules/i3.c +++ b/modules/i3.c @@ -418,10 +418,12 @@ handle_workspace_event(int sock, int type, const struct json_object *json, void bool is_rename = strcmp(change_str, "rename") == 0; bool is_move = strcmp(change_str, "move") == 0; bool is_urgent = strcmp(change_str, "urgent") == 0; + bool is_reload = strcmp(change_str, "reload") == 0; struct json_object *current, *_current_id; - if (!json_object_object_get_ex(json, "current", ¤t) || - !json_object_object_get_ex(current, "id", &_current_id)) + if ((!json_object_object_get_ex(json, "current", ¤t) || + !json_object_object_get_ex(current, "id", &_current_id)) && + !is_reload) { LOG_ERR("workspace event without 'current' and/or 'id' properties"); return false; @@ -543,6 +545,12 @@ handle_workspace_event(int sock, int type, const struct json_object *json, void w->urgent = json_object_get_boolean(urgent); } + else if (is_reload) { + /* Schedule full update to check if anything was changed + * during reload */ + i3_send_pkg(sock, I3_IPC_MESSAGE_TYPE_GET_WORKSPACES, NULL); + } + else { LOG_WARN("unimplemented workspace event '%s'", change_str); } From 7a5cf26fb502dc4b9d638062bf2eab67a3a48262 Mon Sep 17 00:00:00 2001 From: Haden Collins Date: Sat, 16 Mar 2024 10:00:24 -0500 Subject: [PATCH 530/611] Add changelog entry --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f1d6b0b..7b66586 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,12 +41,14 @@ * i3/sway: incorrect empty/title state of workspaces ([#343][343]). * mem: state updated on each bar redraw ([#352][352]). * script: buffer overflow when reading large amounts of data. +* i3/sway: module fails when reloading config file ([#361][361]). [311]: https://codeberg.org/dnkl/yambar/issues/311 [302]: https://codeberg.org/dnkl/yambar/issues/302 [330]: https://codeberg.org/dnkl/yambar/issues/330 [343]: https://codeberg.org/dnkl/yambar/issues/343 [352]: https://codeberg.org/dnkl/yambar/issues/352 +[361]: https://codeberg.org/dnkl/yambar/issues/361 ### Security From 424f22ab84893c78f2865d70f6ba0604b39a1164 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 18 Mar 2024 16:39:34 +0100 Subject: [PATCH 531/611] ci: rename .woodpecker.yml -> .woodpecker.yaml --- .woodpecker.yml => .woodpecker.yaml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .woodpecker.yml => .woodpecker.yaml (100%) diff --git a/.woodpecker.yml b/.woodpecker.yaml similarity index 100% rename from .woodpecker.yml rename to .woodpecker.yaml From 4066326614a7fca98cf09f6dd2768e9949954c72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 18 Mar 2024 16:40:25 +0100 Subject: [PATCH 532/611] ci: sync with woodpecker 2.x changes --- .woodpecker.yaml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.woodpecker.yaml b/.woodpecker.yaml index 0b0cd6a..3ebe51d 100644 --- a/.woodpecker.yaml +++ b/.woodpecker.yaml @@ -1,5 +1,5 @@ steps: - codespell: + - name: codespell when: branch: - master @@ -11,7 +11,7 @@ steps: - pip install codespell - codespell README.md CHANGELOG.md *.c *.h doc/*.scd - subprojects: + - name: subprojects when: branch: - master @@ -24,12 +24,12 @@ steps: - git clone https://codeberg.org/dnkl/fcft.git - cd .. - x64: + - name: x64 when: branch: - master - releases/* - group: build + depends_on: [subprojects] image: alpine:latest commands: - apk update @@ -83,12 +83,12 @@ steps: - ./yambar --version - cd ../.. - x86: + - name: x86 when: branch: - master - releases/* - group: build + depends_on: [subprojects] image: i386/alpine:latest commands: - apk add musl-dev eudev-libs eudev-dev linux-headers meson ninja gcc scdoc From c19a31d9253f1975b9111490e28a4533b17ba420 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 18 Mar 2024 16:45:41 +0100 Subject: [PATCH 533/611] ci: install and run codespell in/from a venv --- .woodpecker.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.woodpecker.yaml b/.woodpecker.yaml index 3ebe51d..ca65b29 100644 --- a/.woodpecker.yaml +++ b/.woodpecker.yaml @@ -8,8 +8,11 @@ steps: commands: - apk add python3 - apk add py3-pip + - python3 -m venv codespell-venv + - source codespell-venv/bin/activate - pip install codespell - codespell README.md CHANGELOG.md *.c *.h doc/*.scd + - deactivate - name: subprojects when: From 4e07b63cefdaa8b623ce7920e317ed8717d7c089 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 18 Mar 2024 16:42:45 +0100 Subject: [PATCH 534/611] plugin: workaround gcc bug that triggers a compilation error GCC thinks str2type() returns NULL when it doesn't. Closes #350 --- CHANGELOG.md | 2 ++ plugin.c | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b66586..e31c0c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,6 +42,7 @@ * mem: state updated on each bar redraw ([#352][352]). * script: buffer overflow when reading large amounts of data. * i3/sway: module fails when reloading config file ([#361][361]). +* Worked around bug in gcc causing a compilation error ([#350][350]). [311]: https://codeberg.org/dnkl/yambar/issues/311 [302]: https://codeberg.org/dnkl/yambar/issues/302 @@ -49,6 +50,7 @@ [343]: https://codeberg.org/dnkl/yambar/issues/343 [352]: https://codeberg.org/dnkl/yambar/issues/352 [361]: https://codeberg.org/dnkl/yambar/issues/361 +[350]: https://codeberg.org/dnkl/yambar/issues/350 ### Security diff --git a/plugin.c b/plugin.c index ed7f63c..20dfbbf 100644 --- a/plugin.c +++ b/plugin.c @@ -126,7 +126,8 @@ type2str(enum plugin_type type) case PLUGIN_DECORATION: return "decoration"; } - return NULL; + assert(false && "invalid type"); + return ""; } static void __attribute__((constructor)) From c44c66c83f1234fa3842856eba9b17714a597706 Mon Sep 17 00:00:00 2001 From: Sertonix Date: Wed, 7 Feb 2024 14:33:52 +0100 Subject: [PATCH 535/611] network: use dynlist instead of fixed name Closes #271 Closes #265 Closes #71 --- CHANGELOG.md | 4 + doc/yambar-modules-network.5.scd | 29 +- examples/configurations/laptop.conf | 41 +- modules/network.c | 851 +++++++++++++++------------- 4 files changed, 504 insertions(+), 421 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e31c0c8..44f1dad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,10 @@ ### Changed * log-level: default to `warning` +* network: use dynlist instead of fixed name ([#355][355]) + +[355]: https://codeberg.org/dnkl/yambar/pulls/355 + ### Deprecated ### Removed diff --git a/doc/yambar-modules-network.5.scd b/doc/yambar-modules-network.5.scd index 44605e2..cdc5530 100644 --- a/doc/yambar-modules-network.5.scd +++ b/doc/yambar-modules-network.5.scd @@ -6,11 +6,12 @@ network - This module monitors network connection state # DESCRIPTION This module monitors network connection state; disconnected/connected -state and MAC/IP addresses. +state and MAC/IP addresses. It instantiates the provided _content_ +particle for each network interface. Note: while the module internally tracks all assigned IPv4/IPv6 addresses, it currently exposes only a single IPv4 and a single IPv6 -address. +address per network interface. # TAGS @@ -68,10 +69,18 @@ address. :[ *Type* :[ *Req* :< *Description* -| name -: string -: yes -: Name of network interface to monitor +| left-spacing +: int +: no +: Space, in pixels, in the left side of each rendered volume +| right-spacing +: int +: no +: Space, in pixels, on the right side of each rendered volume +| spacing +: int +: no +: Short-hand for setting both _left-spacing_ and _right-spacing_ | poll-interval : int : no @@ -86,9 +95,13 @@ address. bar: left: - network: - name: wlp3s0 content: - string: {text: "{name}: {state} ({ipv4})"} + map: + default: + string: {text: "{name}: {state} ({ipv4})"} + conditions: + ipv4 == "": + string: {text: "{name}: {state}"} ``` # SEE ALSO diff --git a/examples/configurations/laptop.conf b/examples/configurations/laptop.conf index 87f3d1c..cee2639 100644 --- a/examples/configurations/laptop.conf +++ b/examples/configurations/laptop.conf @@ -169,34 +169,39 @@ bar: - string: {text: , font: *awesome} - string: {text: "{layout}"} - network: - name: enp1s0 content: map: + default: {empty: {}} conditions: - ~carrier: {empty: {}} - carrier: - map: - default: {string: {text: , font: *awesome, foreground: ffffff66}} - conditions: - state == up && ipv4 != "": {string: {text: , font: *awesome}} + name == enp1s0: + conditions: + ~carrier: {empty: {}} + carrier: + map: + default: {string: {text: , font: *awesome, foreground: ffffff66}} + conditions: + state == up && ipv4 != "": {string: {text: , font: *awesome}} - network: - name: wlp2s0 poll-interval: 1000 content: map: - default: {string: {text: , font: *awesome, foreground: ffffff66}} + default: {empty: {}} conditions: - state == down: {string: {text: , font: *awesome, foreground: ff0000ff}} - state == up: + name == wlp2s0: map: - default: - - string: {text: , font: *awesome} - - string: {text: "{ssid} {dl-speed:mb}/{ul-speed:mb} Mb/s"} - + default: {string: {text: , font: *awesome, foreground: ffffff66}} conditions: - ipv4 == "": - - string: {text: , font: *awesome, foreground: ffffff66} - - string: {text: "{ssid} {dl-speed:mb}/{ul-speed:mb} Mb/s", foreground: ffffff66} + state == down: {string: {text: , font: *awesome, foreground: ff0000ff}} + state == up: + map: + default: + - string: {text: , font: *awesome} + - string: {text: "{ssid} {dl-speed:mb}/{ul-speed:mb} Mb/s"} + + conditions: + ipv4 == "": + - string: {text: , font: *awesome, foreground: ffffff66} + - string: {text: "{ssid} {dl-speed:mb}/{ul-speed:mb} Mb/s", foreground: ffffff66} - alsa: card: hw:PCH mixer: Master diff --git a/modules/network.c b/modules/network.c index 3336c23..24e811f 100644 --- a/modules/network.c +++ b/modules/network.c @@ -30,9 +30,10 @@ #include "../config.h" #include "../config-verify.h" #include "../module.h" +#include "../particles/dynlist.h" #include "../plugin.h" -#define UNUSED __attribute__((unused)) +#define max(x, y) ((x) > (y) ? (x) : (y)) static const long min_poll_interval = 250; @@ -49,25 +50,12 @@ struct af_addr { } addr; }; -struct private { - char *iface; - struct particle *label; - int poll_interval; +struct iface { + char *name; - int genl_sock; - int rt_sock; - int urandom_fd; + uint32_t get_stats_seq_nr; - struct { - uint16_t family_id; - uint32_t get_interface_seq_nr; - uint32_t get_station_seq_nr; - uint32_t get_scan_seq_nr; - } nl80211; - - bool get_addresses; - - int ifindex; + int index; uint8_t mac[6]; bool carrier; uint8_t state; /* IFLA_OPERSTATE */ @@ -88,6 +76,36 @@ struct private { uint64_t dl_bits; }; +struct private { + struct particle *label; + int poll_interval; + + int left_spacing; + int right_spacing; + + bool get_addresses; + + int genl_sock; + int rt_sock; + int urandom_fd; + + struct { + uint16_t family_id; + uint32_t get_interface_seq_nr; + uint32_t get_scan_seq_nr; + } nl80211; + + tll(struct iface) ifaces; +}; + +static void +free_iface(struct iface iface) +{ + tll_free(iface.addrs); + free(iface.ssid); + free(iface.name); +} + static void destroy(struct module *mod) { @@ -100,22 +118,17 @@ destroy(struct module *mod) if (m->urandom_fd >= 0) close(m->urandom_fd); - tll_free(m->addrs); - free(m->ssid); - free(m->iface); - free(m); + tll_foreach(m->ifaces, it) + free_iface(it->item); + free(m); module_default_destroy(mod); } static const char * description(const struct module *mod) { - static char desc[32]; - const struct private *m = mod->private; - - snprintf(desc, sizeof(desc), "net(%s)", m->iface); - return desc; + return "network"; } static struct exposable * @@ -125,70 +138,78 @@ content(struct module *mod) mtx_lock(&mod->lock); - const char *state = NULL; - switch (m->state) { - case IF_OPER_UNKNOWN: state = "unknown"; break; - case IF_OPER_NOTPRESENT: state = "not present"; break; - case IF_OPER_DOWN: state = "down"; break; - case IF_OPER_LOWERLAYERDOWN: state = "lower layers down"; break; - case IF_OPER_TESTING: state = "testing"; break; - case IF_OPER_DORMANT: state = "dormant"; break; - case IF_OPER_UP: state = "up"; break; - default: state = "unknown"; break; + struct exposable *exposables[max(tll_length(m->ifaces), 1)]; + size_t idx = 0; + + tll_foreach(m->ifaces, it) { + struct iface *iface = &it->item; + + const char *state = NULL; + switch (iface->state) { + case IF_OPER_UNKNOWN: state = "unknown"; break; + case IF_OPER_NOTPRESENT: state = "not present"; break; + case IF_OPER_DOWN: state = "down"; break; + case IF_OPER_LOWERLAYERDOWN: state = "lower layers down"; break; + case IF_OPER_TESTING: state = "testing"; break; + case IF_OPER_DORMANT: state = "dormant"; break; + case IF_OPER_UP: state = "up"; break; + default: state = "unknown"; break; + } + + char mac_str[6 * 2 + 5 + 1]; + char ipv4_str[INET_ADDRSTRLEN] = {0}; + char ipv6_str[INET6_ADDRSTRLEN] = {0}; + + snprintf(mac_str, sizeof(mac_str), "%02x:%02x:%02x:%02x:%02x:%02x", + iface->mac[0], iface->mac[1], iface->mac[2], iface->mac[3], iface->mac[4], iface->mac[5]); + + /* TODO: this exposes the *last* added address of each kind. Can + * we expose all in some way? */ + tll_foreach(iface->addrs, it) { + if (it->item.family == AF_INET) + inet_ntop(AF_INET, &it->item.addr.ipv4, ipv4_str, sizeof(ipv4_str)); + else if (it->item.family == AF_INET6) + if(!IN6_IS_ADDR_LINKLOCAL(&it->item.addr.ipv6)) + inet_ntop(AF_INET6, &it->item.addr.ipv6, ipv6_str, sizeof(ipv6_str)); + } + + int quality = 0; + if (iface->signal_strength_dbm != 0) { + if (iface->signal_strength_dbm <= -100) + quality = 0; + else if (iface->signal_strength_dbm >= -50) + quality = 100; + else + quality = 2 * (iface->signal_strength_dbm + 100); + } + + struct tag_set tags = { + .tags = (struct tag *[]){ + tag_new_string(mod, "name", iface->name), + tag_new_int(mod, "index", iface->index), + tag_new_bool(mod, "carrier", iface->carrier), + tag_new_string(mod, "state", state), + tag_new_string(mod, "mac", mac_str), + tag_new_string(mod, "ipv4", ipv4_str), + tag_new_string(mod, "ipv6", ipv6_str), + tag_new_string(mod, "ssid", iface->ssid), + tag_new_int(mod, "signal", iface->signal_strength_dbm), + tag_new_int_range(mod, "quality", quality, 0, 100), + tag_new_int(mod, "rx-bitrate", iface->rx_bitrate), + tag_new_int(mod, "tx-bitrate", iface->tx_bitrate), + tag_new_float(mod, "dl-speed", iface->dl_speed), + tag_new_float(mod, "ul-speed", iface->ul_speed), + }, + .count = 14, + }; + exposables[idx++] = m->label->instantiate(m->label, &tags); + tag_set_destroy(&tags); } - char mac_str[6 * 2 + 5 + 1]; - char ipv4_str[INET_ADDRSTRLEN] = {0}; - char ipv6_str[INET6_ADDRSTRLEN] = {0}; - - snprintf(mac_str, sizeof(mac_str), "%02x:%02x:%02x:%02x:%02x:%02x", - m->mac[0], m->mac[1], m->mac[2], m->mac[3], m->mac[4], m->mac[5]); - - /* TODO: this exposes the *last* added address of each kind. Can - * we expose all in some way? */ - tll_foreach(m->addrs, it) { - if (it->item.family == AF_INET) - inet_ntop(AF_INET, &it->item.addr.ipv4, ipv4_str, sizeof(ipv4_str)); - else if (it->item.family == AF_INET6) - if(!IN6_IS_ADDR_LINKLOCAL(&it->item.addr.ipv6)) - inet_ntop(AF_INET6, &it->item.addr.ipv6, ipv6_str, sizeof(ipv6_str)); - } - - int quality = 0; - if (m->signal_strength_dbm != 0) { - if (m->signal_strength_dbm <= -100) - quality = 0; - else if (m->signal_strength_dbm >= -50) - quality = 100; - else - quality = 2 * (m->signal_strength_dbm + 100); - } - - struct tag_set tags = { - .tags = (struct tag *[]){ - tag_new_string(mod, "name", m->iface), - tag_new_int(mod, "index", m->ifindex), - tag_new_bool(mod, "carrier", m->carrier), - tag_new_string(mod, "state", state), - tag_new_string(mod, "mac", mac_str), - tag_new_string(mod, "ipv4", ipv4_str), - tag_new_string(mod, "ipv6", ipv6_str), - tag_new_string(mod, "ssid", m->ssid), - tag_new_int(mod, "signal", m->signal_strength_dbm), - tag_new_int_range(mod, "quality", quality, 0, 100), - tag_new_int(mod, "rx-bitrate", m->rx_bitrate), - tag_new_int(mod, "tx-bitrate", m->tx_bitrate), - tag_new_float(mod, "dl-speed", m->dl_speed), - tag_new_float(mod, "ul-speed", m->ul_speed), - }, - .count = 14, - }; - mtx_unlock(&mod->lock); - struct exposable *exposable = m->label->instantiate(m->label, &tags); - tag_set_destroy(&tags); - return exposable; + return dynlist_exposable_new( + exposables, idx, m->left_spacing, m->right_spacing); } /* Returns a value suitable for nl_pid/nlmsg_pid */ @@ -279,8 +300,8 @@ send_rt_request(struct private *m, int request) }; if (!send_nlmsg(m->rt_sock, &req, req.hdr.nlmsg_len)) { - LOG_ERRNO("%s: failed to send netlink RT request (%d)", - m->iface, request); + LOG_ERRNO("failed to send netlink RT request (%d)", + request); return false; } @@ -288,8 +309,22 @@ send_rt_request(struct private *m, int request) } static bool -send_rt_getstats_request(struct private *m) +send_rt_getstats_request(struct private *m, struct iface *iface) { + if (iface->get_stats_seq_nr > 0) { + LOG_DBG( + "%s: RT get-stats request already in progress", iface->name); + return true; + } + + LOG_DBG("%s: sending RT get-stats request", iface->name); + + uint32_t seq; + if (read(m->urandom_fd, &seq, sizeof(seq)) != sizeof(seq)) { + LOG_ERRNO("failed to read from /dev/urandom"); + return false; + } + struct { struct nlmsghdr hdr; struct if_stats_msg rt; @@ -298,12 +333,12 @@ send_rt_getstats_request(struct private *m) .nlmsg_len = NLMSG_LENGTH(sizeof(req.rt)), .nlmsg_type = RTM_GETSTATS, .nlmsg_flags = NLM_F_REQUEST, - .nlmsg_seq = 1, + .nlmsg_seq = seq, .nlmsg_pid = nl_pid_value(), }, .rt = { - .ifindex = m->ifindex, + .ifindex = iface->index, .filter_mask = IFLA_STATS_LINK_64, .family = AF_UNSPEC, }, @@ -311,9 +346,10 @@ send_rt_getstats_request(struct private *m) if (!send_nlmsg(m->rt_sock, &req, req.hdr.nlmsg_len)) { LOG_ERRNO("%s: failed to send netlink RT getstats request (%d)", - m->iface, RTM_GETSTATS); + iface->name, RTM_GETSTATS); return false; } + iface->get_stats_seq_nr = seq; return true; } @@ -361,8 +397,7 @@ send_ctrl_get_family_request(struct private *m) ""); if (!send_nlmsg(m->genl_sock, &req, req.hdr.nlmsg_len)) { - LOG_ERRNO("%s: failed to send netlink ctrl-get-family request", - m->iface); + LOG_ERRNO("failed to send netlink ctrl-get-family request"); return false; } @@ -370,11 +405,69 @@ send_ctrl_get_family_request(struct private *m) } static bool -send_nl80211_request(struct private *m, uint8_t cmd, uint16_t flags, uint32_t seq) +send_nl80211_request(struct private *m, uint8_t cmd, uint32_t seq) { - if (m->ifindex < 0) + if (m->nl80211.family_id == (uint16_t)-1) return false; + const struct { + struct nlmsghdr hdr; + struct { + struct genlmsghdr genl; + } msg __attribute__((aligned(NLMSG_ALIGNTO))); + } req = { + .hdr = { + .nlmsg_len = NLMSG_LENGTH(sizeof(req.msg)), + .nlmsg_type = m->nl80211.family_id, + .nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP, + .nlmsg_seq = seq, + .nlmsg_pid = nl_pid_value(), + }, + + .msg = { + .genl = { + .cmd = cmd, + .version = 1, + }, + }, + }; + + if (!send_nlmsg(m->genl_sock, &req, req.hdr.nlmsg_len)) { + LOG_ERRNO("failed to send netlink nl80211 get-inteface request"); + return false; + } + + return true; +} + +static bool +send_nl80211_get_interface(struct private *m) +{ + if (m->nl80211.get_interface_seq_nr > 0) { + LOG_DBG("nl80211 get-interface request already in progress"); + return true; + } + + uint32_t seq; + if (read(m->urandom_fd, &seq, sizeof(seq)) != sizeof(seq)) { + LOG_ERRNO("failed to read from /dev/urandom"); + return false; + } + + LOG_DBG("sending nl80211 get-interface request %d", seq); + + if (!send_nl80211_request(m, NL80211_CMD_GET_INTERFACE, seq)) + return false; + + m->nl80211.get_interface_seq_nr = seq; + return true; +} + +static bool +send_nl80211_get_station(struct private *m, struct iface *iface) +{ + LOG_DBG("sending nl80211 get-station request"); + if (m->nl80211.family_id == (uint16_t)-1) return false; @@ -391,14 +484,14 @@ send_nl80211_request(struct private *m, uint8_t cmd, uint16_t flags, uint32_t se .hdr = { .nlmsg_len = NLMSG_LENGTH(sizeof(req.msg)), .nlmsg_type = m->nl80211.family_id, - .nlmsg_flags = flags, - .nlmsg_seq = seq, + .nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP, + .nlmsg_seq = 1, .nlmsg_pid = nl_pid_value(), }, .msg = { .genl = { - .cmd = cmd, + .cmd = NL80211_CMD_GET_STATION, .version = 1, }, @@ -408,124 +501,40 @@ send_nl80211_request(struct private *m, uint8_t cmd, uint16_t flags, uint32_t se .nla_len = sizeof(req.msg.ifindex), }, - .index = m->ifindex, + .index = iface->index, }, }, }; if (!send_nlmsg(m->genl_sock, &req, req.hdr.nlmsg_len)) { - LOG_ERRNO("%s: failed to send netlink nl80211 get-inteface request", - m->iface); + LOG_ERRNO("failed to send netlink nl80211 get-inteface request"); return false; } return true; } -static bool -send_nl80211_get_interface(struct private *m) -{ - if (m->nl80211.get_interface_seq_nr > 0) { - LOG_DBG( - "%s: nl80211 get-interface request already in progress", m->iface); - return true; - } - - LOG_DBG("%s: sending nl80211 get-interface request", m->iface); - - uint32_t seq; - if (read(m->urandom_fd, &seq, sizeof(seq)) != sizeof(seq)) { - LOG_ERRNO("failed to read from /dev/urandom"); - return false; - } - - if (send_nl80211_request(m, NL80211_CMD_GET_INTERFACE, NLM_F_REQUEST, seq)) { - m->nl80211.get_interface_seq_nr = seq; - return true; - } else - return false; -} - -static bool -send_nl80211_get_station(struct private *m) -{ - if (m->nl80211.get_station_seq_nr > 0) { - LOG_DBG( - "%s: nl80211 get-station request already in progress", m->iface); - return true; - } - - LOG_DBG("%s: sending nl80211 get-station request", m->iface); - - uint32_t seq; - if (read(m->urandom_fd, &seq, sizeof(seq)) != sizeof(seq)) { - LOG_ERRNO("failed to read from /dev/urandom"); - return false; - } - - if (send_nl80211_request( - m, NL80211_CMD_GET_STATION, NLM_F_REQUEST | NLM_F_DUMP, seq)) - { - m->nl80211.get_station_seq_nr = seq; - return true; - } else - return false; -} - static bool send_nl80211_get_scan(struct private *m) { if (m->nl80211.get_scan_seq_nr > 0) { - LOG_ERR( - "%s: nl80211 get-scan request already in progress", m->iface); + LOG_ERR("nl80211 get-scan request already in progress"); return true; } - LOG_DBG("%s: sending nl80211 get-scan request", m->iface); - uint32_t seq; if (read(m->urandom_fd, &seq, sizeof(seq)) != sizeof(seq)) { LOG_ERRNO("failed to read from /dev/urandom"); return false; } - if (send_nl80211_request( - m, NL80211_CMD_GET_SCAN, NLM_F_REQUEST | NLM_F_DUMP, seq)) - { - m->nl80211.get_scan_seq_nr = seq; - return true; - } else + LOG_DBG("sending nl80211 get-scan request %d", seq); + + if (!send_nl80211_request(m, NL80211_CMD_GET_SCAN, seq)) return false; -} -static bool -find_my_ifindex(struct module *mod, const struct ifinfomsg *msg, size_t len) -{ - struct private *m = mod->private; - - for (const struct rtattr *attr = IFLA_RTA(msg); - RTA_OK(attr, len); - attr = RTA_NEXT(attr, len)) - { - switch (attr->rta_type) { - case IFLA_IFNAME: - if (strcmp((const char *)RTA_DATA(attr), m->iface) == 0) { - LOG_INFO("%s: ifindex=%d", m->iface, msg->ifi_index); - - mtx_lock(&mod->lock); - m->ifindex = msg->ifi_index; - mtx_unlock(&mod->lock); - - send_nl80211_get_interface(m); - send_nl80211_get_station(m); - return true; - } - - return false; - } - } - - return false; + m->nl80211.get_scan_seq_nr = seq; + return true; } static void @@ -536,54 +545,73 @@ handle_link(struct module *mod, uint16_t type, struct private *m = mod->private; - if (m->ifindex == -1) { - /* We don't know our own ifindex yet. Let's see if we can find - * it in the message */ - if (!find_my_ifindex(mod, msg, len)) { - /* Nope, message wasn't for us (IFLA_IFNAME mismatch) */ - return; + if (type == RTM_DELLINK) { + tll_foreach(m->ifaces, it) { + if (msg->ifi_index != it->item.index) + continue; + mtx_lock(&mod->lock); + tll_remove_and_free(m->ifaces, it, free_iface); + mtx_unlock(&mod->lock); + break; } - } - assert(m->ifindex >= 0); - - if (msg->ifi_index != m->ifindex) { - /* Not for us */ + mod->bar->refresh(mod->bar); return; } - bool update_bar = false; + struct iface *iface = NULL; + tll_foreach(m->ifaces, it) { + if (msg->ifi_index != it->item.index) + continue; + iface = &it->item; + break; + } + + if (iface == NULL) { + mtx_lock(&mod->lock); + tll_push_back(m->ifaces, + ((struct iface){ + .index = msg->ifi_index, + .state = IF_OPER_DOWN, + .addrs = tll_init(), + })); + mtx_unlock(&mod->lock); + iface = &tll_back(m->ifaces); + } for (const struct rtattr *attr = IFLA_RTA(msg); RTA_OK(attr, len); attr = RTA_NEXT(attr, len)) { switch (attr->rta_type) { + case IFLA_IFNAME: + mtx_lock(&mod->lock); + iface->name = strdup((const char *)RTA_DATA(attr)); + LOG_DBG("%s: index=%d", iface->name, iface->index); + mtx_unlock(&mod->lock); case IFLA_OPERSTATE: { uint8_t operstate = *(const uint8_t *)RTA_DATA(attr); - if (m->state == operstate) + if (iface->state == operstate) break; - LOG_DBG("%s: IFLA_OPERSTATE: %hhu -> %hhu", m->iface, m->state, operstate); + LOG_DBG("%s: IFLA_OPERSTATE: %hhu -> %hhu", iface->name, iface->state, operstate); mtx_lock(&mod->lock); - m->state = operstate; + iface->state = operstate; mtx_unlock(&mod->lock); - update_bar = true; break; } case IFLA_CARRIER: { uint8_t carrier = *(const uint8_t *)RTA_DATA(attr); - if (m->carrier == carrier) + if (iface->carrier == carrier) break; - LOG_DBG("%s: IFLA_CARRIER: %hhu -> %hhu", m->iface, m->carrier, carrier); + LOG_DBG("%s: IFLA_CARRIER: %hhu -> %hhu", iface->name, iface->carrier, carrier); mtx_lock(&mod->lock); - m->carrier = carrier; + iface->carrier = carrier; mtx_unlock(&mod->lock); - update_bar = true; break; } @@ -592,24 +620,28 @@ handle_link(struct module *mod, uint16_t type, break; const uint8_t *mac = RTA_DATA(attr); - if (memcmp(m->mac, mac, sizeof(m->mac)) == 0) + if (memcmp(iface->mac, mac, sizeof(iface->mac)) == 0) break; LOG_DBG("%s: IFLA_ADDRESS: %02x:%02x:%02x:%02x:%02x:%02x", - m->iface, + iface->name, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); mtx_lock(&mod->lock); - memcpy(m->mac, mac, sizeof(m->mac)); + memcpy(iface->mac, mac, sizeof(iface->mac)); mtx_unlock(&mod->lock); - update_bar = true; break; } } } - if (update_bar) - mod->bar->refresh(mod->bar); + assert(iface->name != NULL); + + /* Reset address initialization */ + m->get_addresses = true; + + send_nl80211_get_interface(m); + mod->bar->refresh(mod->bar); } static void @@ -620,14 +652,21 @@ handle_address(struct module *mod, uint16_t type, struct private *m = mod->private; - assert(m->ifindex >= 0); + bool update_bar = false; - if (msg->ifa_index != m->ifindex) { - /* Not for us */ - return; + struct iface *iface = NULL; + + tll_foreach(m->ifaces, it) { + if (msg->ifa_index != it->item.index) + continue; + iface = &it->item; + break; } - bool update_bar = false; + if (iface == NULL) { + LOG_ERR("failed to find network interface with index %d. Probaly a yambar bug", msg->ifa_index); + return; + } for (const struct rtattr *attr = IFA_RTA(msg); RTA_OK(attr, len); @@ -642,21 +681,21 @@ handle_address(struct module *mod, uint16_t type, char s[INET6_ADDRSTRLEN]; inet_ntop(msg->ifa_family, raw_addr, s, sizeof(s)); #endif - LOG_DBG("%s: IFA_ADDRESS (%s): %s", m->iface, + LOG_DBG("%s: IFA_ADDRESS (%s): %s", iface->name, type == RTM_NEWADDR ? "add" : "del", s); mtx_lock(&mod->lock); if (type == RTM_DELADDR) { /* Find address in our list and remove it */ - tll_foreach(m->addrs, it) { + tll_foreach(iface->addrs, it) { if (it->item.family != msg->ifa_family) continue; if (memcmp(&it->item.addr, raw_addr, addr_len) != 0) continue; - tll_remove(m->addrs, it); + tll_remove(iface->addrs, it); update_bar = true; break; } @@ -664,7 +703,7 @@ handle_address(struct module *mod, uint16_t type, /* Append address to our list */ struct af_addr a = {.family = msg->ifa_family}; memcpy(&a.addr, raw_addr, addr_len); - tll_push_back(m->addrs, a); + tll_push_back(iface->addrs, a); update_bar = true; } @@ -679,9 +718,10 @@ handle_address(struct module *mod, uint16_t type, } static bool -foreach_nlattr(struct module *mod, const struct genlmsghdr *genl, size_t len, - bool (*cb)(struct module *mod, uint16_t type, bool nested, - const void *payload, size_t len)) +foreach_nlattr(struct module *mod, struct iface *iface, const struct genlmsghdr *genl, size_t len, + bool (*cb)(struct module *mod, struct iface *iface, uint16_t type, bool nested, + const void *payload, size_t len, void *ctx), + void *ctx) { const uint8_t *raw = (const uint8_t *)genl + GENL_HDRLEN; const uint8_t *end = (const uint8_t *)genl + len; @@ -694,7 +734,7 @@ foreach_nlattr(struct module *mod, const struct genlmsghdr *genl, size_t len, bool nested = (attr->nla_type & NLA_F_NESTED) != 0;; const void *payload = raw + NLA_HDRLEN; - if (!cb(mod, type, nested, payload, attr->nla_len - NLA_HDRLEN)) + if (!cb(mod, iface, type, nested, payload, attr->nla_len - NLA_HDRLEN, ctx)) return false; } @@ -702,8 +742,8 @@ foreach_nlattr(struct module *mod, const struct genlmsghdr *genl, size_t len, } static bool -foreach_nlattr_nested(struct module *mod, const void *parent_payload, size_t len, - bool (*cb)(struct module *mod, uint16_t type, +foreach_nlattr_nested(struct module *mod, struct iface *iface, const void *parent_payload, size_t len, + bool (*cb)(struct module *mod, struct iface *iface, uint16_t type, bool nested, const void *payload, size_t len, void *ctx), void *ctx) @@ -719,7 +759,7 @@ foreach_nlattr_nested(struct module *mod, const void *parent_payload, size_t len bool nested = (attr->nla_type & NLA_F_NESTED) != 0; const void *payload = raw + NLA_HDRLEN; - if (!cb(mod, type, nested, payload, attr->nla_len - NLA_HDRLEN, ctx)) + if (!cb(mod, iface, type, nested, payload, attr->nla_len - NLA_HDRLEN, ctx)) return false; } @@ -732,10 +772,9 @@ struct mcast_group { }; static bool -parse_mcast_group(struct module *mod, uint16_t type, bool nested, +parse_mcast_group(struct module *mod, struct iface *iface, uint16_t type, bool nested, const void *payload, size_t len, void *_ctx) { - struct private *m = mod->private; struct mcast_group *ctx = _ctx; switch (type) { @@ -750,8 +789,8 @@ parse_mcast_group(struct module *mod, uint16_t type, bool nested, } default: - LOG_WARN("%s: unrecognized GENL MCAST GRP attribute: " - "type=%hu, nested=%d, len=%zu", m->iface, type, nested, len); + LOG_WARN("unrecognized GENL MCAST GRP attribute: " + "type=%hu, nested=%d, len=%zu", type, nested, len); break; } @@ -759,13 +798,13 @@ parse_mcast_group(struct module *mod, uint16_t type, bool nested, } static bool -parse_mcast_groups(struct module *mod, uint16_t type, bool nested, +parse_mcast_groups(struct module *mod, struct iface *iface, uint16_t type, bool nested, const void *payload, size_t len, void *_ctx) { struct private *m = mod->private; struct mcast_group group = {0}; - foreach_nlattr_nested(mod, payload, len, &parse_mcast_group, &group); + foreach_nlattr_nested(mod, NULL, payload, len, &parse_mcast_group, &group); LOG_DBG("MCAST: %s -> %u", group.name, group.id); @@ -787,8 +826,8 @@ parse_mcast_groups(struct module *mod, uint16_t type, bool nested, } static bool -handle_genl_ctrl(struct module *mod, uint16_t type, bool nested, - const void *payload, size_t len) +handle_genl_ctrl(struct module *mod, struct iface *iface, uint16_t type, bool nested, + const void *payload, size_t len, void *_ctx) { struct private *m = mod->private; @@ -796,7 +835,6 @@ handle_genl_ctrl(struct module *mod, uint16_t type, bool nested, case CTRL_ATTR_FAMILY_ID: { m->nl80211.family_id = *(const uint16_t *)payload; send_nl80211_get_interface(m); - send_nl80211_get_station(m); break; } @@ -805,12 +843,38 @@ handle_genl_ctrl(struct module *mod, uint16_t type, bool nested, break; case CTRL_ATTR_MCAST_GROUPS: - foreach_nlattr_nested(mod, payload, len, &parse_mcast_groups, NULL); + foreach_nlattr_nested(mod, NULL, payload, len, &parse_mcast_groups, NULL); break; default: - LOG_DBG("%s: unrecognized GENL CTRL attribute: " - "type=%hu, nested=%d, len=%zu", m->iface, type, nested, len); + LOG_DBG("unrecognized GENL CTRL attribute: " + "type=%hu, nested=%d, len=%zu", type, nested, len); + break; + } + + return true; +} + + +static bool +find_nl80211_iface(struct module *mod, struct iface *_iface, uint16_t type, bool nested, + const void *payload, size_t len, void *ctx) +{ + struct private *m = mod->private; + struct iface **iface = ctx; + + switch (type) { + case NL80211_ATTR_IFINDEX: + if (*iface != NULL) + if (*(uint32_t *)payload == (*iface)->index) + return false; + tll_foreach(m->ifaces, it) { + if (*(uint32_t *)payload != it->item.index) + continue; + *iface = &it->item; + return false; + } + LOG_ERR("could not find interface with index %d", *(uint32_t *)payload); break; } @@ -818,46 +882,23 @@ handle_genl_ctrl(struct module *mod, uint16_t type, bool nested, } static bool -check_for_nl80211_ifindex(struct module *mod, uint16_t type, bool nested, - const void *payload, size_t len) +handle_nl80211_new_interface(struct module *mod, struct iface *iface, uint16_t type, bool nested, + const void *payload, size_t len, void *_ctx) { - struct private *m = mod->private; - switch (type) { case NL80211_ATTR_IFINDEX: - return *(uint32_t *)payload == m->ifindex; - } - - return true; -} - -static bool -nl80211_is_for_us(struct module *mod, const struct genlmsghdr *genl, - size_t msg_size) -{ - return foreach_nlattr(mod, genl, msg_size, &check_for_nl80211_ifindex); -} - -static bool -handle_nl80211_new_interface(struct module *mod, uint16_t type, bool nested, - const void *payload, size_t len) -{ - struct private *m = mod->private; - - switch (type) { - case NL80211_ATTR_IFINDEX: - assert(*(uint32_t *)payload == m->ifindex); + assert(*(uint32_t *)payload == iface->index); break; case NL80211_ATTR_SSID: { const char *ssid = payload; - if (m->ssid == NULL || strncmp(m->ssid, ssid, len) != 0) - LOG_INFO("%s: SSID: %.*s", m->iface, (int)len, ssid); + if (iface->ssid == NULL || strncmp(iface->ssid, ssid, len) != 0) + LOG_INFO("%s: SSID: %.*s", iface->name, (int)len, ssid); mtx_lock(&mod->lock); - free(m->ssid); - m->ssid = strndup(ssid, len); + free(iface->ssid); + iface->ssid = strndup(ssid, len); mtx_unlock(&mod->lock); mod->bar->refresh(mod->bar); @@ -866,7 +907,7 @@ handle_nl80211_new_interface(struct module *mod, uint16_t type, bool nested, default: LOG_DBG("%s: unrecognized nl80211 attribute: " - "type=%hu, nested=%d, len=%zu", m->iface, type, nested, len); + "type=%hu, nested=%d, len=%zu", iface->name, type, nested, len); break; } @@ -878,10 +919,9 @@ struct rate_info_ctx { }; static bool -handle_nl80211_rate_info(struct module *mod, uint16_t type, bool nested, +handle_nl80211_rate_info(struct module *mod, struct iface *iface, uint16_t type, bool nested, const void *payload, size_t len, void *_ctx) { - struct private *m UNUSED = mod->private; struct rate_info_ctx *ctx = _ctx; switch (type) { @@ -902,7 +942,7 @@ handle_nl80211_rate_info(struct module *mod, uint16_t type, bool nested, default: LOG_DBG("%s: unrecognized nl80211 rate info attribute: " - "type=%hu, nested=%d, len=%zu", m->iface, type, nested, len); + "type=%hu, nested=%d, len=%zu", iface->name, type, nested, len); break; } @@ -914,10 +954,9 @@ struct station_info_ctx { }; static bool -handle_nl80211_station_info(struct module *mod, uint16_t type, bool nested, +handle_nl80211_station_info(struct module *mod, struct iface *iface, uint16_t type, bool nested, const void *payload, size_t len, void *_ctx) { - struct private *m = mod->private; struct station_info_ctx *ctx = _ctx; switch (type) { @@ -925,23 +964,22 @@ handle_nl80211_station_info(struct module *mod, uint16_t type, bool nested, LOG_DBG("signal strength (last): %hhd dBm", *(uint8_t *)payload); break; - case NL80211_STA_INFO_SIGNAL_AVG: { + case NL80211_STA_INFO_SIGNAL_AVG: LOG_DBG("signal strength (average): %hhd dBm", *(uint8_t *)payload); mtx_lock(&mod->lock); - m->signal_strength_dbm = *(int8_t *)payload; + iface->signal_strength_dbm = *(int8_t *)payload; mtx_unlock(&mod->lock); ctx->update_bar = true; break; - } case NL80211_STA_INFO_TX_BITRATE: { struct rate_info_ctx rctx = {0}; foreach_nlattr_nested( - mod, payload, len, &handle_nl80211_rate_info, &rctx); + mod, iface, payload, len, &handle_nl80211_rate_info, &rctx); LOG_DBG("TX bitrate: %.1f Mbit/s", rctx.bitrate / 1000. / 1000.); mtx_lock(&mod->lock); - m->tx_bitrate = rctx.bitrate; + iface->tx_bitrate = rctx.bitrate; mtx_unlock(&mod->lock); ctx->update_bar = true; break; @@ -950,11 +988,11 @@ handle_nl80211_station_info(struct module *mod, uint16_t type, bool nested, case NL80211_STA_INFO_RX_BITRATE: { struct rate_info_ctx rctx = {0}; foreach_nlattr_nested( - mod, payload, len, &handle_nl80211_rate_info, &rctx); + mod, iface, payload, len, &handle_nl80211_rate_info, &rctx); LOG_DBG("RX bitrate: %.1f Mbit/s", rctx.bitrate / 1000. / 1000.); mtx_lock(&mod->lock); - m->rx_bitrate = rctx.bitrate; + iface->rx_bitrate = rctx.bitrate; mtx_unlock(&mod->lock); ctx->update_bar = true; break; @@ -962,7 +1000,7 @@ handle_nl80211_station_info(struct module *mod, uint16_t type, bool nested, default: LOG_DBG("%s: unrecognized nl80211 station info attribute: " - "type=%hu, nested=%d, len=%zu", m->iface, type, nested, len); + "type=%hu, nested=%d, len=%zu", iface->name, type, nested, len); break; } @@ -970,16 +1008,17 @@ handle_nl80211_station_info(struct module *mod, uint16_t type, bool nested, } static bool -handle_nl80211_new_station(struct module *mod, uint16_t type, bool nested, - const void *payload, size_t len) +handle_nl80211_new_station(struct module *mod, struct iface *iface, uint16_t type, bool nested, + const void *payload, size_t len, void *_ctx) { - struct private *m UNUSED = mod->private; - switch (type) { + case NL80211_ATTR_IFINDEX: + break; + case NL80211_ATTR_STA_INFO: { struct station_info_ctx ctx = {0}; foreach_nlattr_nested( - mod, payload, len, &handle_nl80211_station_info, &ctx); + mod, iface, payload, len, &handle_nl80211_station_info, &ctx); if (ctx.update_bar) mod->bar->refresh(mod->bar); @@ -988,7 +1027,7 @@ handle_nl80211_new_station(struct module *mod, uint16_t type, bool nested, default: LOG_DBG("%s: unrecognized nl80211 attribute: " - "type=%hu, nested=%d, len=%zu", m->iface, type, nested, len); + "type=%hu, nested=%d, len=%zu", iface->name, type, nested, len); break; } @@ -996,9 +1035,8 @@ handle_nl80211_new_station(struct module *mod, uint16_t type, bool nested, } static bool -handle_ies(struct module *mod, const void *_ies, size_t len) +handle_ies(struct module *mod, struct iface *iface, const void *_ies, size_t len) { - struct private *m = mod->private; const uint8_t *ies = _ies; while (len >= 2 && len - 2 >= ies[1]) { @@ -1007,12 +1045,12 @@ handle_ies(struct module *mod, const void *_ies, size_t len) const char *ssid = (const char *)&ies[2]; const size_t ssid_len = ies[1]; - if (m->ssid == NULL || strncmp(m->ssid, ssid, ssid_len) != 0) - LOG_INFO("%s: SSID: %.*s", m->iface, (int)ssid_len, ssid); + if (iface->ssid == NULL || strncmp(iface->ssid, ssid, ssid_len) != 0) + LOG_INFO("%s: SSID: %.*s", iface->name, (int)ssid_len, ssid); mtx_lock(&mod->lock); - free(m->ssid); - m->ssid = strndup(ssid, ssid_len); + free(iface->ssid); + iface->ssid = strndup(ssid, ssid_len); mtx_unlock(&mod->lock); mod->bar->refresh(mod->bar); @@ -1034,10 +1072,9 @@ struct scan_results_context { }; static bool -handle_nl80211_bss(struct module *mod, uint16_t type, bool nested, +handle_nl80211_bss(struct module *mod, struct iface *iface, uint16_t type, bool nested, const void *payload, size_t len, void *_ctx) { - struct private *m UNUSED = mod->private; struct scan_results_context *ctx = _ctx; switch (type) { @@ -1049,7 +1086,7 @@ handle_nl80211_bss(struct module *mod, uint16_t type, bool nested, if (ctx->ies != NULL) { /* Deferred handling of BSS_INFORMATION_ELEMENTS */ - return handle_ies(mod, ctx->ies, ctx->ies_size); + return handle_ies(mod, iface, ctx->ies, ctx->ies_size); } } break; @@ -1057,7 +1094,7 @@ handle_nl80211_bss(struct module *mod, uint16_t type, bool nested, case NL80211_BSS_INFORMATION_ELEMENTS: if (ctx->associated) - return handle_ies(mod, payload, len); + return handle_ies(mod, iface, payload, len); else { /* * We’re either not associated, or, we haven’t seen the @@ -1076,16 +1113,14 @@ handle_nl80211_bss(struct module *mod, uint16_t type, bool nested, } static bool -handle_nl80211_scan_results(struct module *mod, uint16_t type, bool nested, - const void *payload, size_t len) +handle_nl80211_scan_results(struct module *mod, struct iface *iface, uint16_t type, bool nested, + const void *payload, size_t len, void *_ctx) { - struct private *m UNUSED = mod->private; - struct scan_results_context ctx = {0}; switch (type) { case NL80211_ATTR_BSS: - foreach_nlattr_nested(mod, payload, len, &handle_nl80211_bss, &ctx); + foreach_nlattr_nested(mod, iface, payload, len, &handle_nl80211_bss, &ctx); break; } @@ -1131,21 +1166,38 @@ netlink_receive_messages(int sock, void **reply, size_t *len) } static void -handle_stats(struct module *mod, struct rt_stats_msg *msg) +handle_stats(struct module *mod, uint32_t seq, struct rt_stats_msg *msg) { struct private *m = mod->private; + + struct iface *iface = NULL; + + tll_foreach(m->ifaces, it) { + if (seq != it->item.get_stats_seq_nr) + continue; + iface = &it->item; + /* Current request is now considered complete */ + iface->get_stats_seq_nr = 0; + break; + } + + if (iface == NULL) { + LOG_ERR("Couldn't find iface"); + return; + } + uint64_t ul_bits = msg->stats.tx_bytes * 8; uint64_t dl_bits = msg->stats.rx_bytes * 8; const double poll_interval_secs = (double)m->poll_interval / 1000.; - if (m->ul_bits != 0) - m->ul_speed = (double)(ul_bits - m->ul_bits) / poll_interval_secs; - if (m->dl_bits != 0) - m->dl_speed = (double)(dl_bits - m->dl_bits) / poll_interval_secs; + if (iface->ul_bits != 0) + iface->ul_speed = (double)(ul_bits - iface->ul_bits) / poll_interval_secs; + if (iface->dl_bits != 0) + iface->dl_speed = (double)(dl_bits - iface->dl_bits) / poll_interval_secs; - m->ul_bits = ul_bits; - m->dl_bits = dl_bits; + iface->ul_bits = ul_bits; + iface->dl_bits = dl_bits; } static bool @@ -1155,15 +1207,12 @@ parse_rt_reply(struct module *mod, const struct nlmsghdr *hdr, size_t len) /* Process response */ for (; NLMSG_OK(hdr, len); hdr = NLMSG_NEXT(hdr, len)) { + switch (hdr->nlmsg_type) { case NLMSG_DONE: - if (m->ifindex == -1) { - LOG_ERR("%s: failed to find interface", m->iface); - return false; - } /* Request initial list of IPv4/6 addresses */ - if (m->get_addresses && m->ifindex != -1) { + if (m->get_addresses) { m->get_addresses = false; send_rt_request(m, RTM_GETADDR); } @@ -1188,20 +1237,20 @@ parse_rt_reply(struct module *mod, const struct nlmsghdr *hdr, size_t len) } case RTM_NEWSTATS: { struct rt_stats_msg *msg = NLMSG_DATA(hdr); - handle_stats(mod, msg); + handle_stats(mod, hdr->nlmsg_seq, msg); break; } case NLMSG_ERROR:{ const struct nlmsgerr *err = NLMSG_DATA(hdr); - LOG_ERRNO_P(-err->error, "%s: netlink RT reply", m->iface); + LOG_ERRNO_P(-err->error, "netlink RT reply"); return false; } default: LOG_WARN( - "%s: unrecognized netlink message type: 0x%x", - m->iface, hdr->nlmsg_type); + "unrecognized netlink message type: 0x%x", + hdr->nlmsg_type); return false; } } @@ -1213,29 +1262,35 @@ static bool parse_genl_reply(struct module *mod, const struct nlmsghdr *hdr, size_t len) { struct private *m = mod->private; + struct iface *iface = NULL; for (; NLMSG_OK(hdr, len); hdr = NLMSG_NEXT(hdr, len)) { + if (hdr->nlmsg_type == GENL_ID_CTRL) { + assert(hdr->nlmsg_seq == 1); + const struct genlmsghdr *genl = NLMSG_DATA(hdr); + const size_t msg_size = NLMSG_PAYLOAD(hdr, 0); + foreach_nlattr(mod, NULL, genl, msg_size, &handle_genl_ctrl, NULL); + continue; + } + if (hdr->nlmsg_seq == m->nl80211.get_interface_seq_nr) { /* Current request is now considered complete */ m->nl80211.get_interface_seq_nr = 0; + + /* Can’t issue both get-station and get-scan at the + * same time. So, always run a get-scan when a + * get-station is complete */ + send_nl80211_get_scan(m); } if (hdr->nlmsg_type == NLMSG_DONE) { - if (hdr->nlmsg_seq == m->nl80211.get_station_seq_nr) { - /* Current request is now considered complete */ - m->nl80211.get_station_seq_nr = 0; - } - - else if (hdr->nlmsg_seq == m->nl80211.get_scan_seq_nr) { + if (hdr->nlmsg_seq == m->nl80211.get_scan_seq_nr) { /* Current request is now considered complete */ m->nl80211.get_scan_seq_nr = 0; - } - } - else if (hdr->nlmsg_type == GENL_ID_CTRL) { - const struct genlmsghdr *genl = NLMSG_DATA(hdr); - const size_t msg_size = NLMSG_PAYLOAD(hdr, 0); - foreach_nlattr(mod, genl, msg_size, &handle_genl_ctrl); + tll_foreach(m->ifaces, it) + send_nl80211_get_station(m, &it->item); + } } else if (hdr->nlmsg_type == m->nl80211.family_id) { @@ -1244,11 +1299,12 @@ parse_genl_reply(struct module *mod, const struct nlmsghdr *hdr, size_t len) switch (genl->cmd) { case NL80211_CMD_NEW_INTERFACE: - if (nl80211_is_for_us(mod, genl, msg_size)) { - LOG_DBG("%s: got interface information", m->iface); - foreach_nlattr( - mod, genl, msg_size, &handle_nl80211_new_interface); - } + if (foreach_nlattr(mod, NULL, genl, msg_size, &find_nl80211_iface, &iface)) + continue; + + LOG_DBG("%s: got interface information", iface->name); + foreach_nlattr( + mod, iface, genl, msg_size, &handle_nl80211_new_interface, NULL); break; case NL80211_CMD_CONNECT: @@ -1262,49 +1318,43 @@ parse_genl_reply(struct module *mod, const struct nlmsghdr *hdr, size_t len) * * Thus, we need to explicitly request an update. */ - if (nl80211_is_for_us(mod, genl, msg_size)) { - LOG_DBG("%s: connected, requesting interface information", - m->iface); - send_nl80211_get_interface(m); - send_nl80211_get_station(m); - } + LOG_DBG("connected, requesting interface information"); + send_nl80211_get_interface(m); break; case NL80211_CMD_DISCONNECT: - if (nl80211_is_for_us(mod, genl, msg_size)) { - LOG_DBG("%s: disconnected, resetting SSID etc", m->iface); + if (foreach_nlattr(mod, NULL, genl, msg_size, &find_nl80211_iface, &iface)) + continue; - mtx_lock(&mod->lock); - free(m->ssid); - m->ssid = NULL; - m->signal_strength_dbm = 0; - m->rx_bitrate = m->tx_bitrate = 0; - mtx_unlock(&mod->lock); - } + LOG_DBG("%s: disconnected, resetting SSID etc", iface->name); + + mtx_lock(&mod->lock); + free(iface->ssid); + iface->ssid = NULL; + iface->signal_strength_dbm = 0; + iface->rx_bitrate = iface->tx_bitrate = 0; + mtx_unlock(&mod->lock); break; case NL80211_CMD_NEW_STATION: - if (nl80211_is_for_us(mod, genl, msg_size)) { - LOG_DBG("%s: got station information", m->iface); - foreach_nlattr(mod, genl, msg_size, &handle_nl80211_new_station); - } + if (foreach_nlattr(mod, NULL, genl, msg_size, &find_nl80211_iface, &iface)) + continue; + + LOG_DBG("%s: got station information", iface->name); + foreach_nlattr(mod, iface, genl, msg_size, &handle_nl80211_new_station, NULL); LOG_DBG("%s: signal: %d dBm, RX=%u Mbit/s, TX=%u Mbit/s", - m->iface, m->signal_strength_dbm, - m->rx_bitrate / 1000 / 1000, - m->tx_bitrate / 1000 / 1000); - - /* Can’t issue both get-station and get-scan at the - * same time. So, always run a get-scan when a - * get-station is complete */ - send_nl80211_get_scan(m); + iface->name, iface->signal_strength_dbm, + iface->rx_bitrate / 1000 / 1000, + iface->tx_bitrate / 1000 / 1000); break; case NL80211_CMD_NEW_SCAN_RESULTS: - if (nl80211_is_for_us(mod, genl, msg_size)) { - LOG_DBG("%s: got scan results", m->iface); - foreach_nlattr(mod, genl, msg_size, &handle_nl80211_scan_results); - } + if (foreach_nlattr(mod, NULL, genl, msg_size, &find_nl80211_iface, &iface)) + continue; + + LOG_DBG("%s: got scan results", iface->name); + foreach_nlattr(mod, iface, genl, msg_size, &handle_nl80211_scan_results, NULL); break; default: @@ -1321,15 +1371,16 @@ parse_genl_reply(struct module *mod, const struct nlmsghdr *hdr, size_t len) ; /* iface is not an nl80211 device */ else if (nl_errno == ENOENT) ; /* iface down? */ - else - LOG_ERRNO_P(nl_errno, "%s: nl80211 reply (seq-nr: %u)", - m->iface, hdr->nlmsg_seq); + else { + LOG_ERRNO_P(nl_errno, "nl80211 reply (seq-nr: %u)", + hdr->nlmsg_seq); + } } else { LOG_WARN( - "%s: unrecognized netlink message type: 0x%x", - m->iface, hdr->nlmsg_type); + "unrecognized netlink message type: 0x%x", + hdr->nlmsg_type); return false; } } @@ -1347,7 +1398,7 @@ run(struct module *mod) if (m->poll_interval > 0) { timer_fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC); if (timer_fd < 0) { - LOG_ERRNO("%s: failed to create poll timer FD", m->iface); + LOG_ERRNO("failed to create poll timer FD"); goto out; } @@ -1360,7 +1411,7 @@ run(struct module *mod) }; if (timerfd_settime(timer_fd, 0, &poll_time, NULL) < 0) { - LOG_ERRNO("%s: failed to arm poll timer", m->iface); + LOG_ERRNO("failed to arm poll timer"); goto out; } } @@ -1394,12 +1445,12 @@ run(struct module *mod) if ((fds[1].revents & POLLHUP) || (fds[2].revents & POLLHUP)) { - LOG_ERR("%s: disconnected from netlink socket", m->iface); + LOG_ERR("disconnected from netlink socket"); break; } if (fds[3].revents & POLLHUP) { - LOG_ERR("%s: disconnected from timer FD", m->iface); + LOG_ERR("disconnected from timer FD"); break; } @@ -1442,8 +1493,10 @@ run(struct module *mod) break; } - send_nl80211_get_station(m); - send_rt_getstats_request(m); + tll_foreach(m->ifaces, it) { + send_nl80211_get_station(m, &it->item); + send_rt_getstats_request(m, &it->item); + }; } } @@ -1461,7 +1514,7 @@ run(struct module *mod) } static struct module * -network_new(const char *iface, struct particle *label, int poll_interval) +network_new(struct particle *label, int poll_interval, int left_spacing, int right_spacing) { int urandom_fd = open("/dev/urandom", O_RDONLY); if (urandom_fd < 0) { @@ -1470,17 +1523,16 @@ network_new(const char *iface, struct particle *label, int poll_interval) } struct private *priv = calloc(1, sizeof(*priv)); - priv->iface = strdup(iface); priv->label = label; priv->poll_interval = poll_interval; + priv->left_spacing = left_spacing; + priv->right_spacing = right_spacing; priv->genl_sock = -1; priv->rt_sock = -1; priv->urandom_fd = urandom_fd; + priv->get_addresses = false; priv->nl80211.family_id = -1; - priv->get_addresses = true; - priv->ifindex = -1; - priv->state = IF_OPER_DOWN; struct module *mod = module_common_new(); mod->private = priv; @@ -1494,13 +1546,20 @@ network_new(const char *iface, struct particle *label, int poll_interval) static struct module * from_conf(const struct yml_node *node, struct conf_inherit inherited) { - const struct yml_node *name = yml_get_value(node, "name"); const struct yml_node *content = yml_get_value(node, "content"); const struct yml_node *poll = yml_get_value(node, "poll-interval"); + const struct yml_node *spacing = yml_get_value(node, "spacing"); + const struct yml_node *left_spacing = yml_get_value(node, "left-spacing"); + const struct yml_node *right_spacing = yml_get_value(node, "right-spacing"); + + int left = spacing != NULL ? yml_value_as_int(spacing) : + left_spacing != NULL ? yml_value_as_int(left_spacing) : 0; + int right = spacing != NULL ? yml_value_as_int(spacing) : + right_spacing != NULL ? yml_value_as_int(right_spacing) : 0; return network_new( - yml_value_as_string(name), conf_to_particle(content, inherited), - poll != NULL ? yml_value_as_int(poll) : 0); + conf_to_particle(content, inherited), + poll != NULL ? yml_value_as_int(poll) : 0, left, right); } static bool @@ -1523,7 +1582,9 @@ static bool verify_conf(keychain_t *chain, const struct yml_node *node) { static const struct attr_info attrs[] = { - {"name", true, &conf_verify_string}, + {"spacing", false, &conf_verify_unsigned}, + {"left-spacing", false, &conf_verify_unsigned}, + {"right-spacing", false, &conf_verify_unsigned}, {"poll-interval", false, &conf_verify_poll_interval}, MODULE_COMMON_ATTRS, }; From 53dec73ed2c6be7411551db5533860c928e557f4 Mon Sep 17 00:00:00 2001 From: Delgan Date: Sun, 31 Mar 2024 18:53:57 +0000 Subject: [PATCH 536/611] Fix miscalculation of list width in presence of empty particles --- CHANGELOG.md | 2 ++ particles/dynlist.c | 7 ++++--- particles/list.c | 7 ++++--- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 44f1dad..b181fca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,6 +47,7 @@ * script: buffer overflow when reading large amounts of data. * i3/sway: module fails when reloading config file ([#361][361]). * Worked around bug in gcc causing a compilation error ([#350][350]). +* Miscalculation of list width in presence of empty particles ([#369][369]). [311]: https://codeberg.org/dnkl/yambar/issues/311 [302]: https://codeberg.org/dnkl/yambar/issues/302 @@ -55,6 +56,7 @@ [352]: https://codeberg.org/dnkl/yambar/issues/352 [361]: https://codeberg.org/dnkl/yambar/issues/361 [350]: https://codeberg.org/dnkl/yambar/issues/350 +[369]: https://codeberg.org/dnkl/yambar/issues/369 ### Security diff --git a/particles/dynlist.c b/particles/dynlist.c index 5b64dbe..85cad7b 100644 --- a/particles/dynlist.c +++ b/particles/dynlist.c @@ -71,7 +71,8 @@ dynlist_expose(const struct exposable *exposable, pixman_image_t *pix, int x, in for (size_t i = 0; i < e->count; i++) { const struct exposable *ee = e->exposables[i]; ee->expose(ee, pix, x + left_spacing, y, height); - x += left_spacing + e->widths[i] + right_spacing; + if (e->widths[i] > 0) + x += left_spacing + e->widths[i] + right_spacing; } } @@ -95,8 +96,8 @@ on_mouse(struct exposable *exposable, struct bar *bar, } return; } - - px += e->left_spacing + e->exposables[i]->width + e->right_spacing; + if (e->exposables[i]->width > 0) + px += e->left_spacing + e->exposables[i]->width + e->right_spacing; } LOG_DBG("on_mouse missed all sub-particles"); diff --git a/particles/list.c b/particles/list.c index a2c37c6..2887d3c 100644 --- a/particles/list.c +++ b/particles/list.c @@ -80,7 +80,8 @@ expose(const struct exposable *exposable, pixman_image_t *pix, int x, int y, int for (size_t i = 0; i < e->count; i++) { const struct exposable *ee = e->exposables[i]; ee->expose(ee, pix, x + left_spacing, y, height); - x += left_spacing + e->widths[i] + right_spacing; + if (e->widths[i] > 0) + x += left_spacing + e->widths[i] + right_spacing; } } @@ -109,8 +110,8 @@ on_mouse(struct exposable *exposable, struct bar *bar, } return; } - - px += e->left_spacing + e->exposables[i]->width + e->right_spacing; + if (e->exposables[i]->width > 0) + px += e->left_spacing + e->exposables[i]->width + e->right_spacing; } /* We're between sub-particles (or in the left/right margin) */ From 58c397d15416faa50c76edef08f334ede293f17a Mon Sep 17 00:00:00 2001 From: Delgan Date: Mon, 1 Apr 2024 08:39:17 +0000 Subject: [PATCH 537/611] Fix CI failing due to outdated test config file --- test/full-conf-good.yml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/test/full-conf-good.yml b/test/full-conf-good.yml index d89d494..a6f6e99 100644 --- a/test/full-conf-good.yml +++ b/test/full-conf-good.yml @@ -39,8 +39,13 @@ bar: host: 127.0.0.1 content: {string: {text: "{state}"}} - network: - name: ldsjfdf - content: {string: {text: "{name}"}} + content: + map: + default: + string: {text: "{name}: {state} ({ipv4})"} + conditions: + ipv4 == "": + string: {text: "{name}: {state}"} - removables: content: {string: {text: "{label}"}} # - xkb: From da19c091222c0af48795db14550093786fa28b64 Mon Sep 17 00:00:00 2001 From: Delgan Date: Mon, 1 Apr 2024 08:53:50 +0000 Subject: [PATCH 538/611] Add missing "dynlist" dependency to network module --- modules/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/meson.build b/modules/meson.build index f8d1e80..e2ed56e 100644 --- a/modules/meson.build +++ b/modules/meson.build @@ -98,7 +98,7 @@ if plugin_label_enabled endif if plugin_network_enabled - mod_data += {'network': [[], []]} + mod_data += {'network': [[], [dynlist]]} endif if plugin_pipewire_enabled From 28a18ad91e67e67d634f82d431c6b2ceab905640 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 5 Apr 2024 16:11:37 +0200 Subject: [PATCH 539/611] log: fix syslog not respecting the configured log level --- CHANGELOG.md | 1 + log.c | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b181fca..2d01729 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -48,6 +48,7 @@ * i3/sway: module fails when reloading config file ([#361][361]). * Worked around bug in gcc causing a compilation error ([#350][350]). * Miscalculation of list width in presence of empty particles ([#369][369]). +* Log-level not respected by syslog. [311]: https://codeberg.org/dnkl/yambar/issues/311 [302]: https://codeberg.org/dnkl/yambar/issues/302 diff --git a/log.c b/log.c index 5864853..2d454a8 100644 --- a/log.c +++ b/log.c @@ -15,7 +15,7 @@ #define UNUSED __attribute__((unused)) static bool colorize = false; -static bool do_syslog = true; +static bool do_syslog = false; static enum log_class log_level = LOG_CLASS_NONE; static const struct { @@ -102,6 +102,9 @@ _sys_log(enum log_class log_class, const char *module, if (!do_syslog) return; + if (log_class > log_level) + return; + /* Map our log level to syslog's level */ int level = log_level_map[log_class].syslog_equivalent; From d841aeeecd41077a9ff51ea184dc926f7181d90b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 6 Apr 2024 15:39:19 +0200 Subject: [PATCH 540/611] config: layer: add 'overlay' and 'background' The layer option (Wayland only) now accepts 'overlay' and 'background'. Closes #372 --- CHANGELOG.md | 3 +++ bar/bar.h | 2 +- bar/wayland.c | 23 ++++++++++++++++++++--- config-verify.c | 4 +++- config.c | 6 +++++- doc/yambar.5.scd | 3 ++- 6 files changed, 34 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d01729..09f8c93 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,8 +20,11 @@ * network: new `quality` tag (Wi-Fi only). * Read alternative config from pipes and FIFOs (e.g. `--config /dev/stdin`) ([#340][340]). +* Added `overlay` and `background` as possible `layer` values + ([#372][372]). [340]: https://codeberg.org/dnkl/yambar/pulls/340 +[372]: https://codeberg.org/dnkl/yambar/issues/372 ### Changed diff --git a/bar/bar.h b/bar/bar.h index 717b690..ce91247 100644 --- a/bar/bar.h +++ b/bar/bar.h @@ -18,7 +18,7 @@ struct bar { }; enum bar_location { BAR_TOP, BAR_BOTTOM }; -enum bar_layer { BAR_LAYER_TOP, BAR_LAYER_BOTTOM }; +enum bar_layer { BAR_LAYER_OVERLAY, BAR_LAYER_TOP, BAR_LAYER_BOTTOM, BAR_LAYER_BACKGROUND }; enum bar_backend { BAR_BACKEND_AUTO, BAR_BACKEND_XCB, BAR_BACKEND_WAYLAND }; struct bar_config { diff --git a/bar/wayland.c b/bar/wayland.c index 22ca3e2..7b6575b 100644 --- a/bar/wayland.c +++ b/bar/wayland.c @@ -814,9 +814,26 @@ create_surface(struct wayland_backend *backend) wl_surface_add_listener(backend->surface, &surface_listener, backend); - enum zwlr_layer_shell_v1_layer layer = bar->layer == BAR_LAYER_BOTTOM - ? ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM - : ZWLR_LAYER_SHELL_V1_LAYER_TOP; + + enum zwlr_layer_shell_v1_layer layer; + + switch (bar->layer) { + case BAR_LAYER_BACKGROUND: + layer = ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND; + break; + + case BAR_LAYER_BOTTOM: + layer = ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM; + break; + + case BAR_LAYER_TOP: + layer = ZWLR_LAYER_SHELL_V1_LAYER_TOP; + break; + + case BAR_LAYER_OVERLAY: + layer = ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY; + break; + } backend->layer_surface = zwlr_layer_shell_v1_get_layer_surface( backend->layer_shell, backend->surface, diff --git a/config-verify.c b/config-verify.c index 9b4fad7..d4211f1 100644 --- a/config-verify.c +++ b/config-verify.c @@ -453,7 +453,9 @@ verify_bar_location(keychain_t *chain, const struct yml_node *node) static bool verify_bar_layer(keychain_t *chain, const struct yml_node *node) { - return conf_verify_enum(chain, node, (const char *[]){"top", "bottom"}, 2); + return conf_verify_enum( + chain, node, + (const char *[]){"overlay", "top", "bottom", "background"}, 4); } bool diff --git a/config.c b/config.c index 2ad305f..a0eba59 100644 --- a/config.c +++ b/config.c @@ -340,10 +340,14 @@ conf_to_bar(const struct yml_node *bar, enum bar_backend backend) const struct yml_node *layer = yml_get_value(bar, "layer"); if (layer != NULL) { const char *tmp = yml_value_as_string(layer); - if (strcmp(tmp, "top") == 0) + if (strcmp(tmp, "overlay") == 0) + conf.layer = BAR_LAYER_OVERLAY; + else if (strcmp(tmp, "top") == 0) conf.layer = BAR_LAYER_TOP; else if (strcmp(tmp, "bottom") == 0) conf.layer = BAR_LAYER_BOTTOM; + else if (strcmp(tmp, "background") == 0) + conf.layer = BAR_LAYER_BACKGROUND; else assert(false); } diff --git a/doc/yambar.5.scd b/doc/yambar.5.scd index 3cc9557..1b06939 100644 --- a/doc/yambar.5.scd +++ b/doc/yambar.5.scd @@ -49,7 +49,8 @@ types that are frequently used: | layer : string : no -: Layer to put bar on. One of _top_ or _bottom_. Wayland only +: Layer to put bar on. One of _overlay_, _top_, _bottom_ or + _background_. Wayland only. Default: _bottom_. | left-spacing : int : no From b85ba99980312114b591d5ae837afe20db5e4480 Mon Sep 17 00:00:00 2001 From: Delgan Date: Sun, 7 Apr 2024 10:05:10 +0200 Subject: [PATCH 541/611] Apply "clang-format" preferences globally --- .clang-format | 7 + bar/backend.h | 6 +- bar/bar.c | 77 +++----- bar/private.h | 3 +- bar/wayland.c | 366 +++++++++++++++---------------------- bar/xcb.c | 208 ++++++++------------- char32.c | 15 +- char32.h | 2 +- config-verify.c | 115 ++++-------- config-verify.h | 7 +- config.c | 113 ++++-------- config.h | 7 +- decoration.h | 9 +- decorations/background.c | 11 +- decorations/border.c | 32 ++-- decorations/overline.c | 9 +- decorations/stack.c | 17 +- decorations/underline.c | 10 +- log.c | 42 ++--- log.h | 71 +++---- main.c | 53 +++--- module.c | 2 +- module.h | 12 +- modules/alsa.c | 223 +++++++++------------- modules/backlight.c | 23 ++- modules/battery.c | 105 +++++------ modules/clock.c | 50 ++--- modules/cpu.c | 68 +++---- modules/disk-io.c | 56 +++--- modules/dwl.c | 16 +- modules/foreign-toplevel.c | 149 +++++++-------- modules/i3-common.c | 57 +++--- modules/i3-common.h | 6 +- modules/i3.c | 214 +++++++++------------- modules/label.c | 8 +- modules/mem.c | 7 +- modules/mpd.c | 126 ++++++------- modules/network.c | 317 +++++++++++++++----------------- modules/pipewire.c | 8 +- modules/pulse.c | 87 +++------ modules/removables.c | 224 +++++++++++------------ modules/river.c | 174 +++++++----------- modules/script.c | 86 +++------ modules/sway-xkb.c | 49 ++--- modules/xkb.c | 145 +++++---------- modules/xwindow.c | 80 ++++---- particle.c | 63 +++---- particle.h | 47 ++--- particles/dynlist.c | 16 +- particles/dynlist.h | 4 +- particles/empty.c | 5 +- particles/list.c | 43 ++--- particles/map.c | 180 +++++++++--------- particles/map.h | 2 +- particles/progress-bar.c | 59 +++--- particles/ramp.c | 56 +++--- particles/string.c | 48 ++--- plugin.c | 132 +++++++------ plugin.h | 8 +- tag.c | 118 ++++++------ tag.h | 17 +- xcb.c | 82 ++++----- yml.c | 186 ++++++++----------- yml.h | 8 +- 64 files changed, 1868 insertions(+), 2678 deletions(-) diff --git a/.clang-format b/.clang-format index 528a36b..2436e90 100644 --- a/.clang-format +++ b/.clang-format @@ -3,6 +3,7 @@ BasedOnStyle: GNU IndentWidth: 4 --- Language: Cpp +Standard: Auto PointerAlignment: Right ColumnLimit: 120 BreakBeforeBraces: Custom @@ -15,3 +16,9 @@ BraceWrapping: SpaceBeforeParens: ControlStatements Cpp11BracedListStyle: true + +WhitespaceSensitiveMacros: + - REGISTER_CORE_PARTICLE + - REGISTER_CORE_DECORATION + - REGISTER_CORE_PLUGIN + - REGISTER_CORE_MODULE diff --git a/bar/backend.h b/bar/backend.h index 47fae95..b7a9fcb 100644 --- a/bar/backend.h +++ b/bar/backend.h @@ -7,10 +7,8 @@ struct backend { bool (*setup)(struct bar *bar); void (*cleanup)(struct bar *bar); - void (*loop)(struct bar *bar, - void (*expose)(const struct bar *bar), - void (*on_mouse)(struct bar *bar, enum mouse_event event, - enum mouse_button btn, int x, int y)); + void (*loop)(struct bar *bar, void (*expose)(const struct bar *bar), + void (*on_mouse)(struct bar *bar, enum mouse_event event, enum mouse_button btn, int x, int y)); void (*commit)(const struct bar *bar); void (*refresh)(const struct bar *bar); void (*set_cursor)(struct bar *bar, const char *cursor); diff --git a/bar/bar.c b/bar/bar.c index 10e0785..3890bc0 100644 --- a/bar/bar.c +++ b/bar/bar.c @@ -1,15 +1,15 @@ #include "bar.h" #include "private.h" -#include -#include -#include -#include -#include -#include #include -#include #include +#include +#include +#include +#include +#include +#include +#include #include @@ -18,11 +18,11 @@ #include "../log.h" #if defined(ENABLE_X11) - #include "xcb.h" +#include "xcb.h" #endif #if defined(ENABLE_WAYLAND) - #include "wayland.h" +#include "wayland.h" #endif #define max(x, y) ((x) > (y) ? (x) : (y)) @@ -75,9 +75,8 @@ expose(const struct bar *_bar) const struct private *bar = _bar->private; pixman_image_t *pix = bar->pix; - pixman_image_fill_rectangles( - PIXMAN_OP_SRC, pix, &bar->background, 1, - &(pixman_rectangle16_t){0, 0, bar->width, bar->height_with_border}); + pixman_image_fill_rectangles(PIXMAN_OP_SRC, pix, &bar->background, 1, + &(pixman_rectangle16_t){0, 0, bar->width, bar->height_with_border}); pixman_image_fill_rectangles( PIXMAN_OP_OVER, pix, &bar->border.color, 4, @@ -86,20 +85,15 @@ expose(const struct bar *_bar) {0, 0, bar->border.left_width, bar->height_with_border}, /* Right */ - {bar->width - bar->border.right_width, - 0, bar->border.right_width, bar->height_with_border}, + {bar->width - bar->border.right_width, 0, bar->border.right_width, bar->height_with_border}, /* Top */ - {bar->border.left_width, - 0, - bar->width - bar->border.left_width - bar->border.right_width, + {bar->border.left_width, 0, bar->width - bar->border.left_width - bar->border.right_width, bar->border.top_width}, /* Bottom */ - {bar->border.left_width, - bar->height_with_border - bar->border.bottom_width, - bar->width - bar->border.left_width - bar->border.right_width, - bar->border.bottom_width}, + {bar->border.left_width, bar->height_with_border - bar->border.bottom_width, + bar->width - bar->border.left_width - bar->border.right_width, bar->border.bottom_width}, }); for (size_t i = 0; i < bar->left.count; i++) { @@ -136,12 +130,8 @@ expose(const struct bar *_bar) int x = bar->border.left_width + bar->left_margin - bar->left_spacing; pixman_region32_t clip; pixman_region32_init_rect( - &clip, - bar->border.left_width + bar->left_margin, - bar->border.top_width, - (bar->width - - bar->left_margin - bar->right_margin - - bar->border.left_width - bar->border.right_width), + &clip, bar->border.left_width + bar->left_margin, bar->border.top_width, + (bar->width - bar->left_margin - bar->right_margin - bar->border.left_width - bar->border.right_width), bar->height); pixman_image_set_clip_region32(pix, &clip); pixman_region32_fini(&clip); @@ -161,11 +151,7 @@ expose(const struct bar *_bar) x += bar->left_spacing + e->width + bar->right_spacing; } - x = bar->width - ( - right_width + - bar->left_spacing + - bar->right_margin + - bar->border.right_width); + x = bar->width - (right_width + bar->left_spacing + bar->right_margin + bar->border.right_width); for (size_t i = 0; i < bar->right.count; i++) { const struct exposable *e = bar->right.exps[i]; @@ -177,7 +163,6 @@ expose(const struct bar *_bar) bar->backend.iface->commit(_bar); } - static void refresh(const struct bar *bar) { @@ -200,15 +185,12 @@ output_name(const struct bar *bar) } static void -on_mouse(struct bar *_bar, enum mouse_event event, enum mouse_button btn, - int x, int y) +on_mouse(struct bar *_bar, enum mouse_event event, enum mouse_button btn, int x, int y) { struct private *bar = _bar->private; - if ((y < bar->border.top_width || - y >= (bar->height_with_border - bar->border.bottom_width)) || - (x < bar->border.left_width || x >= (bar->width - bar->border.right_width))) - { + if ((y < bar->border.top_width || y >= (bar->height_with_border - bar->border.bottom_width)) + || (x < bar->border.left_width || x >= (bar->width - bar->border.right_width))) { set_cursor(_bar, "left_ptr"); return; } @@ -250,10 +232,7 @@ on_mouse(struct bar *_bar, enum mouse_event event, enum mouse_button btn, mx += e->width + bar->right_spacing; } - mx = bar->width - (right_width + - bar->left_spacing + - bar->right_margin + - bar->border.right_width); + mx = bar->width - (right_width + bar->left_spacing + bar->right_margin + bar->border.right_width); for (size_t i = 0; i < bar->right.count; i++) { struct exposable *e = bar->right.exps[i]; @@ -294,8 +273,7 @@ run(struct bar *_bar) { struct private *bar = _bar->private; - bar->height_with_border = - bar->height + bar->border.top_width + bar->border.bottom_width; + bar->height_with_border = bar->height + bar->border.top_width + bar->border.bottom_width; if (!bar->backend.iface->setup(_bar)) { bar->backend.iface->cleanup(_bar); @@ -347,8 +325,7 @@ run(struct bar *_bar) thrd_join(thrd_left[i], &mod_ret); if (mod_ret != 0) { const struct module *m = bar->left.mods[i]; - LOG_ERR("module: LEFT #%zu (%s): non-zero exit value: %d", - i, m->description(m), mod_ret); + LOG_ERR("module: LEFT #%zu (%s): non-zero exit value: %d", i, m->description(m), mod_ret); } ret = ret == 0 && mod_ret != 0 ? mod_ret : ret; } @@ -356,8 +333,7 @@ run(struct bar *_bar) thrd_join(thrd_center[i], &mod_ret); if (mod_ret != 0) { const struct module *m = bar->center.mods[i]; - LOG_ERR("module: CENTER #%zu (%s): non-zero exit value: %d", - i, m->description(m), mod_ret); + LOG_ERR("module: CENTER #%zu (%s): non-zero exit value: %d", i, m->description(m), mod_ret); } ret = ret == 0 && mod_ret != 0 ? mod_ret : ret; } @@ -365,8 +341,7 @@ run(struct bar *_bar) thrd_join(thrd_right[i], &mod_ret); if (mod_ret != 0) { const struct module *m = bar->right.mods[i]; - LOG_ERR("module: RIGHT #%zu (%s): non-zero exit value: %d", - i, m->description(m), mod_ret); + LOG_ERR("module: RIGHT #%zu (%s): non-zero exit value: %d", i, m->description(m), mod_ret); } ret = ret == 0 && mod_ret != 0 ? mod_ret : ret; } diff --git a/bar/private.h b/bar/private.h index f216c99..d48b07f 100644 --- a/bar/private.h +++ b/bar/private.h @@ -3,7 +3,8 @@ #include "../bar/bar.h" #include "backend.h" -struct private { +struct private +{ /* From bar_config */ char *monitor; enum bar_layer layer; diff --git a/bar/wayland.c b/bar/wayland.c index 7b6575b..50dc62d 100644 --- a/bar/wayland.c +++ b/bar/wayland.c @@ -1,25 +1,25 @@ #include "wayland.h" +#include +#include +#include +#include #include #include #include -#include #include -#include -#include -#include -#include #include #include +#include #include #include #include #include -#include #include +#include #define LOG_MODULE "bar:wayland" #define LOG_ENABLE_DBG 0 @@ -29,7 +29,7 @@ #include "private.h" #if !defined(MFD_NOEXEC_SEAL) - #define MFD_NOEXEC_SEAL 0 +#define MFD_NOEXEC_SEAL 0 #endif struct buffer { @@ -116,16 +116,15 @@ struct wayland_backend { /* We're already waiting for a frame done callback */ bool render_scheduled; - tll(struct buffer) buffers; /* List of SHM buffers */ - struct buffer *next_buffer; /* Bar is rendering to this one */ - struct buffer *pending_buffer; /* Finished, but not yet rendered */ + tll(struct buffer) buffers; /* List of SHM buffers */ + struct buffer *next_buffer; /* Bar is rendering to this one */ + struct buffer *pending_buffer; /* Finished, but not yet rendered */ struct wl_callback *frame_callback; double aggregated_scroll; bool have_discrete; - void (*bar_on_mouse)(struct bar *bar, enum mouse_event event, - enum mouse_button btn, int x, int y); + void (*bar_on_mouse)(struct bar *bar, enum mouse_event event, enum mouse_button btn, int x, int y); }; static void @@ -157,7 +156,7 @@ bar_backend_wayland_new(void) static void shm_format(void *data, struct wl_shm *wl_shm, uint32_t format) { - //printf("SHM format: 0x%08x\n", format); + // printf("SHM format: 0x%08x\n", format); } static const struct wl_shm_listener shm_listener = { @@ -167,10 +166,7 @@ static const struct wl_shm_listener shm_listener = { static void update_cursor_surface(struct wayland_backend *backend, struct seat *seat) { - if (seat->pointer.serial == 0 || - seat->pointer.cursor == NULL || - seat->pointer.surface == NULL) - { + if (seat->pointer.serial == 0 || seat->pointer.cursor == NULL || seat->pointer.surface == NULL) { return; } @@ -179,17 +175,12 @@ update_cursor_surface(struct wayland_backend *backend, struct seat *seat) const int scale = seat->pointer.scale; wl_surface_set_buffer_scale(seat->pointer.surface, scale); - wl_surface_attach( - seat->pointer.surface, wl_cursor_image_get_buffer(image), 0, 0); + wl_surface_attach(seat->pointer.surface, wl_cursor_image_get_buffer(image), 0, 0); - wl_pointer_set_cursor( - seat->wl_pointer, seat->pointer.serial, - seat->pointer.surface, - image->hotspot_x / scale, image->hotspot_y / scale); + wl_pointer_set_cursor(seat->wl_pointer, seat->pointer.serial, seat->pointer.surface, image->hotspot_x / scale, + image->hotspot_y / scale); - - wl_surface_damage_buffer( - seat->pointer.surface, 0, 0, INT32_MAX, INT32_MAX); + wl_surface_damage_buffer(seat->pointer.surface, 0, 0, INT32_MAX, INT32_MAX); wl_surface_commit(seat->pointer.surface); wl_display_flush(backend->display); @@ -219,11 +210,9 @@ reload_cursor_theme(struct seat *seat, int new_scale) } } - LOG_INFO("%s: cursor theme: %s, size: %u, scale: %d", - seat->name, cursor_theme, cursor_size, new_scale); + LOG_INFO("%s: cursor theme: %s, size: %u, scale: %d", seat->name, cursor_theme, cursor_size, new_scale); - struct wl_cursor_theme *theme = wl_cursor_theme_load( - cursor_theme, cursor_size * new_scale, seat->backend->shm); + struct wl_cursor_theme *theme = wl_cursor_theme_load(cursor_theme, cursor_size * new_scale, seat->backend->shm); if (theme == NULL) { LOG_ERR("%s: failed to load cursor theme", seat->name); @@ -235,8 +224,7 @@ reload_cursor_theme(struct seat *seat, int new_scale) } static void -wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, - uint32_t serial, struct wl_surface *surface, +wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface, wl_fixed_t surface_x, wl_fixed_t surface_y) { struct seat *seat = data; @@ -252,8 +240,7 @@ wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, } static void -wl_pointer_leave(void *data, struct wl_pointer *wl_pointer, - uint32_t serial, struct wl_surface *surface) +wl_pointer_leave(void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface) { struct seat *seat = data; struct wayland_backend *backend = seat->backend; @@ -265,8 +252,7 @@ wl_pointer_leave(void *data, struct wl_pointer *wl_pointer, } static void -wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, - uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) +wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) { struct seat *seat = data; struct wayland_backend *backend = seat->backend; @@ -275,14 +261,12 @@ wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, seat->pointer.y = wl_fixed_to_int(surface_y) * backend->scale; backend->active_seat = seat; - backend->bar_on_mouse( - backend->bar, ON_MOUSE_MOTION, MOUSE_BTN_NONE, - seat->pointer.x, seat->pointer.y); + backend->bar_on_mouse(backend->bar, ON_MOUSE_MOTION, MOUSE_BTN_NONE, seat->pointer.x, seat->pointer.y); } static void -wl_pointer_button(void *data, struct wl_pointer *wl_pointer, - uint32_t serial, uint32_t time, uint32_t button, uint32_t state) +wl_pointer_button(void *data, struct wl_pointer *wl_pointer, uint32_t serial, uint32_t time, uint32_t button, + uint32_t state) { struct seat *seat = data; struct wayland_backend *backend = seat->backend; @@ -293,23 +277,31 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer, enum mouse_button btn; switch (button) { - case BTN_LEFT: btn = MOUSE_BTN_LEFT; break; - case BTN_MIDDLE: btn = MOUSE_BTN_MIDDLE; break; - case BTN_RIGHT: btn = MOUSE_BTN_RIGHT; break; - case BTN_SIDE: btn = MOUSE_BTN_PREVIOUS; break; - case BTN_EXTRA: btn = MOUSE_BTN_NEXT; break; + case BTN_LEFT: + btn = MOUSE_BTN_LEFT; + break; + case BTN_MIDDLE: + btn = MOUSE_BTN_MIDDLE; + break; + case BTN_RIGHT: + btn = MOUSE_BTN_RIGHT; + break; + case BTN_SIDE: + btn = MOUSE_BTN_PREVIOUS; + break; + case BTN_EXTRA: + btn = MOUSE_BTN_NEXT; + break; default: return; } - backend->bar_on_mouse( - backend->bar, ON_MOUSE_CLICK, btn, seat->pointer.x, seat->pointer.y); + backend->bar_on_mouse(backend->bar, ON_MOUSE_CLICK, btn, seat->pointer.x, seat->pointer.y); } } static void -wl_pointer_axis(void *data, struct wl_pointer *wl_pointer, - uint32_t time, uint32_t axis, wl_fixed_t value) +wl_pointer_axis(void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis, wl_fixed_t value) { if (axis != WL_POINTER_AXIS_VERTICAL_SCROLL) return; @@ -325,24 +317,18 @@ wl_pointer_axis(void *data, struct wl_pointer *wl_pointer, const double amount = wl_fixed_to_double(value); - if ((backend->aggregated_scroll > 0 && amount < 0) || - (backend->aggregated_scroll < 0 && amount > 0)) - { + if ((backend->aggregated_scroll > 0 && amount < 0) || (backend->aggregated_scroll < 0 && amount > 0)) { backend->aggregated_scroll = amount; } else backend->aggregated_scroll += amount; - enum mouse_button btn = backend->aggregated_scroll > 0 - ? MOUSE_BTN_WHEEL_DOWN - : MOUSE_BTN_WHEEL_UP; + enum mouse_button btn = backend->aggregated_scroll > 0 ? MOUSE_BTN_WHEEL_DOWN : MOUSE_BTN_WHEEL_UP; const double step = bar->trackpad_sensitivity; const double adjust = backend->aggregated_scroll > 0 ? -step : step; while (fabs(backend->aggregated_scroll) >= step) { - backend->bar_on_mouse( - backend->bar, ON_MOUSE_CLICK, btn, - seat->pointer.x, seat->pointer.y); + backend->bar_on_mouse(backend->bar, ON_MOUSE_CLICK, btn, seat->pointer.x, seat->pointer.y); backend->aggregated_scroll += adjust; } } @@ -356,14 +342,12 @@ wl_pointer_frame(void *data, struct wl_pointer *wl_pointer) } static void -wl_pointer_axis_source(void *data, struct wl_pointer *wl_pointer, - uint32_t axis_source) +wl_pointer_axis_source(void *data, struct wl_pointer *wl_pointer, uint32_t axis_source) { } static void -wl_pointer_axis_stop(void *data, struct wl_pointer *wl_pointer, - uint32_t time, uint32_t axis) +wl_pointer_axis_stop(void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis) { if (axis != WL_POINTER_AXIS_VERTICAL_SCROLL) return; @@ -374,8 +358,7 @@ wl_pointer_axis_stop(void *data, struct wl_pointer *wl_pointer, } static void -wl_pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer, - uint32_t axis, int32_t discrete) +wl_pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer, uint32_t axis, int32_t discrete) { if (axis != WL_POINTER_AXIS_VERTICAL_SCROLL) return; @@ -384,16 +367,12 @@ wl_pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer, struct wayland_backend *backend = seat->backend; backend->have_discrete = true; - enum mouse_button btn = discrete > 0 - ? MOUSE_BTN_WHEEL_DOWN - : MOUSE_BTN_WHEEL_UP; + enum mouse_button btn = discrete > 0 ? MOUSE_BTN_WHEEL_DOWN : MOUSE_BTN_WHEEL_UP; int count = abs(discrete); for (int32_t i = 0; i < count; i++) { - backend->bar_on_mouse( - backend->bar, ON_MOUSE_CLICK, btn, - seat->pointer.x, seat->pointer.y); + backend->bar_on_mouse(backend->bar, ON_MOUSE_CLICK, btn, seat->pointer.x, seat->pointer.y); } } @@ -410,16 +389,14 @@ static const struct wl_pointer_listener pointer_listener = { }; static void -seat_handle_capabilities(void *data, struct wl_seat *wl_seat, - enum wl_seat_capability caps) +seat_handle_capabilities(void *data, struct wl_seat *wl_seat, enum wl_seat_capability caps) { struct seat *seat = data; if (caps & WL_SEAT_CAPABILITY_POINTER) { if (seat->wl_pointer == NULL) { assert(seat->pointer.surface == NULL); - seat->pointer.surface = wl_compositor_create_surface( - seat->backend->compositor); + seat->pointer.surface = wl_compositor_create_surface(seat->backend->compositor); if (seat->pointer.surface == NULL) { LOG_ERR("%s: failed to create pointer surface", seat->name); @@ -459,10 +436,8 @@ static const struct wl_seat_listener seat_listener = { }; static void -output_geometry(void *data, struct wl_output *wl_output, int32_t x, int32_t y, - int32_t physical_width, int32_t physical_height, - int32_t subpixel, const char *make, const char *model, - int32_t transform) +output_geometry(void *data, struct wl_output *wl_output, int32_t x, int32_t y, int32_t physical_width, + int32_t physical_height, int32_t subpixel, const char *make, const char *model, int32_t transform) { struct monitor *mon = data; mon->width_mm = physical_width; @@ -470,8 +445,7 @@ output_geometry(void *data, struct wl_output *wl_output, int32_t x, int32_t y, } static void -output_mode(void *data, struct wl_output *wl_output, uint32_t flags, - int32_t width, int32_t height, int32_t refresh) +output_mode(void *data, struct wl_output *wl_output, uint32_t flags, int32_t width, int32_t height, int32_t refresh) { } @@ -516,8 +490,7 @@ output_name(void *data, struct wl_output *wl_output, const char *name) #if defined(WL_OUTPUT_DESCRIPTION_SINCE_VERSION) static void -output_description(void *data, struct wl_output *wl_output, - const char *description) +output_description(void *data, struct wl_output *wl_output, const char *description) { } #endif @@ -536,9 +509,7 @@ static const struct wl_output_listener output_listener = { }; static void -xdg_output_handle_logical_position(void *data, - struct zxdg_output_v1 *xdg_output, - int32_t x, int32_t y) +xdg_output_handle_logical_position(void *data, struct zxdg_output_v1 *xdg_output, int32_t x, int32_t y) { struct monitor *mon = data; mon->x = x; @@ -546,8 +517,7 @@ xdg_output_handle_logical_position(void *data, } static void -xdg_output_handle_logical_size(void *data, struct zxdg_output_v1 *xdg_output, - int32_t width, int32_t height) +xdg_output_handle_logical_size(void *data, struct zxdg_output_v1 *xdg_output, int32_t width, int32_t height) { struct monitor *mon = data; mon->width_px = width; @@ -562,9 +532,8 @@ xdg_output_handle_done(void *data, struct zxdg_output_v1 *xdg_output) { const struct monitor *mon = data; - LOG_INFO("monitor: %s: %dx%d+%d+%d (%dx%dmm)", - mon->name, mon->width_px, mon->height_px, - mon->x, mon->y, mon->width_mm, mon->height_mm); + LOG_INFO("monitor: %s: %dx%d+%d+%d (%dx%dmm)", mon->name, mon->width_px, mon->height_px, mon->x, mon->y, + mon->width_mm, mon->height_mm); struct wayland_backend *backend = mon->backend; struct private *bar = backend->bar->private; @@ -576,15 +545,11 @@ xdg_output_handle_done(void *data, struct zxdg_output_v1 *xdg_output) return; } - const bool output_is_our_configured_monitor = ( - bar->monitor != NULL && - mon->name != NULL && - strcmp(bar->monitor, mon->name) == 0); + const bool output_is_our_configured_monitor + = (bar->monitor != NULL && mon->name != NULL && strcmp(bar->monitor, mon->name) == 0); - const bool output_is_last_mapped = ( - backend->last_mapped_monitor != NULL && - mon->name != NULL && - strcmp(backend->last_mapped_monitor, mon->name) == 0); + const bool output_is_last_mapped = (backend->last_mapped_monitor != NULL && mon->name != NULL + && strcmp(backend->last_mapped_monitor, mon->name) == 0); if (output_is_our_configured_monitor) LOG_DBG("%s: using this monitor (user configured)", mon->name); @@ -606,8 +571,7 @@ xdg_output_handle_done(void *data, struct zxdg_output_v1 *xdg_output) } static void -xdg_output_handle_name(void *data, struct zxdg_output_v1 *xdg_output, - const char *name) +xdg_output_handle_name(void *data, struct zxdg_output_v1 *xdg_output, const char *name) { struct monitor *mon = data; free(mon->name); @@ -615,8 +579,7 @@ xdg_output_handle_name(void *data, struct zxdg_output_v1 *xdg_output, } static void -xdg_output_handle_description(void *data, struct zxdg_output_v1 *xdg_output, - const char *description) +xdg_output_handle_description(void *data, struct zxdg_output_v1 *xdg_output, const char *description) { } @@ -634,14 +597,12 @@ verify_iface_version(const char *iface, uint32_t version, uint32_t wanted) if (version >= wanted) return true; - LOG_ERR("%s: need interface version %u, but compositor only implements %u", - iface, wanted, version); + LOG_ERR("%s: need interface version %u, but compositor only implements %u", iface, wanted, version); return false; } static void -handle_global(void *data, struct wl_registry *registry, - uint32_t name, const char *interface, uint32_t version) +handle_global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { LOG_DBG("global: 0x%08x, interface=%s, version=%u", name, interface, version); struct wayland_backend *backend = data; @@ -651,8 +612,7 @@ handle_global(void *data, struct wl_registry *registry, if (!verify_iface_version(interface, version, required)) return; - backend->compositor = wl_registry_bind( - registry, name, &wl_compositor_interface, required); + backend->compositor = wl_registry_bind(registry, name, &wl_compositor_interface, required); } else if (strcmp(interface, wl_shm_interface.name) == 0) { @@ -660,8 +620,7 @@ handle_global(void *data, struct wl_registry *registry, if (!verify_iface_version(interface, version, required)) return; - backend->shm = wl_registry_bind( - registry, name, &wl_shm_interface, required); + backend->shm = wl_registry_bind(registry, name, &wl_shm_interface, required); wl_shm_add_listener(backend->shm, &shm_listener, backend); } @@ -670,13 +629,9 @@ handle_global(void *data, struct wl_registry *registry, if (!verify_iface_version(interface, version, required)) return; - struct wl_output *output = wl_registry_bind( - registry, name, &wl_output_interface, required); + struct wl_output *output = wl_registry_bind(registry, name, &wl_output_interface, required); - tll_push_back(backend->monitors, ((struct monitor){ - .backend = backend, - .wl_name = name, - .output = output})); + tll_push_back(backend->monitors, ((struct monitor){.backend = backend, .wl_name = name, .output = output})); struct monitor *mon = &tll_back(backend->monitors); wl_output_add_listener(output, &output_listener, mon); @@ -689,8 +644,7 @@ handle_global(void *data, struct wl_registry *registry, assert(backend->xdg_output_manager != NULL); if (backend->xdg_output_manager != NULL) { - mon->xdg = zxdg_output_manager_v1_get_xdg_output( - backend->xdg_output_manager, mon->output); + mon->xdg = zxdg_output_manager_v1_get_xdg_output(backend->xdg_output_manager, mon->output); zxdg_output_v1_add_listener(mon->xdg, &xdg_output_listener, mon); } @@ -701,8 +655,7 @@ handle_global(void *data, struct wl_registry *registry, if (!verify_iface_version(interface, version, required)) return; - backend->layer_shell = wl_registry_bind( - registry, name, &zwlr_layer_shell_v1_interface, required); + backend->layer_shell = wl_registry_bind(registry, name, &zwlr_layer_shell_v1_interface, required); } else if (strcmp(interface, wl_seat_interface.name) == 0) { @@ -710,12 +663,10 @@ handle_global(void *data, struct wl_registry *registry, if (!verify_iface_version(interface, version, required)) return; - struct wl_seat *seat = wl_registry_bind( - registry, name, &wl_seat_interface, required); + struct wl_seat *seat = wl_registry_bind(registry, name, &wl_seat_interface, required); assert(seat != NULL); - tll_push_back( - backend->seats, ((struct seat){.backend = backend, .seat = seat, .id = name})); + tll_push_back(backend->seats, ((struct seat){.backend = backend, .seat = seat, .id = name})); wl_seat_add_listener(seat, &seat_listener, &tll_back(backend->seats)); } @@ -725,8 +676,7 @@ handle_global(void *data, struct wl_registry *registry, if (!verify_iface_version(interface, version, required)) return; - backend->xdg_output_manager = wl_registry_bind( - registry, name, &zxdg_output_manager_v1_interface, required); + backend->xdg_output_manager = wl_registry_bind(registry, name, &zxdg_output_manager_v1_interface, required); } } @@ -735,7 +685,8 @@ handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) { struct wayland_backend *backend = data; - tll_foreach(backend->seats, it) { + tll_foreach(backend->seats, it) + { if (it->item.id == name) { if (backend->active_seat == &it->item) backend->active_seat = NULL; @@ -745,7 +696,8 @@ handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) } } - tll_foreach(backend->monitors, it) { + tll_foreach(backend->monitors, it) + { struct monitor *mon = &it->item; if (mon->wl_name == name) { LOG_INFO("%s disconnected/disabled", mon->name); @@ -770,8 +722,7 @@ static const struct wl_registry_listener registry_listener = { }; static void -layer_surface_configure(void *data, struct zwlr_layer_surface_v1 *surface, - uint32_t serial, uint32_t w, uint32_t h) +layer_surface_configure(void *data, struct zwlr_layer_surface_v1 *surface, uint32_t serial, uint32_t w, uint32_t h) { struct wayland_backend *backend = data; backend->width = w * backend->scale; @@ -814,7 +765,6 @@ create_surface(struct wayland_backend *backend) wl_surface_add_listener(backend->surface, &surface_listener, backend); - enum zwlr_layer_shell_v1_layer layer; switch (bar->layer) { @@ -836,28 +786,22 @@ create_surface(struct wayland_backend *backend) } backend->layer_surface = zwlr_layer_shell_v1_get_layer_surface( - backend->layer_shell, backend->surface, - backend->monitor != NULL ? backend->monitor->output : NULL, - layer, "panel"); + backend->layer_shell, backend->surface, backend->monitor != NULL ? backend->monitor->output : NULL, layer, + "panel"); if (backend->layer_surface == NULL) { LOG_ERR("failed to create layer shell surface"); return false; } - zwlr_layer_surface_v1_add_listener( - backend->layer_surface, &layer_surface_listener, backend); + zwlr_layer_surface_v1_add_listener(backend->layer_surface, &layer_surface_listener, backend); /* Aligned to top, maximum width */ - enum zwlr_layer_surface_v1_anchor top_or_bottom = bar->location == BAR_TOP - ? ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP - : ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; + enum zwlr_layer_surface_v1_anchor top_or_bottom + = bar->location == BAR_TOP ? ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP : ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; - zwlr_layer_surface_v1_set_anchor( - backend->layer_surface, - ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | - ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | - top_or_bottom); + zwlr_layer_surface_v1_set_anchor(backend->layer_surface, ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT + | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | top_or_bottom); return true; } @@ -890,7 +834,7 @@ destroy_surface(struct wayland_backend *backend) static void buffer_release(void *data, struct wl_buffer *wl_buffer) { - //printf("buffer release\n"); + // printf("buffer release\n"); struct buffer *buffer = data; assert(buffer->busy); buffer->busy = false; @@ -903,7 +847,8 @@ static const struct wl_buffer_listener buffer_listener = { static struct buffer * get_buffer(struct wayland_backend *backend) { - tll_foreach(backend->buffers, it) { + tll_foreach(backend->buffers, it) + { if (!it->item.busy && it->item.width == backend->width && it->item.height == backend->height) { it->item.busy = true; return &it->item; @@ -936,13 +881,10 @@ get_buffer(struct wayland_backend *backend) * *with* it, and if that fails, try again *without* it. */ errno = 0; - pool_fd = memfd_create( - "yambar-wayland-shm-buffer-pool", - MFD_CLOEXEC | MFD_ALLOW_SEALING | MFD_NOEXEC_SEAL); + pool_fd = memfd_create("yambar-wayland-shm-buffer-pool", MFD_CLOEXEC | MFD_ALLOW_SEALING | MFD_NOEXEC_SEAL); if (pool_fd < 0 && errno == EINVAL) { - pool_fd = memfd_create( - "yambar-wayland-shm-buffer-pool", MFD_CLOEXEC | MFD_ALLOW_SEALING); + pool_fd = memfd_create("yambar-wayland-shm-buffer-pool", MFD_CLOEXEC | MFD_ALLOW_SEALING); } #elif defined(__FreeBSD__) // memfd_create on FreeBSD 13 is SHM_ANON without sealing support @@ -958,8 +900,7 @@ get_buffer(struct wayland_backend *backend) } /* Total size */ - const uint32_t stride = stride_for_format_and_width( - PIXMAN_a8r8g8b8, backend->width); + const uint32_t stride = stride_for_format_and_width(PIXMAN_a8r8g8b8, backend->width); size = stride * backend->height; if (ftruncate(pool_fd, size) == -1) { @@ -976,9 +917,7 @@ get_buffer(struct wayland_backend *backend) #if defined(MEMFD_CREATE) /* Seal file - we no longer allow any kind of resizing */ /* TODO: wayland mmaps(PROT_WRITE), for some unknown reason, hence we cannot use F_SEAL_FUTURE_WRITE */ - if (fcntl(pool_fd, F_ADD_SEALS, - F_SEAL_GROW | F_SEAL_SHRINK | /*F_SEAL_FUTURE_WRITE |*/ F_SEAL_SEAL) < 0) - { + if (fcntl(pool_fd, F_ADD_SEALS, F_SEAL_GROW | F_SEAL_SHRINK | /*F_SEAL_FUTURE_WRITE |*/ F_SEAL_SEAL) < 0) { LOG_ERRNO("failed to seal SHM backing memory file"); /* This is not a fatal error */ } @@ -990,37 +929,35 @@ get_buffer(struct wayland_backend *backend) goto err; } - buf = wl_shm_pool_create_buffer( - pool, 0, backend->width, backend->height, stride, WL_SHM_FORMAT_ARGB8888); + buf = wl_shm_pool_create_buffer(pool, 0, backend->width, backend->height, stride, WL_SHM_FORMAT_ARGB8888); if (buf == NULL) { LOG_ERR("failed to create SHM buffer"); goto err; } /* We use the entire pool for our single buffer */ - wl_shm_pool_destroy(pool); pool = NULL; - close(pool_fd); pool_fd = -1; + wl_shm_pool_destroy(pool); + pool = NULL; + close(pool_fd); + pool_fd = -1; - pix = pixman_image_create_bits_no_clear( - PIXMAN_a8r8g8b8, backend->width, backend->height, (uint32_t *)mmapped, stride); + pix = pixman_image_create_bits_no_clear(PIXMAN_a8r8g8b8, backend->width, backend->height, (uint32_t *)mmapped, + stride); if (pix == NULL) { LOG_ERR("failed to create pixman image"); goto err; } /* Push to list of available buffers, but marked as 'busy' */ - tll_push_back( - backend->buffers, - ((struct buffer){ - .busy = true, - .width = backend->width, - .height = backend->height, - .size = size, - .mmapped = mmapped, - .wl_buf = buf, - .pix = pix, - }) - ); + tll_push_back(backend->buffers, ((struct buffer){ + .busy = true, + .width = backend->width, + .height = backend->height, + .size = size, + .mmapped = mmapped, + .wl_buf = buf, + .pix = pix, + })); struct buffer *ret = &tll_back(backend->buffers); wl_buffer_add_listener(ret->wl_buf, &buffer_listener, ret); @@ -1050,7 +987,8 @@ guess_scale(const struct wayland_backend *backend) bool all_have_same_scale = true; int last_scale = -1; - tll_foreach(backend->monitors, it) { + tll_foreach(backend->monitors, it) + { if (last_scale == -1) last_scale = it->item.scale; else if (last_scale != it->item.scale) { @@ -1086,29 +1024,21 @@ update_size(struct wayland_backend *backend) bar->height = height - bar->border.top_width - bar->border.bottom_width; bar->height_with_border = height; - zwlr_layer_surface_v1_set_size( - backend->layer_surface, 0, bar->height_with_border / scale); + zwlr_layer_surface_v1_set_size(backend->layer_surface, 0, bar->height_with_border / scale); zwlr_layer_surface_v1_set_exclusive_zone( backend->layer_surface, - (bar->height_with_border + (bar->location == BAR_TOP - ? bar->border.bottom_margin - : bar->border.top_margin)) - / scale); + (bar->height_with_border + (bar->location == BAR_TOP ? bar->border.bottom_margin : bar->border.top_margin)) + / scale); - zwlr_layer_surface_v1_set_margin( - backend->layer_surface, - bar->border.top_margin / scale, - bar->border.right_margin / scale, - bar->border.bottom_margin / scale, - bar->border.left_margin / scale - ); + zwlr_layer_surface_v1_set_margin(backend->layer_surface, bar->border.top_margin / scale, + bar->border.right_margin / scale, bar->border.bottom_margin / scale, + bar->border.left_margin / scale); /* Trigger a 'configure' event, after which we'll have the width */ wl_surface_commit(backend->surface); wl_display_roundtrip(backend->display); - if (backend->width == -1 || - backend->height != bar->height_with_border) { + if (backend->width == -1 || backend->height != bar->height_with_border) { LOG_ERR("failed to get panel width"); return false; } @@ -1178,8 +1108,7 @@ setup(struct bar *_bar) return false; } - assert(backend->monitor == NULL || - backend->width / backend->monitor->scale <= backend->monitor->width_px); + assert(backend->monitor == NULL || backend->width / backend->monitor->scale <= backend->monitor->width_px); if (pipe2(backend->pipe_fds, O_CLOEXEC | O_NONBLOCK) == -1) { LOG_ERRNO("failed to create pipe"); @@ -1201,7 +1130,8 @@ cleanup(struct bar *_bar) if (backend->pipe_fds[1] >= 0) close(backend->pipe_fds[1]); - tll_foreach(backend->monitors, it) { + tll_foreach(backend->monitors, it) + { struct monitor *mon = &it->item; free(mon->name); @@ -1216,13 +1146,13 @@ cleanup(struct bar *_bar) if (backend->xdg_output_manager != NULL) zxdg_output_manager_v1_destroy(backend->xdg_output_manager); - tll_foreach(backend->seats, it) - seat_destroy(&it->item); + tll_foreach(backend->seats, it) seat_destroy(&it->item); tll_free(backend->seats); destroy_surface(backend); - tll_foreach(backend->buffers, it) { + tll_foreach(backend->buffers, it) + { if (it->item.wl_buf != NULL) wl_buffer_destroy(it->item.wl_buf); if (it->item.pix != NULL) @@ -1247,14 +1177,11 @@ cleanup(struct bar *_bar) /* Destroyed when freeing buffer list */ bar->pix = NULL; - } static void -loop(struct bar *_bar, - void (*expose)(const struct bar *bar), - void (*on_mouse)(struct bar *bar, enum mouse_event event, - enum mouse_button btn, int x, int y)) +loop(struct bar *_bar, void (*expose)(const struct bar *bar), + void (*on_mouse)(struct bar *bar, enum mouse_event event, enum mouse_button btn, int x, int y)) { struct private *bar = _bar->private; struct wayland_backend *backend = bar->backend.data; @@ -1341,25 +1268,23 @@ out: if (!send_abort_to_modules) return; - if (write(_bar->abort_fd, &(uint64_t){1}, sizeof(uint64_t)) - != sizeof(uint64_t)) - { + if (write(_bar->abort_fd, &(uint64_t){1}, sizeof(uint64_t)) != sizeof(uint64_t)) { LOG_ERRNO("failed to signal abort to modules"); } - //wl_display_cancel_read(backend->display); + // wl_display_cancel_read(backend->display); } static void -surface_enter(void *data, struct wl_surface *wl_surface, - struct wl_output *wl_output) +surface_enter(void *data, struct wl_surface *wl_surface, struct wl_output *wl_output) { struct wayland_backend *backend = data; free(backend->last_mapped_monitor); backend->last_mapped_monitor = NULL; - tll_foreach(backend->monitors, it) { + tll_foreach(backend->monitors, it) + { struct monitor *mon = &it->item; if (mon->output != wl_output) @@ -1379,8 +1304,7 @@ surface_enter(void *data, struct wl_surface *wl_surface, } static void -surface_leave(void *data, struct wl_surface *wl_surface, - struct wl_output *wl_output) +surface_leave(void *data, struct wl_surface *wl_surface, struct wl_output *wl_output) { struct wayland_backend *backend = data; const struct monitor *mon = backend->monitor; @@ -1399,8 +1323,7 @@ static const struct wl_surface_listener surface_listener = { .leave = &surface_leave, }; -static void frame_callback( - void *data, struct wl_callback *wl_callback, uint32_t callback_data); +static void frame_callback(void *data, struct wl_callback *wl_callback, uint32_t callback_data); static const struct wl_callback_listener frame_listener = { .done = &frame_callback, @@ -1409,7 +1332,7 @@ static const struct wl_callback_listener frame_listener = { static void frame_callback(void *data, struct wl_callback *wl_callback, uint32_t callback_data) { - //printf("frame callback\n"); + // printf("frame callback\n"); struct private *bar = data; struct wayland_backend *backend = bar->backend.data; @@ -1436,7 +1359,7 @@ frame_callback(void *data, struct wl_callback *wl_callback, uint32_t callback_da backend->pending_buffer = NULL; backend->render_scheduled = true; } else - ;//printf("nothing more to do\n"); + ; // printf("nothing more to do\n"); } static void @@ -1445,7 +1368,7 @@ commit(const struct bar *_bar) struct private *bar = _bar->private; struct wayland_backend *backend = bar->backend.data; - //printf("commit: %dxl%d\n", backend->width, backend->height); + // printf("commit: %dxl%d\n", backend->width, backend->height); if (backend->next_buffer == NULL) return; @@ -1454,7 +1377,7 @@ commit(const struct bar *_bar) assert(backend->next_buffer->busy); if (backend->render_scheduled) { - //printf("already scheduled\n"); + // printf("already scheduled\n"); if (backend->pending_buffer != NULL) backend->pending_buffer->busy = false; @@ -1463,7 +1386,7 @@ commit(const struct bar *_bar) backend->next_buffer = NULL; } else { - //printf("scheduling new frame callback\n"); + // printf("scheduling new frame callback\n"); struct buffer *buffer = backend->next_buffer; assert(buffer->busy); @@ -1491,9 +1414,7 @@ refresh(const struct bar *_bar) const struct private *bar = _bar->private; const struct wayland_backend *backend = bar->backend.data; - if (write(backend->pipe_fds[1], &(uint8_t){1}, sizeof(uint8_t)) - != sizeof(uint8_t)) - { + if (write(backend->pipe_fds[1], &(uint8_t){1}, sizeof(uint8_t)) != sizeof(uint8_t)) { LOG_ERRNO("failed to signal 'refresh' to main thread"); } } @@ -1513,8 +1434,7 @@ set_cursor(struct bar *_bar, const char *cursor) seat->pointer.xcursor = cursor; - seat->pointer.cursor = wl_cursor_theme_get_cursor( - seat->pointer.theme, cursor); + seat->pointer.cursor = wl_cursor_theme_get_cursor(seat->pointer.theme, cursor); if (seat->pointer.cursor == NULL) { LOG_ERR("%s: failed to load cursor '%s'", seat->name, cursor); diff --git a/bar/xcb.c b/bar/xcb.c index d8c5f9c..2552fe6 100644 --- a/bar/xcb.c +++ b/bar/xcb.c @@ -1,16 +1,16 @@ #include "xcb.h" -#include #include +#include -#include #include #include +#include #include -#include #include #include +#include #include #include #include @@ -39,7 +39,6 @@ struct xcb_backend { void *client_pixmap; size_t client_pixmap_size; pixman_image_t *pix; - }; void * @@ -55,11 +54,8 @@ setup(struct bar *_bar) struct private *bar = _bar->private; struct xcb_backend *backend = bar->backend.data; - if (bar->border.left_margin != 0 || - bar->border.right_margin != 0 || - bar->border.top_margin != 0 || - bar->border.bottom_margin) - { + if (bar->border.left_margin != 0 || bar->border.right_margin != 0 || bar->border.top_margin != 0 + || bar->border.bottom_margin) { LOG_WARN("non-zero border margins ignored in X11 backend"); } @@ -76,10 +72,8 @@ setup(struct bar *_bar) xcb_screen_t *screen = xcb_aux_get_screen(backend->conn, default_screen); - xcb_randr_get_monitors_reply_t *monitors = xcb_randr_get_monitors_reply( - backend->conn, - xcb_randr_get_monitors(backend->conn, screen->root, 0), - &e); + xcb_randr_get_monitors_reply_t *monitors + = xcb_randr_get_monitors_reply(backend->conn, xcb_randr_get_monitors(backend->conn, screen->root, 0), &e); if (e != NULL) { LOG_ERR("failed to get monitor list: %s", xcb_error(e)); @@ -90,17 +84,13 @@ setup(struct bar *_bar) /* Find monitor coordinates and width/height */ bool found_monitor = false; - for (xcb_randr_monitor_info_iterator_t it = - xcb_randr_get_monitors_monitors_iterator(monitors); - it.rem > 0; - xcb_randr_monitor_info_next(&it)) - { + for (xcb_randr_monitor_info_iterator_t it = xcb_randr_get_monitors_monitors_iterator(monitors); it.rem > 0; + xcb_randr_monitor_info_next(&it)) { const xcb_randr_monitor_info_t *mon = it.data; char *name = get_atom_name(backend->conn, mon->name); - LOG_INFO("monitor: %s: %ux%u+%u+%u (%ux%umm)", name, - mon->width, mon->height, mon->x, mon->y, - mon->width_in_millimeters, mon->height_in_millimeters); + LOG_INFO("monitor: %s: %ux%u+%u+%u (%ux%umm)", name, mon->width, mon->height, mon->x, mon->y, + mon->width_in_millimeters, mon->height_in_millimeters); /* User wants a specific monitor, and this is not the one */ if (bar->monitor != NULL && strcmp(bar->monitor, name) != 0) { @@ -111,14 +101,11 @@ setup(struct bar *_bar) backend->x = mon->x; backend->y = mon->y; bar->width = mon->width; - backend->y += bar->location == BAR_TOP ? 0 - : screen->height_in_pixels - bar->height_with_border; + backend->y += bar->location == BAR_TOP ? 0 : screen->height_in_pixels - bar->height_with_border; found_monitor = true; - if ((bar->monitor != NULL && strcmp(bar->monitor, name) == 0) || - (bar->monitor == NULL && mon->primary)) - { + if ((bar->monitor != NULL && strcmp(bar->monitor, name) == 0) || (bar->monitor == NULL && mon->primary)) { /* Exact match */ free(name); break; @@ -155,61 +142,34 @@ setup(struct bar *_bar) LOG_DBG("using a %hhu-bit visual", depth); backend->colormap = xcb_generate_id(backend->conn); - xcb_create_colormap( - backend->conn, 0, backend->colormap, screen->root, vis->visual_id); + xcb_create_colormap(backend->conn, 0, backend->colormap, screen->root, vis->visual_id); backend->win = xcb_generate_id(backend->conn); xcb_create_window( - backend->conn, - depth, backend->win, screen->root, - backend->x, backend->y, bar->width, bar->height_with_border, - 0, - XCB_WINDOW_CLASS_INPUT_OUTPUT, vis->visual_id, - (XCB_CW_BACK_PIXEL | - XCB_CW_BORDER_PIXEL | - XCB_CW_EVENT_MASK | - XCB_CW_COLORMAP), - (const uint32_t []){ - screen->black_pixel, - screen->white_pixel, - (XCB_EVENT_MASK_EXPOSURE | - XCB_EVENT_MASK_BUTTON_RELEASE | - XCB_EVENT_MASK_BUTTON_PRESS | - XCB_EVENT_MASK_POINTER_MOTION | - XCB_EVENT_MASK_STRUCTURE_NOTIFY), - backend->colormap} - ); + backend->conn, depth, backend->win, screen->root, backend->x, backend->y, bar->width, bar->height_with_border, + 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, vis->visual_id, + (XCB_CW_BACK_PIXEL | XCB_CW_BORDER_PIXEL | XCB_CW_EVENT_MASK | XCB_CW_COLORMAP), + (const uint32_t[]){screen->black_pixel, screen->white_pixel, + (XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_BUTTON_PRESS + | XCB_EVENT_MASK_POINTER_MOTION | XCB_EVENT_MASK_STRUCTURE_NOTIFY), + backend->colormap}); const char *title = "yambar"; - xcb_change_property( - backend->conn, - XCB_PROP_MODE_REPLACE, backend->win, - XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8, - strlen(title), title); + xcb_change_property(backend->conn, XCB_PROP_MODE_REPLACE, backend->win, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8, + strlen(title), title); - xcb_change_property( - backend->conn, - XCB_PROP_MODE_REPLACE, backend->win, - _NET_WM_PID, XCB_ATOM_CARDINAL, 32, 1, (const uint32_t []){getpid()}); - xcb_change_property( - backend->conn, - XCB_PROP_MODE_REPLACE, backend->win, - _NET_WM_WINDOW_TYPE, XCB_ATOM_ATOM, 32, - 1, (const uint32_t []){_NET_WM_WINDOW_TYPE_DOCK}); - xcb_change_property( - backend->conn, - XCB_PROP_MODE_REPLACE, backend->win, - _NET_WM_STATE, XCB_ATOM_ATOM, 32, - 2, (const uint32_t []){_NET_WM_STATE_ABOVE, _NET_WM_STATE_STICKY}); - xcb_change_property( - backend->conn, - XCB_PROP_MODE_REPLACE, backend->win, - _NET_WM_DESKTOP, XCB_ATOM_CARDINAL, 32, 1, (const uint32_t []){0xffffffff}); + xcb_change_property(backend->conn, XCB_PROP_MODE_REPLACE, backend->win, _NET_WM_PID, XCB_ATOM_CARDINAL, 32, 1, + (const uint32_t[]){getpid()}); + xcb_change_property(backend->conn, XCB_PROP_MODE_REPLACE, backend->win, _NET_WM_WINDOW_TYPE, XCB_ATOM_ATOM, 32, 1, + (const uint32_t[]){_NET_WM_WINDOW_TYPE_DOCK}); + xcb_change_property(backend->conn, XCB_PROP_MODE_REPLACE, backend->win, _NET_WM_STATE, XCB_ATOM_ATOM, 32, 2, + (const uint32_t[]){_NET_WM_STATE_ABOVE, _NET_WM_STATE_STICKY}); + xcb_change_property(backend->conn, XCB_PROP_MODE_REPLACE, backend->win, _NET_WM_DESKTOP, XCB_ATOM_CARDINAL, 32, 1, + (const uint32_t[]){0xffffffff}); /* Always on top */ - xcb_configure_window( - backend->conn, backend->win, XCB_CONFIG_WINDOW_STACK_MODE, - (const uint32_t []){XCB_STACK_MODE_ABOVE}); + xcb_configure_window(backend->conn, backend->win, XCB_CONFIG_WINDOW_STACK_MODE, + (const uint32_t[]){XCB_STACK_MODE_ABOVE}); uint32_t top_strut, bottom_strut; uint32_t top_pair[2], bottom_pair[2]; @@ -232,42 +192,38 @@ setup(struct bar *_bar) uint32_t strut[] = { /* left/right/top/bottom */ - 0, 0, + 0, + 0, top_strut, bottom_strut, /* start/end pairs for left/right/top/bottom */ - 0, 0, - 0, 0, - top_pair[0], top_pair[1], - bottom_pair[0], bottom_pair[1], + 0, + 0, + 0, + 0, + top_pair[0], + top_pair[1], + bottom_pair[0], + bottom_pair[1], }; - xcb_change_property( - backend->conn, - XCB_PROP_MODE_REPLACE, backend->win, - _NET_WM_STRUT, XCB_ATOM_CARDINAL, 32, - 4, strut); + xcb_change_property(backend->conn, XCB_PROP_MODE_REPLACE, backend->win, _NET_WM_STRUT, XCB_ATOM_CARDINAL, 32, 4, + strut); - xcb_change_property( - backend->conn, - XCB_PROP_MODE_REPLACE, backend->win, - _NET_WM_STRUT_PARTIAL, XCB_ATOM_CARDINAL, 32, - 12, strut); + xcb_change_property(backend->conn, XCB_PROP_MODE_REPLACE, backend->win, _NET_WM_STRUT_PARTIAL, XCB_ATOM_CARDINAL, + 32, 12, strut); backend->gc = xcb_generate_id(backend->conn); - xcb_create_gc(backend->conn, backend->gc, backend->win, - XCB_GC_FOREGROUND | XCB_GC_GRAPHICS_EXPOSURES, - (const uint32_t []){screen->white_pixel, 0}); + xcb_create_gc(backend->conn, backend->gc, backend->win, XCB_GC_FOREGROUND | XCB_GC_GRAPHICS_EXPOSURES, + (const uint32_t[]){screen->white_pixel, 0}); - const uint32_t stride = stride_for_format_and_width( - PIXMAN_a8r8g8b8, bar->width); + const uint32_t stride = stride_for_format_and_width(PIXMAN_a8r8g8b8, bar->width); backend->client_pixmap_size = stride * bar->height_with_border; backend->client_pixmap = malloc(backend->client_pixmap_size); - backend->pix = pixman_image_create_bits_no_clear( - PIXMAN_a8r8g8b8, bar->width, bar->height_with_border, - (uint32_t *)backend->client_pixmap, stride); + backend->pix = pixman_image_create_bits_no_clear(PIXMAN_a8r8g8b8, bar->width, bar->height_with_border, + (uint32_t *)backend->client_pixmap, stride); bar->pix = backend->pix; xcb_map_window(backend->conn, backend->win); @@ -310,10 +266,8 @@ cleanup(struct bar *_bar) } static void -loop(struct bar *_bar, - void (*expose)(const struct bar *bar), - void (*on_mouse)(struct bar *bar, enum mouse_event event, - enum mouse_button btn, int x, int y)) +loop(struct bar *_bar, void (*expose)(const struct bar *bar), + void (*on_mouse)(struct bar *bar, enum mouse_event event, enum mouse_button btn, int x, int y)) { struct private *bar = _bar->private; struct xcb_backend *backend = bar->backend.data; @@ -323,10 +277,7 @@ loop(struct bar *_bar, const int fd = xcb_get_file_descriptor(backend->conn); while (true) { - struct pollfd fds[] = { - {.fd = _bar->abort_fd, .events = POLLIN}, - {.fd = fd, .events = POLLIN} - }; + struct pollfd fds[] = {{.fd = _bar->abort_fd, .events = POLLIN}, {.fd = fd, .events = POLLIN}}; poll(fds, sizeof(fds) / sizeof(fds[0]), -1); @@ -335,18 +286,14 @@ loop(struct bar *_bar, if (fds[1].revents & POLLHUP) { LOG_WARN("disconnected from XCB"); - if (write(_bar->abort_fd, &(uint64_t){1}, sizeof(uint64_t)) - != sizeof(uint64_t)) - { + if (write(_bar->abort_fd, &(uint64_t){1}, sizeof(uint64_t)) != sizeof(uint64_t)) { LOG_ERRNO("failed to signal abort to modules"); } break; } - for (xcb_generic_event_t *e = xcb_wait_for_event(backend->conn); - e != NULL; - e = xcb_poll_for_event(backend->conn)) - { + for (xcb_generic_event_t *e = xcb_wait_for_event(backend->conn); e != NULL; + e = xcb_poll_for_event(backend->conn)) { switch (XCB_EVENT_RESPONSE_TYPE(e)) { case 0: LOG_ERR("XCB: %s", xcb_error((const xcb_generic_error_t *)e)); @@ -369,9 +316,12 @@ loop(struct bar *_bar, const xcb_button_release_event_t *evt = (void *)e; switch (evt->detail) { - case 1: case 2: case 3: case 4: case 5: - on_mouse(_bar, ON_MOUSE_CLICK, - evt->detail, evt->event_x, evt->event_y); + case 1: + case 2: + case 3: + case 4: + case 5: + on_mouse(_bar, ON_MOUSE_CLICK, evt->detail, evt->event_x, evt->event_y); break; } break; @@ -405,10 +355,9 @@ commit(const struct bar *_bar) const struct private *bar = _bar->private; const struct xcb_backend *backend = bar->backend.data; - xcb_put_image( - backend->conn, XCB_IMAGE_FORMAT_Z_PIXMAP, backend->win, backend->gc, - bar->width, bar->height_with_border, 0, 0, 0, - backend->depth, backend->client_pixmap_size, backend->client_pixmap); + xcb_put_image(backend->conn, XCB_IMAGE_FORMAT_Z_PIXMAP, backend->win, backend->gc, bar->width, + bar->height_with_border, 0, 0, 0, backend->depth, backend->client_pixmap_size, + backend->client_pixmap); xcb_flush(backend->conn); } @@ -424,19 +373,15 @@ refresh(const struct bar *_bar) * the size of the event structure */ xcb_expose_event_t *evt = calloc(32, 1); - *evt = (xcb_expose_event_t){ - .response_type = XCB_EXPOSE, - .window = backend->win, - .x = 0, - .y = 0, - .width = bar->width, - .height = bar->height, - .count = 1 - }; + *evt = (xcb_expose_event_t){.response_type = XCB_EXPOSE, + .window = backend->win, + .x = 0, + .y = 0, + .width = bar->width, + .height = bar->height, + .count = 1}; - xcb_send_event( - backend->conn, false, backend->win, XCB_EVENT_MASK_EXPOSURE, - (char *)evt); + xcb_send_event(backend->conn, false, backend->win, XCB_EVENT_MASK_EXPOSURE, (char *)evt); xcb_flush(backend->conn); free(evt); @@ -458,8 +403,7 @@ set_cursor(struct bar *_bar, const char *cursor) xcb_free_cursor(backend->conn, backend->cursor); backend->cursor = xcb_cursor_load_cursor(backend->cursor_ctx, cursor); - xcb_change_window_attributes( - backend->conn, backend->win, XCB_CW_CURSOR, &backend->cursor); + xcb_change_window_attributes(backend->conn, backend->win, XCB_CW_CURSOR, &backend->cursor); } static const char * diff --git a/char32.c b/char32.c index 0ca029a..eb3abb4 100644 --- a/char32.c +++ b/char32.c @@ -1,15 +1,15 @@ #include "char32.h" +#include #include #include -#include #include #if defined __has_include - #if __has_include () - #include - #endif +#if __has_include() +#include +#endif #endif #define LOG_MODULE "char32" @@ -24,14 +24,13 @@ * - both use the same encoding (though we require that encoding to be UTF-32) */ -_Static_assert( - sizeof(wchar_t) == sizeof(char32_t), "wchar_t vs. char32_t size mismatch"); +_Static_assert(sizeof(wchar_t) == sizeof(char32_t), "wchar_t vs. char32_t size mismatch"); #if !defined(__STDC_UTF_32__) || !__STDC_UTF_32__ - #error "char32_t does not use UTF-32" +#error "char32_t does not use UTF-32" #endif #if (!defined(__STDC_ISO_10646__) || !__STDC_ISO_10646__) && !defined(__FreeBSD__) - #error "wchar_t does not use UTF-32" +#error "wchar_t does not use UTF-32" #endif size_t diff --git a/char32.h b/char32.h index b01a7d3..1e7a9de 100644 --- a/char32.h +++ b/char32.h @@ -1,7 +1,7 @@ #pragma once -#include #include +#include size_t c32len(const char32_t *s); char32_t *ambstoc32(const char *src); diff --git a/config-verify.c b/config-verify.c index d4211f1..ed7d2f5 100644 --- a/config-verify.c +++ b/config-verify.c @@ -1,7 +1,7 @@ #include "config.h" -#include #include +#include #include @@ -16,11 +16,9 @@ conf_err_prefix(const keychain_t *chain, const struct yml_node *node) static char msg[4096]; int idx = 0; - idx += snprintf(&msg[idx], sizeof(msg) - idx, "%zu:%zu: ", - yml_source_line(node), yml_source_column(node)); + idx += snprintf(&msg[idx], sizeof(msg) - idx, "%zu:%zu: ", yml_source_line(node), yml_source_column(node)); - tll_foreach(*chain, key) - idx += snprintf(&msg[idx], sizeof(msg) - idx, "%s.", key->item); + tll_foreach(*chain, key) idx += snprintf(&msg[idx], sizeof(msg) - idx, "%s.", key->item); /* Remove trailing "." */ msg[idx - 1] = '\0'; @@ -45,8 +43,7 @@ conf_verify_int(keychain_t *chain, const struct yml_node *node) if (yml_value_is_int(node)) return true; - LOG_ERR("%s: value is not an integer: '%s'", - conf_err_prefix(chain, node), yml_value_as_string(node)); + LOG_ERR("%s: value is not an integer: '%s'", conf_err_prefix(chain, node), yml_value_as_string(node)); return false; } @@ -56,8 +53,7 @@ conf_verify_unsigned(keychain_t *chain, const struct yml_node *node) if (yml_value_is_int(node) && yml_value_as_int(node) >= 0) return true; - LOG_ERR("%s: value is not a positive integer: '%s'", - conf_err_prefix(chain, node), yml_value_as_string(node)); + LOG_ERR("%s: value is not a positive integer: '%s'", conf_err_prefix(chain, node), yml_value_as_string(node)); return false; } @@ -67,8 +63,7 @@ conf_verify_bool(keychain_t *chain, const struct yml_node *node) if (yml_value_is_bool(node)) return true; - LOG_ERR("%s: value is not a boolean: '%s'", - conf_err_prefix(chain, node), yml_value_as_string(node)); + LOG_ERR("%s: value is not a boolean: '%s'", conf_err_prefix(chain, node), yml_value_as_string(node)); return false; } @@ -81,10 +76,7 @@ conf_verify_list(keychain_t *chain, const struct yml_node *node, return false; } - for (struct yml_list_iter iter = yml_list_iter(node); - iter.node != NULL; - yml_list_next(&iter)) - { + for (struct yml_list_iter iter = yml_list_iter(node); iter.node != NULL; yml_list_next(&iter)) { if (!verify(chain, iter.node)) return false; } @@ -93,8 +85,7 @@ conf_verify_list(keychain_t *chain, const struct yml_node *node, } bool -conf_verify_enum(keychain_t *chain, const struct yml_node *node, - const char *values[], size_t count) +conf_verify_enum(keychain_t *chain, const struct yml_node *node, const char *values[], size_t count) { const char *s = yml_value_as_string(node); if (s == NULL) { @@ -115,8 +106,7 @@ conf_verify_enum(keychain_t *chain, const struct yml_node *node, } bool -conf_verify_dict(keychain_t *chain, const struct yml_node *node, - const struct attr_info info[]) +conf_verify_dict(keychain_t *chain, const struct yml_node *node, const struct attr_info info[]) { if (!yml_is_dict(node)) { LOG_ERR("%s: must be a dictionary", conf_err_prefix(chain, node)); @@ -131,10 +121,7 @@ conf_verify_dict(keychain_t *chain, const struct yml_node *node, bool exists[count]; memset(exists, 0, sizeof(exists)); - for (struct yml_dict_iter it = yml_dict_iter(node); - it.key != NULL; - yml_dict_next(&it)) - { + for (struct yml_dict_iter it = yml_dict_iter(node); it.key != NULL; yml_dict_next(&it)) { const char *key = yml_value_as_string(it.key); if (key == NULL) { LOG_ERR("%s: key must be a string", conf_err_prefix(chain, it.key)); @@ -190,8 +177,7 @@ verify_on_click_path(keychain_t *chain, const struct yml_node *node) const bool is_tilde = path[0] == '~' && path[1] == '/'; if (!is_absolute && !is_tilde) { - LOG_ERR("%s: path must be either absolute, or begin with '~/", - conf_err_prefix(chain, node)); + LOG_ERR("%s: path must be either absolute, or begin with '~/", conf_err_prefix(chain, node)); return false; } @@ -208,14 +194,10 @@ conf_verify_on_click(keychain_t *chain, const struct yml_node *node) return verify_on_click_path(chain, node); static const struct attr_info info[] = { - {"left", false, &verify_on_click_path}, - {"middle", false, &verify_on_click_path}, - {"right", false, &verify_on_click_path}, - {"wheel-up", false, &verify_on_click_path}, - {"wheel-down", false, &verify_on_click_path}, - {"previous", false, &verify_on_click_path}, - {"next", false, &verify_on_click_path}, - {NULL, false, NULL}, + {"left", false, &verify_on_click_path}, {"middle", false, &verify_on_click_path}, + {"right", false, &verify_on_click_path}, {"wheel-up", false, &verify_on_click_path}, + {"wheel-down", false, &verify_on_click_path}, {"previous", false, &verify_on_click_path}, + {"next", false, &verify_on_click_path}, {NULL, false, NULL}, }; return conf_verify_dict(chain, node, info); @@ -234,21 +216,18 @@ conf_verify_color(keychain_t *chain, const struct yml_node *node) int v = sscanf(s, "%02x%02x%02x%02x", &r, &g, &b, &a); if (strlen(s) != 8 || v != 4) { - LOG_ERR("%s: value must be a color ('rrggbbaa', e.g ff00ffff)", - conf_err_prefix(chain, node)); + LOG_ERR("%s: value must be a color ('rrggbbaa', e.g ff00ffff)", conf_err_prefix(chain, node)); return false; } return true; } - bool conf_verify_font(keychain_t *chain, const struct yml_node *node) { if (!yml_is_scalar(node)) { - LOG_ERR("%s: font must be a fontconfig-formatted string", - conf_err_prefix(chain, node)); + LOG_ERR("%s: font must be a fontconfig-formatted string", conf_err_prefix(chain, node)); return false; } @@ -258,8 +237,7 @@ conf_verify_font(keychain_t *chain, const struct yml_node *node) bool conf_verify_font_shaping(keychain_t *chain, const struct yml_node *node) { - return conf_verify_enum( - chain, node, (const char *[]){"full", /*"graphemes",*/ "none"}, 2); + return conf_verify_enum(chain, node, (const char *[]){"full", /*"graphemes",*/ "none"}, 2); } bool @@ -269,7 +247,8 @@ conf_verify_decoration(keychain_t *chain, const struct yml_node *node) if (yml_dict_length(node) != 1) { LOG_ERR("%s: decoration must be a dictionary with a single key; " - "the name of the particle", conf_err_prefix(chain, node)); + "the name of the particle", + conf_err_prefix(chain, node)); return false; } @@ -285,8 +264,7 @@ conf_verify_decoration(keychain_t *chain, const struct yml_node *node) const struct deco_iface *iface = plugin_load_deco(deco_name); if (iface == NULL) { - LOG_ERR("%s: invalid decoration name: %s", - conf_err_prefix(chain, deco), deco_name); + LOG_ERR("%s: invalid decoration name: %s", conf_err_prefix(chain, deco), deco_name); return false; } @@ -303,10 +281,7 @@ conf_verify_particle_list_items(keychain_t *chain, const struct yml_node *node) { assert(yml_is_list(node)); - for (struct yml_list_iter it = yml_list_iter(node); - it.node != NULL; - yml_list_next(&it)) - { + for (struct yml_list_iter it = yml_list_iter(node); it.node != NULL; yml_list_next(&it)) { if (!conf_verify_particle(chain, it.node)) return false; } @@ -321,7 +296,8 @@ conf_verify_particle_dictionary(keychain_t *chain, const struct yml_node *node) if (yml_dict_length(node) != 1) { LOG_ERR("%s: particle must be a dictionary with a single key; " - "the name of the particle", conf_err_prefix(chain, node)); + "the name of the particle", + conf_err_prefix(chain, node)); return false; } @@ -331,15 +307,13 @@ conf_verify_particle_dictionary(keychain_t *chain, const struct yml_node *node) const char *particle_name = yml_value_as_string(particle); if (particle_name == NULL) { - LOG_ERR("%s: particle name must be a string", - conf_err_prefix(chain, particle)); + LOG_ERR("%s: particle name must be a string", conf_err_prefix(chain, particle)); return false; } const struct particle_iface *iface = plugin_load_particle(particle_name); if (iface == NULL) { - LOG_ERR("%s: invalid particle name: %s", - conf_err_prefix(chain, particle), particle_name); + LOG_ERR("%s: invalid particle name: %s", conf_err_prefix(chain, particle), particle_name); return false; } @@ -361,19 +335,18 @@ conf_verify_particle(keychain_t *chain, const struct yml_node *node) else if (yml_is_list(node)) return conf_verify_particle_list_items(chain, node); else { - LOG_ERR("%s: particle must be either a dictionary or a list", - conf_err_prefix(chain, node)); + LOG_ERR("%s: particle must be either a dictionary or a list", conf_err_prefix(chain, node)); return false; } } - static bool verify_module(keychain_t *chain, const struct yml_node *node) { if (!yml_is_dict(node) || yml_dict_length(node) != 1) { LOG_ERR("%s: module must be a dictionary with a single key; " - "the name of the module", conf_err_prefix(chain, node)); + "the name of the module", + conf_err_prefix(chain, node)); return false; } @@ -389,8 +362,7 @@ verify_module(keychain_t *chain, const struct yml_node *node) const struct module_iface *iface = plugin_load_module(mod_name); if (iface == NULL) { - LOG_ERR( - "%s: invalid module name: %s", conf_err_prefix(chain, node), mod_name); + LOG_ERR("%s: invalid module name: %s", conf_err_prefix(chain, node), mod_name); return false; } @@ -412,10 +384,7 @@ verify_module_list(keychain_t *chain, const struct yml_node *node) return false; } - for (struct yml_list_iter it = yml_list_iter(node); - it.node != NULL; - yml_list_next(&it)) - { + for (struct yml_list_iter it = yml_list_iter(node); it.node != NULL; yml_list_next(&it)) { if (!verify_module(chain, it.node)) return false; } @@ -427,18 +396,12 @@ static bool verify_bar_border(keychain_t *chain, const struct yml_node *node) { static const struct attr_info attrs[] = { - {"width", false, &conf_verify_unsigned}, - {"left-width", false, &conf_verify_unsigned}, - {"right-width", false, &conf_verify_unsigned}, - {"top-width", false, &conf_verify_unsigned}, - {"bottom-width", false, &conf_verify_unsigned}, - {"color", false, &conf_verify_color}, - {"margin", false, &conf_verify_unsigned}, - {"left-margin", false, &conf_verify_unsigned}, - {"right-margin", false, &conf_verify_unsigned}, - {"top-margin", false, &conf_verify_unsigned}, - {"bottom-margin", false, &conf_verify_unsigned}, - {NULL, false, NULL}, + {"width", false, &conf_verify_unsigned}, {"left-width", false, &conf_verify_unsigned}, + {"right-width", false, &conf_verify_unsigned}, {"top-width", false, &conf_verify_unsigned}, + {"bottom-width", false, &conf_verify_unsigned}, {"color", false, &conf_verify_color}, + {"margin", false, &conf_verify_unsigned}, {"left-margin", false, &conf_verify_unsigned}, + {"right-margin", false, &conf_verify_unsigned}, {"top-margin", false, &conf_verify_unsigned}, + {"bottom-margin", false, &conf_verify_unsigned}, {NULL, false, NULL}, }; return conf_verify_dict(chain, node, attrs); @@ -453,9 +416,7 @@ verify_bar_location(keychain_t *chain, const struct yml_node *node) static bool verify_bar_layer(keychain_t *chain, const struct yml_node *node) { - return conf_verify_enum( - chain, node, - (const char *[]){"overlay", "top", "bottom", "background"}, 4); + return conf_verify_enum(chain, node, (const char *[]){"overlay", "top", "bottom", "background"}, 4); } bool diff --git a/config-verify.h b/config-verify.h index 0a4ae34..8ad44ce 100644 --- a/config-verify.h +++ b/config-verify.h @@ -26,17 +26,14 @@ chain_pop(keychain_t *chain) tll_pop_back(*chain); } -const char *conf_err_prefix( - const keychain_t *chain, const struct yml_node *node); - +const char *conf_err_prefix(const keychain_t *chain, const struct yml_node *node); bool conf_verify_string(keychain_t *chain, const struct yml_node *node); bool conf_verify_int(keychain_t *chain, const struct yml_node *node); bool conf_verify_unsigned(keychain_t *chain, const struct yml_node *node); bool conf_verify_bool(keychain_t *chain, const struct yml_node *node); -bool conf_verify_enum(keychain_t *chain, const struct yml_node *node, - const char *values[], size_t count); +bool conf_verify_enum(keychain_t *chain, const struct yml_node *node, const char *values[], size_t count); bool conf_verify_list(keychain_t *chain, const struct yml_node *node, bool (*verify)(keychain_t *chain, const struct yml_node *node)); bool conf_verify_dict(keychain_t *chain, const struct yml_node *node, diff --git a/config.c b/config.c index a0eba59..0f80364 100644 --- a/config.c +++ b/config.c @@ -1,10 +1,10 @@ #include "config.h" -#include -#include -#include #include #include +#include +#include +#include #include @@ -21,9 +21,7 @@ static uint8_t hex_nibble(char hex) { - assert((hex >= '0' && hex <= '9') || - (hex >= 'a' && hex <= 'f') || - (hex >= 'A' && hex <= 'F')); + assert((hex >= '0' && hex <= '9') || (hex >= 'a' && hex <= 'f') || (hex >= 'A' && hex <= 'F')); if (hex >= '0' && hex <= '9') return hex - '0'; @@ -57,9 +55,9 @@ conf_to_color(const struct yml_node *node) alpha |= alpha << 8; return (pixman_color_t){ - .red = (uint32_t)(red << 8 | red) * alpha / 0xffff, + .red = (uint32_t)(red << 8 | red) * alpha / 0xffff, .green = (uint32_t)(green << 8 | green) * alpha / 0xffff, - .blue = (uint32_t)(blue << 8 | blue) * alpha / 0xffff, + .blue = (uint32_t)(blue << 8 | blue) * alpha / 0xffff, .alpha = alpha, }; } @@ -74,10 +72,7 @@ conf_to_font(const struct yml_node *node) const char **fonts = NULL; char *copy = strdup(font_spec); - for (const char *font = strtok(copy, ","); - font != NULL; - font = strtok(NULL, ",")) - { + for (const char *font = strtok(copy, ","); font != NULL; font = strtok(NULL, ",")) { /* Trim spaces, strictly speaking not necessary, but looks nice :) */ while (isspace(font[0])) font++; @@ -112,9 +107,7 @@ conf_to_font_shaping(const struct yml_node *node) else if (strcmp(v, "graphemes") == 0) { static bool have_warned = false; - if (!have_warned && - !(fcft_capabilities() & FCFT_CAPABILITY_GRAPHEME_SHAPING)) - { + if (!have_warned && !(fcft_capabilities() & FCFT_CAPABILITY_GRAPHEME_SHAPING)) { have_warned = true; LOG_WARN("cannot enable grapheme shaping; no support in fcft"); } @@ -124,9 +117,7 @@ conf_to_font_shaping(const struct yml_node *node) else if (strcmp(v, "full") == 0) { static bool have_warned = false; - if (!have_warned && - !(fcft_capabilities() & FCFT_CAPABILITY_TEXT_RUN_SHAPING)) - { + if (!have_warned && !(fcft_capabilities() & FCFT_CAPABILITY_TEXT_RUN_SHAPING)) { have_warned = true; LOG_WARN("cannot enable full text shaping; no support in fcft"); } @@ -154,25 +145,20 @@ conf_to_deco(const struct yml_node *node) } static struct particle * -particle_simple_list_from_config(const struct yml_node *node, - struct conf_inherit inherited) +particle_simple_list_from_config(const struct yml_node *node, struct conf_inherit inherited) { size_t count = yml_list_length(node); struct particle *parts[count]; size_t idx = 0; - for (struct yml_list_iter it = yml_list_iter(node); - it.node != NULL; - yml_list_next(&it), idx++) - { + for (struct yml_list_iter it = yml_list_iter(node); it.node != NULL; yml_list_next(&it), idx++) { parts[idx] = conf_to_particle(it.node, inherited); } /* Lazy-loaded function pointer to particle_list_new() */ - static struct particle *(*particle_list_new)( - struct particle *common, - struct particle *particles[], size_t count, - int left_spacing, int right_spacing) = NULL; + static struct particle *(*particle_list_new)(struct particle *common, struct particle *particles[], size_t count, + int left_spacing, int right_spacing) + = NULL; if (particle_list_new == NULL) { const struct plugin *plug = plugin_load("list", PLUGIN_PARTICLE); @@ -181,9 +167,8 @@ particle_simple_list_from_config(const struct yml_node *node, assert(particle_list_new != NULL); } - struct particle *common = particle_common_new( - 0, 0, NULL, fcft_clone(inherited.font), inherited.font_shaping, - inherited.foreground, NULL); + struct particle *common = particle_common_new(0, 0, NULL, fcft_clone(inherited.font), inherited.font_shaping, + inherited.foreground, NULL); return particle_list_new(common, parts, count, 0, 2); } @@ -206,10 +191,8 @@ conf_to_particle(const struct yml_node *node, struct conf_inherit inherited) const struct yml_node *foreground_node = yml_get_value(pair.value, "foreground"); const struct yml_node *deco_node = yml_get_value(pair.value, "deco"); - int left = margin != NULL ? yml_value_as_int(margin) : - left_margin != NULL ? yml_value_as_int(left_margin) : 0; - int right = margin != NULL ? yml_value_as_int(margin) : - right_margin != NULL ? yml_value_as_int(right_margin) : 0; + int left = margin != NULL ? yml_value_as_int(margin) : left_margin != NULL ? yml_value_as_int(left_margin) : 0; + int right = margin != NULL ? yml_value_as_int(margin) : right_margin != NULL ? yml_value_as_int(right_margin) : 0; char *on_click_templates[MOUSE_BTN_COUNT] = {NULL}; if (on_click != NULL) { @@ -234,10 +217,7 @@ conf_to_particle(const struct yml_node *node, struct conf_inherit inherited) } else if (yml_is_dict(on_click)) { - for (struct yml_dict_iter it = yml_dict_iter(on_click); - it.key != NULL; - yml_dict_next(&it)) - { + for (struct yml_dict_iter it = yml_dict_iter(on_click); it.key != NULL; yml_dict_next(&it)) { const char *key = yml_value_as_string(it.key); const char *yml_template = yml_value_as_string(it.value); @@ -286,16 +266,14 @@ conf_to_particle(const struct yml_node *node, struct conf_inherit inherited) * clone the font, since each particle takes ownership of its own * font. */ - struct fcft_font *font = font_node != NULL - ? conf_to_font(font_node) : fcft_clone(inherited.font); - enum font_shaping font_shaping = font_shaping_node != NULL - ? conf_to_font_shaping(font_shaping_node) : inherited.font_shaping; - pixman_color_t foreground = foreground_node != NULL - ? conf_to_color(foreground_node) : inherited.foreground; + struct fcft_font *font = font_node != NULL ? conf_to_font(font_node) : fcft_clone(inherited.font); + enum font_shaping font_shaping + = font_shaping_node != NULL ? conf_to_font_shaping(font_shaping_node) : inherited.font_shaping; + pixman_color_t foreground = foreground_node != NULL ? conf_to_color(foreground_node) : inherited.foreground; /* Instantiate base/common particle */ - struct particle *common = particle_common_new( - left, right, on_click_templates, font, font_shaping, foreground, deco); + struct particle *common + = particle_common_new(left, right, on_click_templates, font, font_shaping, foreground, deco); const struct particle_iface *iface = plugin_load_particle(type); @@ -323,8 +301,7 @@ conf_to_bar(const struct yml_node *bar, enum bar_backend backend) conf.height = yml_value_as_int(height); const struct yml_node *location = yml_get_value(bar, "location"); - conf.location = strcmp(yml_value_as_string(location), "top") == 0 - ? BAR_TOP : BAR_BOTTOM; + conf.location = strcmp(yml_value_as_string(location), "top") == 0 ? BAR_TOP : BAR_BOTTOM; const struct yml_node *background = yml_get_value(bar, "background"); conf.background = conf_to_color(background); @@ -352,7 +329,6 @@ conf_to_bar(const struct yml_node *bar, enum bar_backend backend) assert(false); } - const struct yml_node *spacing = yml_get_value(bar, "spacing"); if (spacing != NULL) conf.left_spacing = conf.right_spacing = yml_value_as_int(spacing); @@ -377,11 +353,8 @@ conf_to_bar(const struct yml_node *bar, enum bar_backend backend) if (right_margin != NULL) conf.right_margin = yml_value_as_int(right_margin); - const struct yml_node *trackpad_sensitivity = - yml_get_value(bar, "trackpad-sensitivity"); - conf.trackpad_sensitivity = trackpad_sensitivity != NULL - ? yml_value_as_int(trackpad_sensitivity) - : 30; + const struct yml_node *trackpad_sensitivity = yml_get_value(bar, "trackpad-sensitivity"); + conf.trackpad_sensitivity = trackpad_sensitivity != NULL ? yml_value_as_int(trackpad_sensitivity) : 30; const struct yml_node *border = yml_get_value(bar, "border"); if (border != NULL) { @@ -398,10 +371,8 @@ conf_to_bar(const struct yml_node *bar, enum bar_backend backend) const struct yml_node *bottom_margin = yml_get_value(border, "bottom-margin"); if (width != NULL) - conf.border.left_width = - conf.border.right_width = - conf.border.top_width = - conf.border.bottom_width = yml_value_as_int(width); + conf.border.left_width = conf.border.right_width = conf.border.top_width = conf.border.bottom_width + = yml_value_as_int(width); if (left_width != NULL) conf.border.left_width = yml_value_as_int(left_width); @@ -416,10 +387,8 @@ conf_to_bar(const struct yml_node *bar, enum bar_backend backend) conf.border.color = conf_to_color(color); if (margin != NULL) - conf.border.left_margin = - conf.border.right_margin = - conf.border.top_margin = - conf.border.bottom_margin = yml_value_as_int(margin); + conf.border.left_margin = conf.border.right_margin = conf.border.top_margin = conf.border.bottom_margin + = yml_value_as_int(margin); if (left_margin != NULL) conf.border.left_margin = yml_value_as_int(left_margin); @@ -474,10 +443,7 @@ conf_to_bar(const struct yml_node *bar, enum bar_backend backend) struct module **mods = calloc(count, sizeof(*mods)); size_t idx = 0; - for (struct yml_list_iter it = yml_list_iter(node); - it.node != NULL; - yml_list_next(&it), idx++) - { + for (struct yml_list_iter it = yml_list_iter(node); it.node != NULL; yml_list_next(&it), idx++) { struct yml_dict_iter m = yml_dict_iter(it.node); const char *mod_name = yml_value_as_string(m.key); @@ -489,16 +455,13 @@ conf_to_bar(const struct yml_node *bar, enum bar_backend backend) */ const struct yml_node *mod_font = yml_get_value(m.value, "font"); const struct yml_node *mod_font_shaping = yml_get_value(m.value, "font-shaping"); - const struct yml_node *mod_foreground = yml_get_value( - m.value, "foreground"); + const struct yml_node *mod_foreground = yml_get_value(m.value, "foreground"); struct conf_inherit mod_inherit = { - .font = mod_font != NULL - ? conf_to_font(mod_font) : inherited.font, - .font_shaping = mod_font_shaping != NULL - ? conf_to_font_shaping(mod_font_shaping) : inherited.font_shaping, - .foreground = mod_foreground != NULL - ? conf_to_color(mod_foreground) : inherited.foreground, + .font = mod_font != NULL ? conf_to_font(mod_font) : inherited.font, + .font_shaping + = mod_font_shaping != NULL ? conf_to_font_shaping(mod_font_shaping) : inherited.font_shaping, + .foreground = mod_foreground != NULL ? conf_to_color(mod_foreground) : inherited.foreground, }; const struct module_iface *iface = plugin_load_module(mod_name); diff --git a/config.h b/config.h index ceb4b85..86c2f4e 100644 --- a/config.h +++ b/config.h @@ -1,9 +1,9 @@ #pragma once -#include -#include "yml.h" #include "bar/bar.h" #include "font-shaping.h" +#include "yml.h" +#include struct bar; struct particle; @@ -25,6 +25,5 @@ struct conf_inherit { pixman_color_t foreground; }; -struct particle *conf_to_particle( - const struct yml_node *node, struct conf_inherit inherited); +struct particle *conf_to_particle(const struct yml_node *node, struct conf_inherit inherited); struct deco *conf_to_deco(const struct yml_node *node); diff --git a/decoration.h b/decoration.h index efb79e2..ba44e43 100644 --- a/decoration.h +++ b/decoration.h @@ -4,10 +4,11 @@ struct deco { void *private; - void (*expose)(const struct deco *deco, pixman_image_t *pix, - int x, int y, int width, int height); + void (*expose)(const struct deco *deco, pixman_image_t *pix, int x, int y, int width, int height); void (*destroy)(struct deco *deco); }; -#define DECORATION_COMMON_ATTRS \ - {NULL, false, NULL} +#define DECORATION_COMMON_ATTRS \ + { \ + NULL, false, NULL \ + } diff --git a/decorations/background.c b/decorations/background.c index b3b9ed2..f1430f1 100644 --- a/decorations/background.c +++ b/decorations/background.c @@ -1,12 +1,13 @@ #include -#include "../config.h" #include "../config-verify.h" +#include "../config.h" #include "../decoration.h" #include "../plugin.h" -struct private { - //struct rgba color; +struct private +{ + // struct rgba color; pixman_color_t color; }; @@ -22,9 +23,7 @@ static void expose(const struct deco *deco, pixman_image_t *pix, int x, int y, int width, int height) { const struct private *d = deco->private; - pixman_image_fill_rectangles( - PIXMAN_OP_OVER, pix, &d->color, 1, - &(pixman_rectangle16_t){x, y, width, height}); + pixman_image_fill_rectangles(PIXMAN_OP_OVER, pix, &d->color, 1, &(pixman_rectangle16_t){x, y, width, height}); } static struct deco * diff --git a/decorations/border.c b/decorations/border.c index 5868e88..e93fc4e 100644 --- a/decorations/border.c +++ b/decorations/border.c @@ -1,7 +1,7 @@ #include -#include "../config.h" #include "../config-verify.h" +#include "../config.h" #include "../decoration.h" #include "../plugin.h" @@ -12,7 +12,8 @@ #define min(x, y) ((x) < (y) ? (x) : (y)) #define max(x, y) ((x) > (y) ? (x) : (y)) -struct private { +struct private +{ pixman_color_t color; int size; }; @@ -29,21 +30,20 @@ static void expose(const struct deco *deco, pixman_image_t *pix, int x, int y, int width, int height) { const struct private *d = deco->private; - pixman_image_fill_rectangles( - PIXMAN_OP_OVER, pix, &d->color, 4, - (pixman_rectangle16_t []){ - /* Top */ - {x, y, width, min(d->size, height)}, + pixman_image_fill_rectangles(PIXMAN_OP_OVER, pix, &d->color, 4, + (pixman_rectangle16_t[]){ + /* Top */ + {x, y, width, min(d->size, height)}, - /* Bottom */ - {x, max(y + height - d->size, y), width, min(d->size, height)}, + /* Bottom */ + {x, max(y + height - d->size, y), width, min(d->size, height)}, - /* Left */ - {x, y, min(d->size, width), height}, + /* Left */ + {x, y, min(d->size, width), height}, - /* Right */ - {max(x + width - d->size, x), y, min(d->size, width), height}, - }); + /* Right */ + {max(x + width - d->size, x), y, min(d->size, width), height}, + }); } static struct deco * @@ -66,9 +66,7 @@ from_conf(const struct yml_node *node) { const struct yml_node *color = yml_get_value(node, "color"); const struct yml_node *size = yml_get_value(node, "size"); - return border_new( - conf_to_color(color), - size != NULL ? yml_value_as_int(size) : 1); + return border_new(conf_to_color(color), size != NULL ? yml_value_as_int(size) : 1); } static bool diff --git a/decorations/overline.c b/decorations/overline.c index 1beb896..e9ff8be 100644 --- a/decorations/overline.c +++ b/decorations/overline.c @@ -1,11 +1,12 @@ #include -#include "../config.h" #include "../config-verify.h" +#include "../config.h" #include "../decoration.h" #include "../plugin.h" -struct private { +struct private +{ int size; pixman_color_t color; }; @@ -22,9 +23,7 @@ static void expose(const struct deco *deco, pixman_image_t *pix, int x, int y, int width, int height) { const struct private *d = deco->private; - pixman_image_fill_rectangles( - PIXMAN_OP_OVER, pix, &d->color, 1, - &(pixman_rectangle16_t){x, y, width, d->size}); + pixman_image_fill_rectangles(PIXMAN_OP_OVER, pix, &d->color, 1, &(pixman_rectangle16_t){x, y, width, d->size}); } static struct deco * diff --git a/decorations/stack.c b/decorations/stack.c index 16c58ee..d8420d2 100644 --- a/decorations/stack.c +++ b/decorations/stack.c @@ -1,13 +1,14 @@ #include #define LOG_MODULE "stack" -#include "../log.h" -#include "../config.h" #include "../config-verify.h" +#include "../config.h" #include "../decoration.h" +#include "../log.h" #include "../plugin.h" -struct private { +struct private +{ struct deco **decos; size_t count; }; @@ -57,10 +58,7 @@ from_conf(const struct yml_node *node) struct deco *decos[count]; size_t idx = 0; - for (struct yml_list_iter it = yml_list_iter(node); - it.node != NULL; - yml_list_next(&it), idx++) - { + for (struct yml_list_iter it = yml_list_iter(node); it.node != NULL; yml_list_next(&it), idx++) { decos[idx] = conf_to_deco(it.node); } @@ -75,10 +73,7 @@ verify_conf(keychain_t *chain, const struct yml_node *node) return false; } - for (struct yml_list_iter it = yml_list_iter(node); - it.node != NULL; - yml_list_next(&it)) - { + for (struct yml_list_iter it = yml_list_iter(node); it.node != NULL; yml_list_next(&it)) { if (!conf_verify_decoration(chain, it.node)) return false; } diff --git a/decorations/underline.c b/decorations/underline.c index 5b8bbd3..0175116 100644 --- a/decorations/underline.c +++ b/decorations/underline.c @@ -1,11 +1,12 @@ #include -#include "../config.h" #include "../config-verify.h" +#include "../config.h" #include "../decoration.h" #include "../plugin.h" -struct private { +struct private +{ int size; pixman_color_t color; }; @@ -22,9 +23,8 @@ static void expose(const struct deco *deco, pixman_image_t *pix, int x, int y, int width, int height) { const struct private *d = deco->private; - pixman_image_fill_rectangles( - PIXMAN_OP_OVER, pix, &d->color, 1, - &(pixman_rectangle16_t){x, y + height - d->size, width, d->size}); + pixman_image_fill_rectangles(PIXMAN_OP_OVER, pix, &d->color, 1, + &(pixman_rectangle16_t){x, y + height - d->size, width, d->size}); } static struct deco * diff --git a/log.c b/log.c index 2d454a8..7ba4193 100644 --- a/log.c +++ b/log.c @@ -1,5 +1,6 @@ #include "log.h" +#include #include #include #include @@ -9,7 +10,6 @@ #include #include #include -#include #define ALEN(v) (sizeof(v) / sizeof((v)[0])) #define UNUSED __attribute__((unused)) @@ -32,23 +32,22 @@ static const struct { }; void -log_init(enum log_colorize _colorize, bool _do_syslog, - enum log_facility syslog_facility, enum log_class _log_level) +log_init(enum log_colorize _colorize, bool _do_syslog, enum log_facility syslog_facility, enum log_class _log_level) { static const int facility_map[] = { [LOG_FACILITY_USER] = LOG_USER, [LOG_FACILITY_DAEMON] = LOG_DAEMON, }; - colorize = _colorize == LOG_COLORIZE_NEVER - ? false : _colorize == LOG_COLORIZE_ALWAYS - ? true : isatty(STDERR_FILENO); + colorize = _colorize == LOG_COLORIZE_NEVER ? false + : _colorize == LOG_COLORIZE_ALWAYS ? true + : isatty(STDERR_FILENO); do_syslog = _do_syslog; log_level = _log_level; int slvl = log_level_map[_log_level].syslog_equivalent; if (do_syslog && slvl != -1) { - openlog(NULL, /*LOG_PID*/0, facility_map[syslog_facility]); + openlog(NULL, /*LOG_PID*/ 0, facility_map[syslog_facility]); setlogmask(LOG_UPTO(slvl)); } } @@ -61,8 +60,8 @@ log_deinit(void) } static void -_log(enum log_class log_class, const char *module, const char *file, int lineno, - const char *fmt, int sys_errno, va_list va) +_log(enum log_class log_class, const char *module, const char *file, int lineno, const char *fmt, int sys_errno, + va_list va) { assert(log_class > LOG_CLASS_NONE); assert(log_class < ALEN(log_level_map)); @@ -92,9 +91,8 @@ _log(enum log_class log_class, const char *module, const char *file, int lineno, } static void -_sys_log(enum log_class log_class, const char *module, - const char UNUSED *file, int UNUSED lineno, - const char *fmt, int sys_errno, va_list va) +_sys_log(enum log_class log_class, const char *module, const char UNUSED *file, int UNUSED lineno, const char *fmt, + int sys_errno, va_list va) { assert(log_class > LOG_CLASS_NONE); assert(log_class < ALEN(log_level_map)); @@ -119,8 +117,7 @@ _sys_log(enum log_class log_class, const char *module, } void -log_msg_va(enum log_class log_class, const char *module, - const char *file, int lineno, const char *fmt, va_list va) +log_msg_va(enum log_class log_class, const char *module, const char *file, int lineno, const char *fmt, va_list va) { va_list va2; va_copy(va2, va); @@ -130,8 +127,7 @@ log_msg_va(enum log_class log_class, const char *module, } void -log_msg(enum log_class log_class, const char *module, - const char *file, int lineno, const char *fmt, ...) +log_msg(enum log_class log_class, const char *module, const char *file, int lineno, const char *fmt, ...) { va_list va; va_start(va, fmt); @@ -140,17 +136,13 @@ log_msg(enum log_class log_class, const char *module, } void -log_errno_va(enum log_class log_class, const char *module, - const char *file, int lineno, - const char *fmt, va_list va) +log_errno_va(enum log_class log_class, const char *module, const char *file, int lineno, const char *fmt, va_list va) { log_errno_provided_va(log_class, module, file, lineno, errno, fmt, va); } void -log_errno(enum log_class log_class, const char *module, - const char *file, int lineno, - const char *fmt, ...) +log_errno(enum log_class log_class, const char *module, const char *file, int lineno, const char *fmt, ...) { va_list va; va_start(va, fmt); @@ -159,8 +151,7 @@ log_errno(enum log_class log_class, const char *module, } void -log_errno_provided_va(enum log_class log_class, const char *module, - const char *file, int lineno, int errno_copy, +log_errno_provided_va(enum log_class log_class, const char *module, const char *file, int lineno, int errno_copy, const char *fmt, va_list va) { va_list va2; @@ -171,8 +162,7 @@ log_errno_provided_va(enum log_class log_class, const char *module, } void -log_errno_provided(enum log_class log_class, const char *module, - const char *file, int lineno, int errno_copy, +log_errno_provided(enum log_class log_class, const char *module, const char *file, int lineno, int errno_copy, const char *fmt, ...) { va_list va; diff --git a/log.h b/log.h index 94fd178..48f16fe 100644 --- a/log.h +++ b/log.h @@ -1,68 +1,43 @@ #pragma once -#include #include +#include enum log_colorize { LOG_COLORIZE_NEVER, LOG_COLORIZE_ALWAYS, LOG_COLORIZE_AUTO }; enum log_facility { LOG_FACILITY_USER, LOG_FACILITY_DAEMON }; -enum log_class { - LOG_CLASS_NONE, - LOG_CLASS_ERROR, - LOG_CLASS_WARNING, - LOG_CLASS_INFO, - LOG_CLASS_DEBUG -}; +enum log_class { LOG_CLASS_NONE, LOG_CLASS_ERROR, LOG_CLASS_WARNING, LOG_CLASS_INFO, LOG_CLASS_DEBUG }; -void log_init(enum log_colorize colorize, bool do_syslog, - enum log_facility syslog_facility, enum log_class log_level); +void log_init(enum log_colorize colorize, bool do_syslog, enum log_facility syslog_facility, enum log_class log_level); void log_deinit(void); -void log_msg( - enum log_class log_class, const char *module, - const char *file, int lineno, - const char *fmt, ...) __attribute__((format (printf, 5, 6))); +void log_msg(enum log_class log_class, const char *module, const char *file, int lineno, const char *fmt, ...) + __attribute__((format(printf, 5, 6))); -void log_errno( - enum log_class log_class, const char *module, - const char *file, int lineno, - const char *fmt, ...) __attribute__((format (printf, 5, 6))); +void log_errno(enum log_class log_class, const char *module, const char *file, int lineno, const char *fmt, ...) + __attribute__((format(printf, 5, 6))); -void log_errno_provided( - enum log_class log_class, const char *module, - const char *file, int lineno, int _errno, - const char *fmt, ...) __attribute__((format (printf, 6, 7))); - -void log_msg_va( - enum log_class log_class, const char *module, - const char *file, int lineno, const char *fmt, va_list va) __attribute__((format (printf, 5, 0))); -void log_errno_va( - enum log_class log_class, const char *module, - const char *file, int lineno, - const char *fmt, va_list va) __attribute__((format (printf, 5, 0))); -void log_errno_provided_va( - enum log_class log_class, const char *module, - const char *file, int lineno, int _errno, - const char *fmt, va_list va) __attribute__((format (printf, 6, 0))); +void log_errno_provided(enum log_class log_class, const char *module, const char *file, int lineno, int _errno, + const char *fmt, ...) __attribute__((format(printf, 6, 7))); +void log_msg_va(enum log_class log_class, const char *module, const char *file, int lineno, const char *fmt, va_list va) + __attribute__((format(printf, 5, 0))); +void log_errno_va(enum log_class log_class, const char *module, const char *file, int lineno, const char *fmt, + va_list va) __attribute__((format(printf, 5, 0))); +void log_errno_provided_va(enum log_class log_class, const char *module, const char *file, int lineno, int _errno, + const char *fmt, va_list va) __attribute__((format(printf, 6, 0))); int log_level_from_string(const char *str); const char *log_level_string_hint(void); -#define LOG_ERR(...) \ - log_msg(LOG_CLASS_ERROR, LOG_MODULE, __FILE__, __LINE__, __VA_ARGS__) -#define LOG_ERRNO(...) \ - log_errno(LOG_CLASS_ERROR, LOG_MODULE, __FILE__, __LINE__, __VA_ARGS__) -#define LOG_ERRNO_P(_errno, ...) \ - log_errno_provided(LOG_CLASS_ERROR, LOG_MODULE, __FILE__, __LINE__, \ - _errno, __VA_ARGS__) -#define LOG_WARN(...) \ - log_msg(LOG_CLASS_WARNING, LOG_MODULE, __FILE__, __LINE__, __VA_ARGS__) -#define LOG_INFO(...) \ - log_msg(LOG_CLASS_INFO, LOG_MODULE, __FILE__, __LINE__, __VA_ARGS__) +#define LOG_ERR(...) log_msg(LOG_CLASS_ERROR, LOG_MODULE, __FILE__, __LINE__, __VA_ARGS__) +#define LOG_ERRNO(...) log_errno(LOG_CLASS_ERROR, LOG_MODULE, __FILE__, __LINE__, __VA_ARGS__) +#define LOG_ERRNO_P(_errno, ...) \ + log_errno_provided(LOG_CLASS_ERROR, LOG_MODULE, __FILE__, __LINE__, _errno, __VA_ARGS__) +#define LOG_WARN(...) log_msg(LOG_CLASS_WARNING, LOG_MODULE, __FILE__, __LINE__, __VA_ARGS__) +#define LOG_INFO(...) log_msg(LOG_CLASS_INFO, LOG_MODULE, __FILE__, __LINE__, __VA_ARGS__) #if defined(LOG_ENABLE_DBG) && LOG_ENABLE_DBG - #define LOG_DBG(...) \ - log_msg(LOG_CLASS_DEBUG, LOG_MODULE, __FILE__, __LINE__, __VA_ARGS__) +#define LOG_DBG(...) log_msg(LOG_CLASS_DEBUG, LOG_MODULE, __FILE__, __LINE__, __VA_ARGS__) #else - #define LOG_DBG(...) +#define LOG_DBG(...) #endif diff --git a/main.c b/main.c index 5d9df44..a9c6932 100644 --- a/main.c +++ b/main.c @@ -1,4 +1,6 @@ #include +#include +#include #include #include #include @@ -9,14 +11,12 @@ #include #include #include -#include -#include -#include -#include -#include #include #include +#include +#include +#include #include "bar/bar.h" #include "config.h" @@ -147,9 +147,8 @@ print_pid(const char *pid_file, bool *unlink_at_exit) int pid_fd = strtoul(pid_file, &end, 10); if (errno != 0 || *end != '\0') { - if ((pid_fd = open(pid_file, - O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, - S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) < 0) { + if ((pid_fd = open(pid_file, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) + < 0) { LOG_ERRNO("%s: failed to open", pid_file); return false; } else @@ -178,16 +177,16 @@ int main(int argc, char *const *argv) { static const struct option longopts[] = { - {"backend", required_argument, 0, 'b'}, - {"config", required_argument, 0, 'c'}, - {"validate", no_argument, 0, 'C'}, - {"print-pid", required_argument, 0, 'p'}, - {"log-level", required_argument, 0, 'd'}, - {"log-colorize", optional_argument, 0, 'l'}, - {"log-no-syslog", no_argument, 0, 's'}, - {"version", no_argument, 0, 'v'}, - {"help", no_argument, 0, 'h'}, - {NULL, no_argument, 0, 0}, + {"backend", required_argument, 0, 'b'}, + {"config", required_argument, 0, 'c'}, + {"validate", no_argument, 0, 'C'}, + {"print-pid", required_argument, 0, 'p'}, + {"log-level", required_argument, 0, 'd'}, + {"log-colorize", optional_argument, 0, 'l'}, + {"log-no-syslog", no_argument, 0, 's'}, + {"version", no_argument, 0, 'v'}, + {"help", no_argument, 0, 'h'}, + {NULL, no_argument, 0, 0}, }; bool unlink_pid_file = false; @@ -224,8 +223,7 @@ main(int argc, char *const *argv) fprintf(stderr, "%s: invalid configuration file: %s\n", optarg, strerror(errno)); return EXIT_FAILURE; } else if (!S_ISREG(st.st_mode) && !S_ISFIFO(st.st_mode)) { - fprintf(stderr, "%s: invalid configuration file: neither a regular file nor a pipe or FIFO\n", - optarg); + fprintf(stderr, "%s: invalid configuration file: neither a regular file nor a pipe or FIFO\n", optarg); return EXIT_FAILURE; } @@ -244,11 +242,7 @@ main(int argc, char *const *argv) case 'd': { int lvl = log_level_from_string(optarg); if (lvl < 0) { - fprintf( - stderr, - "-d,--log-level: %s: argument must be one of %s\n", - optarg, - log_level_string_hint()); + fprintf(stderr, "-d,--log-level: %s: argument must be one of %s\n", optarg, log_level_string_hint()); return EXIT_FAILURE; } log_level = lvl; @@ -292,12 +286,9 @@ main(int argc, char *const *argv) log_init(log_colorize, log_syslog, LOG_FACILITY_DAEMON, log_level); - _Static_assert((int)LOG_CLASS_ERROR == (int)FCFT_LOG_CLASS_ERROR, - "fcft log level enum offset"); - _Static_assert((int)LOG_COLORIZE_ALWAYS == (int)FCFT_LOG_COLORIZE_ALWAYS, - "fcft colorize enum mismatch"); - fcft_init((enum fcft_log_colorize)log_colorize, log_syslog, - (enum fcft_log_class)log_level); + _Static_assert((int)LOG_CLASS_ERROR == (int)FCFT_LOG_CLASS_ERROR, "fcft log level enum offset"); + _Static_assert((int)LOG_COLORIZE_ALWAYS == (int)FCFT_LOG_COLORIZE_ALWAYS, "fcft colorize enum mismatch"); + fcft_init((enum fcft_log_colorize)log_colorize, log_syslog, (enum fcft_log_class)log_level); atexit(&fcft_fini); const struct sigaction sa = {.sa_handler = &signal_handler}; diff --git a/module.c b/module.c index 1e80c32..d3bde3b 100644 --- a/module.c +++ b/module.c @@ -1,6 +1,6 @@ #include "module.h" -#include #include +#include #include struct module * diff --git a/module.h b/module.h index 5f1bc7c..3a83cdc 100644 --- a/module.h +++ b/module.h @@ -35,9 +35,9 @@ void module_default_destroy(struct module *mod); struct exposable *module_begin_expose(struct module *mod); /* List of attributes *all* modules implement */ -#define MODULE_COMMON_ATTRS \ - {"content", true, &conf_verify_particle}, \ - {"anchors", false, NULL}, \ - {"font", false, &conf_verify_font}, \ - {"foreground", false, &conf_verify_color}, \ - {NULL, false, NULL} +#define MODULE_COMMON_ATTRS \ + {"content", true, &conf_verify_particle}, {"anchors", false, NULL}, {"font", false, &conf_verify_font}, \ + {"foreground", false, &conf_verify_color}, \ + { \ + NULL, false, NULL \ + } diff --git a/modules/alsa.c b/modules/alsa.c index 6003ae6..8d7cd25 100644 --- a/modules/alsa.c +++ b/modules/alsa.c @@ -1,8 +1,8 @@ +#include #include #include -#include -#include #include +#include #include @@ -10,10 +10,10 @@ #define LOG_MODULE "alsa" #define LOG_ENABLE_DBG 0 -#include "../log.h" #include "../bar/bar.h" #include "../config-verify.h" #include "../config.h" +#include "../log.h" #include "../plugin.h" enum channel_type { CHANNEL_PLAYBACK, CHANNEL_CAPTURE }; @@ -29,7 +29,8 @@ struct channel { bool muted; }; -struct private { +struct private +{ char *card; char *mixer; char *volume_name; @@ -70,7 +71,8 @@ static void destroy(struct module *mod) { struct private *m = mod->private; - tll_foreach(m->channels, it) { + tll_foreach(m->channels, it) + { channel_free(&it->item); tll_remove(m->channels, it); } @@ -129,9 +131,7 @@ content(struct module *mod) if (use_db) { bool use_linear = db_max - db_min <= 24 * 100; if (use_linear) { - percent = db_min - db_max > 0 - ? round(100. * (db_cur - db_min) / (db_max - db_min)) - : 0; + percent = db_min - db_max > 0 ? round(100. * (db_cur - db_min) / (db_max - db_min)) : 0; } else { double normalized = pow(10, (double)(db_cur - db_max) / 6000.); if (db_min != SND_CTL_TLV_DB_GAIN_MUTE) { @@ -141,9 +141,7 @@ content(struct module *mod) percent = round(100. * normalized); } } else { - percent = vol_max - vol_min > 0 - ? round(100. * (vol_cur - vol_min) / (vol_max - vol_min)) - : 0; + percent = vol_max - vol_min > 0 ? round(100. * (vol_cur - vol_min) / (vol_max - vol_min)) : 0; } struct tag_set tags = { @@ -173,107 +171,94 @@ update_state(struct module *mod, snd_mixer_elem_t *elem) /* If volume level can be changed (i.e. this isn't just a switch; * e.g. a digital channel), get current channel levels */ - tll_foreach(m->channels, it) { + tll_foreach(m->channels, it) + { struct channel *chan = &it->item; - const bool has_volume = chan->type == CHANNEL_PLAYBACK - ? m->has_playback_volume : m->has_capture_volume; - const bool has_db = chan->type == CHANNEL_PLAYBACK - ? m->has_playback_db : m->has_capture_db; + const bool has_volume = chan->type == CHANNEL_PLAYBACK ? m->has_playback_volume : m->has_capture_volume; + const bool has_db = chan->type == CHANNEL_PLAYBACK ? m->has_playback_db : m->has_capture_db; if (!has_volume && !has_db) continue; - if (has_db) { chan->use_db = true; - const long min = chan->type == CHANNEL_PLAYBACK - ? m->playback_db_min : m->capture_db_min; - const long max = chan->type == CHANNEL_PLAYBACK - ? m->playback_db_max : m->capture_db_max; + const long min = chan->type == CHANNEL_PLAYBACK ? m->playback_db_min : m->capture_db_min; + const long max = chan->type == CHANNEL_PLAYBACK ? m->playback_db_max : m->capture_db_max; assert(min <= max); - int r = chan->type == CHANNEL_PLAYBACK - ? snd_mixer_selem_get_playback_dB(elem, chan->id, &chan->db_cur) - : snd_mixer_selem_get_capture_dB(elem, chan->id, &chan->db_cur); + int r = chan->type == CHANNEL_PLAYBACK ? snd_mixer_selem_get_playback_dB(elem, chan->id, &chan->db_cur) + : snd_mixer_selem_get_capture_dB(elem, chan->id, &chan->db_cur); if (r < 0) { - LOG_ERR("%s,%s: %s: failed to get current dB", - m->card, m->mixer, chan->name); + LOG_ERR("%s,%s: %s: failed to get current dB", m->card, m->mixer, chan->name); } if (chan->db_cur < min) { - LOG_WARN( - "%s,%s: %s: current dB is less than the indicated minimum: " - "%ld < %ld", m->card, m->mixer, chan->name, chan->db_cur, min); + LOG_WARN("%s,%s: %s: current dB is less than the indicated minimum: " + "%ld < %ld", + m->card, m->mixer, chan->name, chan->db_cur, min); chan->db_cur = min; } if (chan->db_cur > max) { - LOG_WARN( - "%s,%s: %s: current dB is greater than the indicated maximum: " - "%ld > %ld", m->card, m->mixer, chan->name, chan->db_cur, max); + LOG_WARN("%s,%s: %s: current dB is greater than the indicated maximum: " + "%ld > %ld", + m->card, m->mixer, chan->name, chan->db_cur, max); chan->db_cur = max; } assert(chan->db_cur >= min); - assert(chan->db_cur <= max ); + assert(chan->db_cur <= max); - LOG_DBG("%s,%s: %s: dB: %ld", - m->card, m->mixer, chan->name, chan->db_cur); + LOG_DBG("%s,%s: %s: dB: %ld", m->card, m->mixer, chan->name, chan->db_cur); } else chan->use_db = false; - const long min = chan->type == CHANNEL_PLAYBACK - ? m->playback_vol_min : m->capture_vol_min; - const long max = chan->type == CHANNEL_PLAYBACK - ? m->playback_vol_max : m->capture_vol_max; + const long min = chan->type == CHANNEL_PLAYBACK ? m->playback_vol_min : m->capture_vol_min; + const long max = chan->type == CHANNEL_PLAYBACK ? m->playback_vol_max : m->capture_vol_max; assert(min <= max); - int r = chan->type == CHANNEL_PLAYBACK - ? snd_mixer_selem_get_playback_volume(elem, chan->id, &chan->vol_cur) - : snd_mixer_selem_get_capture_volume(elem, chan->id, &chan->vol_cur); + int r = chan->type == CHANNEL_PLAYBACK ? snd_mixer_selem_get_playback_volume(elem, chan->id, &chan->vol_cur) + : snd_mixer_selem_get_capture_volume(elem, chan->id, &chan->vol_cur); if (r < 0) { - LOG_ERR("%s,%s: %s: failed to get current volume", - m->card, m->mixer, chan->name); + LOG_ERR("%s,%s: %s: failed to get current volume", m->card, m->mixer, chan->name); } if (chan->vol_cur < min) { - LOG_WARN( - "%s,%s: %s: current volume is less than the indicated minimum: " - "%ld < %ld", m->card, m->mixer, chan->name, chan->vol_cur, min); + LOG_WARN("%s,%s: %s: current volume is less than the indicated minimum: " + "%ld < %ld", + m->card, m->mixer, chan->name, chan->vol_cur, min); chan->vol_cur = min; } if (chan->vol_cur > max) { - LOG_WARN( - "%s,%s: %s: current volume is greater than the indicated maximum: " - "%ld > %ld", m->card, m->mixer, chan->name, chan->vol_cur, max); + LOG_WARN("%s,%s: %s: current volume is greater than the indicated maximum: " + "%ld > %ld", + m->card, m->mixer, chan->name, chan->vol_cur, max); chan->vol_cur = max; } assert(chan->vol_cur >= min); - assert(chan->vol_cur <= max ); + assert(chan->vol_cur <= max); - LOG_DBG("%s,%s: %s: volume: %ld", - m->card, m->mixer, chan->name, chan->vol_cur); + LOG_DBG("%s,%s: %s: volume: %ld", m->card, m->mixer, chan->name, chan->vol_cur); } /* Get channels’ muted state */ - tll_foreach(m->channels, it) { + tll_foreach(m->channels, it) + { struct channel *chan = &it->item; int unmuted; - int r = chan->type == CHANNEL_PLAYBACK - ? snd_mixer_selem_get_playback_switch(elem, chan->id, &unmuted) - : snd_mixer_selem_get_capture_switch(elem, chan->id, &unmuted); + int r = chan->type == CHANNEL_PLAYBACK ? snd_mixer_selem_get_playback_switch(elem, chan->id, &unmuted) + : snd_mixer_selem_get_capture_switch(elem, chan->id, &unmuted); if (r < 0) { - LOG_WARN("%s,%s: %s: failed to get muted state", - m->card, m->mixer, chan->name); + LOG_WARN("%s,%s: %s: failed to get muted state", m->card, m->mixer, chan->name); unmuted = 1; } @@ -309,10 +294,8 @@ run_while_online(struct module *mod) return ret; } - if (snd_mixer_attach(handle, m->card) != 0 || - snd_mixer_selem_register(handle, NULL, NULL) != 0 || - snd_mixer_load(handle) != 0) - { + if (snd_mixer_attach(handle, m->card) != 0 || snd_mixer_selem_register(handle, NULL, NULL) != 0 + || snd_mixer_load(handle) != 0) { LOG_ERR("failed to attach to card"); ret = RUN_FAILED_CONNECT; goto err; @@ -323,7 +306,7 @@ run_while_online(struct module *mod) snd_mixer_selem_id_set_index(sid, 0); snd_mixer_selem_id_set_name(sid, m->mixer); - snd_mixer_elem_t* elem = snd_mixer_find_selem(handle, sid); + snd_mixer_elem_t *elem = snd_mixer_find_selem(handle, sid); if (elem == NULL) { LOG_ERR("failed to find mixer"); goto err; @@ -332,30 +315,24 @@ run_while_online(struct module *mod) /* Get playback volume range */ m->has_playback_volume = snd_mixer_selem_has_playback_volume(elem) > 0; if (m->has_playback_volume) { - if (snd_mixer_selem_get_playback_volume_range( - elem, &m->playback_vol_min, &m->playback_vol_max) < 0) - { - LOG_ERR("%s,%s: failed to get playback volume range", - m->card, m->mixer); + if (snd_mixer_selem_get_playback_volume_range(elem, &m->playback_vol_min, &m->playback_vol_max) < 0) { + LOG_ERR("%s,%s: failed to get playback volume range", m->card, m->mixer); assert(m->playback_vol_min == 0); assert(m->playback_vol_max == 0); } if (m->playback_vol_min > m->playback_vol_max) { - LOG_WARN( - "%s,%s: indicated minimum playback volume is greater than the " - "maximum: %ld > %ld", - m->card, m->mixer, m->playback_vol_min, m->playback_vol_max); + LOG_WARN("%s,%s: indicated minimum playback volume is greater than the " + "maximum: %ld > %ld", + m->card, m->mixer, m->playback_vol_min, m->playback_vol_max); m->playback_vol_min = m->playback_vol_max; } } - if (snd_mixer_selem_get_playback_dB_range( - elem, &m->playback_db_min, &m->playback_db_max) < 0) - { - LOG_WARN( - "%s,%s: failed to get playback dB range, " - "will use raw volume values instead", m->card, m->mixer); + if (snd_mixer_selem_get_playback_dB_range(elem, &m->playback_db_min, &m->playback_db_max) < 0) { + LOG_WARN("%s,%s: failed to get playback dB range, " + "will use raw volume values instead", + m->card, m->mixer); m->has_playback_db = false; } else m->has_playback_db = true; @@ -363,30 +340,24 @@ run_while_online(struct module *mod) /* Get capture volume range */ m->has_capture_volume = snd_mixer_selem_has_capture_volume(elem) > 0; if (m->has_capture_volume) { - if (snd_mixer_selem_get_capture_volume_range( - elem, &m->capture_vol_min, &m->capture_vol_max) < 0) - { - LOG_ERR("%s,%s: failed to get capture volume range", - m->card, m->mixer); + if (snd_mixer_selem_get_capture_volume_range(elem, &m->capture_vol_min, &m->capture_vol_max) < 0) { + LOG_ERR("%s,%s: failed to get capture volume range", m->card, m->mixer); assert(m->capture_vol_min == 0); assert(m->capture_vol_max == 0); } if (m->capture_vol_min > m->capture_vol_max) { - LOG_WARN( - "%s,%s: indicated minimum capture volume is greater than the " - "maximum: %ld > %ld", - m->card, m->mixer, m->capture_vol_min, m->capture_vol_max); + LOG_WARN("%s,%s: indicated minimum capture volume is greater than the " + "maximum: %ld > %ld", + m->card, m->mixer, m->capture_vol_min, m->capture_vol_max); m->capture_vol_min = m->capture_vol_max; } } - if (snd_mixer_selem_get_capture_dB_range( - elem, &m->capture_db_min, &m->capture_db_max) < 0) - { - LOG_WARN( - "%s,%s: failed to get capture dB range, " - "will use raw volume values instead", m->card, m->mixer); + if (snd_mixer_selem_get_capture_dB_range(elem, &m->capture_db_min, &m->capture_db_max) < 0) { + LOG_WARN("%s,%s: failed to get capture dB range, " + "will use raw volume values instead", + m->card, m->mixer); m->has_capture_db = false; } else m->has_capture_db = true; @@ -400,7 +371,7 @@ run_while_online(struct module *mod) struct channel chan = { .id = i, .type = is_playback ? CHANNEL_PLAYBACK : CHANNEL_CAPTURE, - .name = strdup(snd_mixer_selem_channel_name( i)), + .name = strdup(snd_mixer_selem_channel_name(i)), }; tll_push_back(m->channels, chan); } @@ -413,13 +384,13 @@ run_while_online(struct module *mod) char channels_str[1024]; int channels_idx = 0; - tll_foreach(m->channels, it) { + tll_foreach(m->channels, it) + { const struct channel *chan = &it->item; - channels_idx += snprintf( - &channels_str[channels_idx], sizeof(channels_str) - channels_idx, - channels_idx == 0 ? "%s (%s)" : ", %s (%s)", - chan->name, chan->type == CHANNEL_PLAYBACK ? "🔊" : "🎤"); + channels_idx += snprintf(&channels_str[channels_idx], sizeof(channels_str) - channels_idx, + channels_idx == 0 ? "%s (%s)" : ", %s (%s)", chan->name, + chan->type == CHANNEL_PLAYBACK ? "🔊" : "🎤"); assert(channels_idx <= sizeof(channels_str)); } @@ -429,7 +400,8 @@ run_while_online(struct module *mod) bool volume_channel_is_valid = m->volume_name == NULL; bool muted_channel_is_valid = m->muted_name == NULL; - tll_foreach(m->channels, it) { + tll_foreach(m->channels, it) + { const struct channel *chan = &it->item; if (m->volume_name != NULL && strcmp(chan->name, m->volume_name) == 0) { m->volume_chan = chan; @@ -462,26 +434,14 @@ run_while_online(struct module *mod) update_state(mod, elem); LOG_INFO( - "%s,%s: %s range=%ld-%ld, current=%ld%s (sources: volume=%s, muted=%s)", - m->card, m->mixer, + "%s,%s: %s range=%ld-%ld, current=%ld%s (sources: volume=%s, muted=%s)", m->card, m->mixer, m->volume_chan->use_db ? "dB" : "volume", - (m->volume_chan->type == CHANNEL_PLAYBACK - ? (m->volume_chan->use_db - ? m->playback_db_min - : m->playback_vol_min) - : (m->volume_chan->use_db - ? m->capture_db_min - : m->capture_vol_min)), - (m->volume_chan->type == CHANNEL_PLAYBACK - ? (m->volume_chan->use_db - ? m->playback_db_max - : m->playback_vol_max) - : (m->volume_chan->use_db - ? m->capture_db_max - : m->capture_vol_max)), + (m->volume_chan->type == CHANNEL_PLAYBACK ? (m->volume_chan->use_db ? m->playback_db_min : m->playback_vol_min) + : (m->volume_chan->use_db ? m->capture_db_min : m->capture_vol_min)), + (m->volume_chan->type == CHANNEL_PLAYBACK ? (m->volume_chan->use_db ? m->playback_db_max : m->playback_vol_max) + : (m->volume_chan->use_db ? m->capture_db_max : m->capture_vol_max)), m->volume_chan->use_db ? m->volume_chan->db_cur : m->volume_chan->vol_cur, - m->muted_chan->muted ? " (muted)" : "", - m->volume_chan->name, m->muted_chan->name); + m->muted_chan->muted ? " (muted)" : "", m->volume_chan->name, m->muted_chan->name); mod->bar->refresh(mod->bar); @@ -595,8 +555,7 @@ run(struct module *mod) bool have_create_event = false; while (!have_create_event) { - struct pollfd fds[] = {{.fd = mod->abort_fd, .events = POLLIN}, - {.fd = ifd, .events = POLLIN}}; + struct pollfd fds[] = {{.fd = mod->abort_fd, .events = POLLIN}, {.fd = ifd, .events = POLLIN}}; int r = poll(fds, sizeof(fds) / sizeof(fds[0]), -1); if (r < 0) { @@ -638,7 +597,7 @@ run(struct module *mod) break; /* Consume inotify data */ - for (const char *ptr = buf; ptr < buf + len; ) { + for (const char *ptr = buf; ptr < buf + len;) { const struct inotify_event *e = (const struct inotify_event *)ptr; if (e->mask & IN_CREATE) { @@ -656,23 +615,20 @@ out: if (wd >= 0) inotify_rm_watch(ifd, wd); if (ifd >= 0) - close (ifd); + close(ifd); return ret; } static struct module * -alsa_new(const char *card, const char *mixer, - const char *volume_channel_name, const char *muted_channel_name, +alsa_new(const char *card, const char *mixer, const char *volume_channel_name, const char *muted_channel_name, struct particle *label) { struct private *priv = calloc(1, sizeof(*priv)); priv->label = label; priv->card = strdup(card); priv->mixer = strdup(mixer); - priv->volume_name = - volume_channel_name != NULL ? strdup(volume_channel_name) : NULL; - priv->muted_name = - muted_channel_name != NULL ? strdup(muted_channel_name) : NULL; + priv->volume_name = volume_channel_name != NULL ? strdup(volume_channel_name) : NULL; + priv->muted_name = muted_channel_name != NULL ? strdup(muted_channel_name) : NULL; struct module *mod = module_common_new(); mod->private = priv; @@ -692,12 +648,9 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) const struct yml_node *muted = yml_get_value(node, "muted"); const struct yml_node *content = yml_get_value(node, "content"); - return alsa_new( - yml_value_as_string(card), - yml_value_as_string(mixer), - volume != NULL ? yml_value_as_string(volume) : NULL, - muted != NULL ? yml_value_as_string(muted) : NULL, - conf_to_particle(content, inherited)); + return alsa_new(yml_value_as_string(card), yml_value_as_string(mixer), + volume != NULL ? yml_value_as_string(volume) : NULL, + muted != NULL ? yml_value_as_string(muted) : NULL, conf_to_particle(content, inherited)); } static bool diff --git a/modules/backlight.c b/modules/backlight.c index 9f26a14..0fa1787 100644 --- a/modules/backlight.c +++ b/modules/backlight.c @@ -1,25 +1,26 @@ +#include +#include +#include +#include #include #include #include -#include -#include #include -#include -#include -#include #include +#include #include #define LOG_MODULE "backlight" -#include "../log.h" #include "../bar/bar.h" -#include "../config.h" #include "../config-verify.h" +#include "../config.h" +#include "../log.h" #include "../plugin.h" -struct private { +struct private +{ struct particle *label; char *device; @@ -145,8 +146,7 @@ initialize(struct private *m) m->current_brightness = readint_from_fd(current_fd); - LOG_INFO("%s: brightness: %ld (max: %ld)", m->device, m->current_brightness, - m->max_brightness); + LOG_INFO("%s: brightness: %ld (max: %ld)", m->device, m->current_brightness, m->max_brightness); return current_fd; } @@ -244,8 +244,7 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) const struct yml_node *name = yml_get_value(node, "name"); const struct yml_node *c = yml_get_value(node, "content"); - return backlight_new( - yml_value_as_string(name), conf_to_particle(c, inherited)); + return backlight_new(yml_value_as_string(name), conf_to_particle(c, inherited)); } static bool diff --git a/modules/battery.c b/modules/battery.c index c947649..c3507d7 100644 --- a/modules/battery.c +++ b/modules/battery.c @@ -1,26 +1,26 @@ +#include +#include +#include #include #include #include #include #include -#include -#include -#include #include -#include #include +#include #include #include #define LOG_MODULE "battery" #define LOG_ENABLE_DBG 0 -#include "../log.h" #include "../bar/bar.h" -#include "../config.h" #include "../config-verify.h" +#include "../config.h" +#include "../log.h" #include "../plugin.h" #define max(x, y) ((x) > (y) ? (x) : (y)) @@ -37,7 +37,8 @@ struct current_state { struct timespec time; }; -struct private { +struct private +{ struct particle *label; long poll_interval; @@ -65,7 +66,7 @@ static int64_t difftimespec_ns(const struct timespec after, const struct timespec before) { return ((int64_t)after.tv_sec - (int64_t)before.tv_sec) * (int64_t)one_sec_in_ns - + ((int64_t)after.tv_nsec - (int64_t)before.tv_nsec); + + ((int64_t)after.tv_nsec - (int64_t)before.tv_nsec); } // Linear Exponential Moving Average (unevenly spaced time series) @@ -88,7 +89,7 @@ ema_linear(struct current_state *state, struct current_state curr, long tau) w2 = (1 - w) / tmp; } else { // Use taylor expansion for numerical stability - w2 = 1 - tmp/2 + tmp*tmp/6 - tmp*tmp*tmp/24; + w2 = 1 - tmp / 2 + tmp * tmp / 6 - tmp * tmp * tmp / 24; } double ema = state->ema * w + curr.current * (1 - w2) + state->current * (w2 - w); @@ -101,8 +102,7 @@ ema_linear(struct current_state *state, struct current_state curr, long tau) } static void -timespec_sub(const struct timespec *a, const struct timespec *b, - struct timespec *res) +timespec_sub(const struct timespec *a, const struct timespec *b, struct timespec *res) { res->tv_sec = a->tv_sec - b->tv_sec; @@ -145,11 +145,8 @@ content(struct module *mod) mtx_lock(&mod->lock); - assert(m->state == STATE_FULL || - m->state == STATE_NOTCHARGING || - m->state == STATE_CHARGING || - m->state == STATE_DISCHARGING || - m->state == STATE_UNKNOWN); + assert(m->state == STATE_FULL || m->state == STATE_NOTCHARGING || m->state == STATE_CHARGING + || m->state == STATE_DISCHARGING || m->state == STATE_UNKNOWN); unsigned long hours; unsigned long minutes; @@ -162,9 +159,8 @@ content(struct module *mod) minutes = m->time_to_full / 60; hours = minutes / 60; minutes = minutes % 60; - } else if (m->energy_full >= 0 && m->charge && m->power >= 0) { - unsigned long energy = m->state == STATE_CHARGING - ? m->energy_full - m->energy : m->energy; + } else if (m->energy_full >= 0 && m->charge && m->power >= 0) { + unsigned long energy = m->state == STATE_CHARGING ? m->energy_full - m->energy : m->energy; double hours_as_float; if (m->state == STATE_FULL || m->state == STATE_NOTCHARGING) @@ -177,8 +173,7 @@ content(struct module *mod) hours = hours_as_float; minutes = (hours_as_float - (double)hours) * 60; } else if (m->charge_full >= 0 && m->charge >= 0 && m->ema_current.current >= 0) { - unsigned long charge = m->state == STATE_CHARGING - ? m->charge_full - m->charge : m->charge; + unsigned long charge = m->state == STATE_CHARGING ? m->charge_full - m->charge : m->charge; double hours_as_float; if (m->state == STATE_FULL || m->state == STATE_NOTCHARGING) @@ -281,8 +276,7 @@ initialize(struct private *m) { int fd = openat(base_dir_fd, "manufacturer", O_RDONLY); if (fd == -1) { - LOG_WARN("/sys/class/power_supply/%s/manufacturer: %s", - m->battery, strerror(errno)); + LOG_WARN("/sys/class/power_supply/%s/manufacturer: %s", m->battery, strerror(errno)); m->manufacturer = NULL; } else { m->manufacturer = strdup(readline_from_fd(fd, sizeof(line_buf), line_buf)); @@ -293,8 +287,7 @@ initialize(struct private *m) { int fd = openat(base_dir_fd, "model_name", O_RDONLY); if (fd == -1) { - LOG_WARN("/sys/class/power_supply/%s/model_name: %s", - m->battery, strerror(errno)); + LOG_WARN("/sys/class/power_supply/%s/model_name: %s", m->battery, strerror(errno)); m->model = NULL; } else { m->model = strdup(readline_from_fd(fd, sizeof(line_buf), line_buf)); @@ -302,9 +295,8 @@ initialize(struct private *m) } } - if (faccessat(base_dir_fd, "energy_full_design", O_RDONLY, 0) == 0 && - faccessat(base_dir_fd, "energy_full", O_RDONLY, 0) == 0) - { + if (faccessat(base_dir_fd, "energy_full_design", O_RDONLY, 0) == 0 + && faccessat(base_dir_fd, "energy_full", O_RDONLY, 0) == 0) { { int fd = openat(base_dir_fd, "energy_full_design", O_RDONLY); if (fd == -1) { @@ -330,9 +322,8 @@ initialize(struct private *m) m->energy_full = m->energy_full_design = -1; } - if (faccessat(base_dir_fd, "charge_full_design", O_RDONLY, 0) == 0 && - faccessat(base_dir_fd, "charge_full", O_RDONLY, 0) == 0) - { + if (faccessat(base_dir_fd, "charge_full_design", O_RDONLY, 0) == 0 + && faccessat(base_dir_fd, "charge_full", O_RDONLY, 0) == 0) { { int fd = openat(base_dir_fd, "charge_full_design", O_RDONLY); if (fd == -1) { @@ -462,8 +453,8 @@ update_status(struct module *mod) } LOG_DBG("capacity: %ld, energy: %ld, power: %ld, charge=%ld, current=%ld, " - "time-to-empty: %ld, time-to-full: %ld", capacity, - energy, power, charge, current, time_to_empty, time_to_full); + "time-to-empty: %ld, time-to-full: %ld", + capacity, energy, power, charge, current, time_to_empty, time_to_full); mtx_lock(&mod->lock); if (m->state != state) { @@ -494,13 +485,10 @@ run(struct module *mod) if (!initialize(m)) return -1; - LOG_INFO("%s: %s %s (at %.1f%% of original capacity)", - m->battery, m->manufacturer, m->model, - (m->energy_full > 0 - ? 100.0 * m->energy_full / m->energy_full_design - : m->charge_full > 0 - ? 100.0 * m->charge_full / m->charge_full_design - : 0.0)); + LOG_INFO("%s: %s %s (at %.1f%% of original capacity)", m->battery, m->manufacturer, m->model, + (m->energy_full > 0 ? 100.0 * m->energy_full / m->energy_full_design + : m->charge_full > 0 ? 100.0 * m->charge_full / m->charge_full_design + : 0.0)); int ret = 1; @@ -555,12 +543,11 @@ run(struct module *mod) struct udev_device *dev = udev_monitor_receive_device(mon); if (dev != NULL) { const char *sysname = udev_device_get_sysname(dev); - udev_for_us = - sysname != NULL && strcmp(sysname, m->battery) == 0; + udev_for_us = sysname != NULL && strcmp(sysname, m->battery) == 0; if (!udev_for_us) { - LOG_DBG("udev notification not for us (%s != %s)", - m->battery, sysname != sysname ? sysname : "NULL"); + LOG_DBG("udev notification not for us (%s != %s)", m->battery, + sysname != sysname ? sysname : "NULL"); } else LOG_DBG("triggering update due to udev notification"); @@ -586,11 +573,9 @@ run(struct module *mod) struct timespec timeout_consumed; timespec_sub(&time_after_poll, &time_before_poll, &timeout_consumed); - const int timeout_consumed_ms = - timeout_consumed.tv_sec * 1000 + timeout_consumed.tv_nsec / 1000000; + const int timeout_consumed_ms = timeout_consumed.tv_sec * 1000 + timeout_consumed.tv_nsec / 1000000; - LOG_DBG("timeout-left before: %dms, consumed: %dms, updated: %dms", - timeout_left_ms, timeout_consumed_ms, + LOG_DBG("timeout-left before: %dms, consumed: %dms, updated: %dms", timeout_left_ms, timeout_consumed_ms, max(timeout_left_ms - timeout_consumed_ms, 0)); timeout_left_ms -= timeout_consumed_ms; @@ -608,7 +593,8 @@ out: } static struct module * -battery_new(const char *battery, struct particle *label, long poll_interval_msecs, int battery_scale, long smoothing_secs) +battery_new(const char *battery, struct particle *label, long poll_interval_msecs, int battery_scale, + long smoothing_secs) { struct private *m = calloc(1, sizeof(*m)); m->label = label; @@ -617,7 +603,7 @@ battery_new(const char *battery, struct particle *label, long poll_interval_msec m->smoothing_scale = smoothing_secs * one_sec_in_ns; m->battery = strdup(battery); m->state = STATE_UNKNOWN; - m->ema_current = (struct current_state){ -1, 0, (struct timespec){0, 0} }; + m->ema_current = (struct current_state){-1, 0, (struct timespec){0, 0}}; struct module *mod = module_common_new(); mod->private = m; @@ -637,18 +623,10 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) const struct yml_node *battery_scale = yml_get_value(node, "battery-scale"); const struct yml_node *smoothing_secs = yml_get_value(node, "smoothing-secs"); - return battery_new( - yml_value_as_string(name), - conf_to_particle(c, inherited), - (poll_interval != NULL - ? yml_value_as_int(poll_interval) - : default_poll_interval), - (battery_scale != NULL - ? yml_value_as_int(battery_scale) - : 1), - (smoothing_secs != NULL - ? yml_value_as_int(smoothing_secs) - : 100)); + return battery_new(yml_value_as_string(name), conf_to_particle(c, inherited), + (poll_interval != NULL ? yml_value_as_int(poll_interval) : default_poll_interval), + (battery_scale != NULL ? yml_value_as_int(battery_scale) : 1), + (smoothing_secs != NULL ? yml_value_as_int(smoothing_secs) : 100)); } static bool @@ -660,8 +638,7 @@ conf_verify_poll_interval(keychain_t *chain, const struct yml_node *node) const long value = yml_value_as_int(node); if (value != 0 && value < min_poll_interval) { - LOG_ERR("%s: interval value cannot be less than %ldms", - conf_err_prefix(chain, node), min_poll_interval); + LOG_ERR("%s: interval value cannot be less than %ldms", conf_err_prefix(chain, node), min_poll_interval); return false; } diff --git a/modules/clock.c b/modules/clock.c index 0487a9d..1758c0a 100644 --- a/modules/clock.c +++ b/modules/clock.c @@ -1,21 +1,22 @@ +#include +#include #include #include #include -#include -#include #include #include #define LOG_MODULE "clock" #define LOG_ENABLE_DBG 0 -#include "../log.h" #include "../bar/bar.h" -#include "../config.h" #include "../config-verify.h" +#include "../config.h" +#include "../log.h" #include "../plugin.h" -struct private { +struct private +{ struct particle *label; enum { UPDATE_GRANULARITY_SECONDS, @@ -57,8 +58,7 @@ content(struct module *mod) strftime(time_str, sizeof(time_str), m->time_format, tm); struct tag_set tags = { - .tags = (struct tag *[]){tag_new_string(mod, "time", time_str), - tag_new_string(mod, "date", date_str)}, + .tags = (struct tag *[]){tag_new_string(mod, "time", time_str), tag_new_string(mod, "date", date_str)}, .count = 2, }; @@ -90,15 +90,12 @@ run(struct module *mod) switch (m->update_granularity) { case UPDATE_GRANULARITY_SECONDS: { - const struct timeval next_second = { - .tv_sec = now.tv_sec + 1, - .tv_usec = 0}; + const struct timeval next_second = {.tv_sec = now.tv_sec + 1, .tv_usec = 0}; struct timeval _timeout; timersub(&next_second, &now, &_timeout); - assert(_timeout.tv_sec == 0 || - (_timeout.tv_sec == 1 && _timeout.tv_usec == 0)); + assert(_timeout.tv_sec == 0 || (_timeout.tv_sec == 1 && _timeout.tv_usec == 0)); timeout_ms = _timeout.tv_usec / 1000; break; } @@ -118,8 +115,7 @@ run(struct module *mod) /* Add 1ms to account for rounding errors */ timeout_ms++; - LOG_DBG("now: %lds %ldµs -> timeout: %dms", - now.tv_sec, now.tv_usec, timeout_ms); + LOG_DBG("now: %lds %ldµs -> timeout: %dms", now.tv_sec, now.tv_usec, timeout_ms); struct pollfd fds[] = {{.fd = mod->abort_fd, .events = POLLIN}}; if (poll(fds, 1, timeout_ms) < 0) { @@ -142,8 +138,7 @@ run(struct module *mod) } static struct module * -clock_new(struct particle *label, const char *date_format, - const char *time_format, bool utc) +clock_new(struct particle *label, const char *date_format, const char *time_format, bool utc) { struct private *m = calloc(1, sizeof(*m)); m->label = label; @@ -152,20 +147,12 @@ clock_new(struct particle *label, const char *date_format, m->utc = utc; static const char *const seconds_formatters[] = { - "%c", - "%s", - "%S", - "%T", - "%r", - "%X", + "%c", "%s", "%S", "%T", "%r", "%X", }; m->update_granularity = UPDATE_GRANULARITY_MINUTES; - for (size_t i = 0; - i < sizeof(seconds_formatters) / sizeof(seconds_formatters[0]); - i++) - { + for (size_t i = 0; i < sizeof(seconds_formatters) / sizeof(seconds_formatters[0]); i++) { if (strstr(time_format, seconds_formatters[i]) != NULL) { m->update_granularity = UPDATE_GRANULARITY_SECONDS; break; @@ -173,8 +160,7 @@ clock_new(struct particle *label, const char *date_format, } LOG_DBG("using %s update granularity", - (m->update_granularity == UPDATE_GRANULARITY_MINUTES - ? "minutes" : "seconds")); + (m->update_granularity == UPDATE_GRANULARITY_MINUTES ? "minutes" : "seconds")); struct module *mod = module_common_new(); mod->private = m; @@ -193,11 +179,9 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) const struct yml_node *time_format = yml_get_value(node, "time-format"); const struct yml_node *utc = yml_get_value(node, "utc"); - return clock_new( - conf_to_particle(c, inherited), - date_format != NULL ? yml_value_as_string(date_format) : "%x", - time_format != NULL ? yml_value_as_string(time_format) : "%H:%M", - utc != NULL ? yml_value_as_bool(utc) : false); + return clock_new(conf_to_particle(c, inherited), date_format != NULL ? yml_value_as_string(date_format) : "%x", + time_format != NULL ? yml_value_as_string(time_format) : "%H:%M", + utc != NULL ? yml_value_as_bool(utc) : false); } static bool diff --git a/modules/cpu.c b/modules/cpu.c index 455d45d..833c188 100644 --- a/modules/cpu.c +++ b/modules/cpu.c @@ -31,7 +31,8 @@ struct cpu_stats { uint32_t *cur_cores_nidle; }; -struct private { +struct private +{ struct particle *template; uint16_t interval; size_t core_count; @@ -69,28 +70,22 @@ get_cpu_nb_cores() } static bool -parse_proc_stat_line(const char *line, uint32_t *user, uint32_t *nice, - uint32_t *system, uint32_t *idle, uint32_t *iowait, - uint32_t *irq, uint32_t *softirq, uint32_t *steal, - uint32_t *guest, uint32_t *guestnice) +parse_proc_stat_line(const char *line, uint32_t *user, uint32_t *nice, uint32_t *system, uint32_t *idle, + uint32_t *iowait, uint32_t *irq, uint32_t *softirq, uint32_t *steal, uint32_t *guest, + uint32_t *guestnice) { int32_t core_id; if (line[sizeof("cpu") - 1] == ' ') { - int read = sscanf( - line, - "cpu %" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32 - " %" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32, - user, nice, system, idle, iowait, irq, softirq, steal, guest, - guestnice); + int read = sscanf(line, + "cpu %" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32 + " %" SCNu32 " %" SCNu32 " %" SCNu32, + user, nice, system, idle, iowait, irq, softirq, steal, guest, guestnice); return read == 10; } else { - int read = sscanf( - line, - "cpu%" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32 - " %" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32 - " %" SCNu32, - &core_id, user, nice, system, idle, iowait, irq, softirq, steal, - guest, guestnice); + int read = sscanf(line, + "cpu%" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32 + " %" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32, + &core_id, user, nice, system, idle, iowait, irq, softirq, steal, guest, guestnice); return read == 11; } } @@ -98,18 +93,12 @@ parse_proc_stat_line(const char *line, uint32_t *user, uint32_t *nice, static uint8_t get_cpu_usage_percent(const struct cpu_stats *cpu_stats, int8_t core_idx) { - uint32_t prev_total = - cpu_stats->prev_cores_idle[core_idx + 1] + - cpu_stats->prev_cores_nidle[core_idx + 1]; + uint32_t prev_total = cpu_stats->prev_cores_idle[core_idx + 1] + cpu_stats->prev_cores_nidle[core_idx + 1]; - uint32_t cur_total = - cpu_stats->cur_cores_idle[core_idx + 1] + - cpu_stats->cur_cores_nidle[core_idx + 1]; + uint32_t cur_total = cpu_stats->cur_cores_idle[core_idx + 1] + cpu_stats->cur_cores_nidle[core_idx + 1]; double totald = cur_total - prev_total; - double nidled = - cpu_stats->cur_cores_nidle[core_idx + 1] - - cpu_stats->prev_cores_nidle[core_idx + 1]; + double nidled = cpu_stats->cur_cores_nidle[core_idx + 1] - cpu_stats->prev_cores_nidle[core_idx + 1]; double percent = (nidled * 100) / (totald + 1); return round(percent); @@ -143,10 +132,8 @@ refresh_cpu_stats(struct cpu_stats *cpu_stats, size_t core_count) while ((read = getline(&line, &len, fp)) != -1 && core <= core_count) { if (strncmp(line, "cpu", sizeof("cpu") - 1) == 0) { - if (!parse_proc_stat_line( - line, &user, &nice, &system, &idle, &iowait, &irq, &softirq, - &steal, &guest, &guestnice)) - { + if (!parse_proc_stat_line(line, &user, &nice, &system, &idle, &iowait, &irq, &softirq, &steal, &guest, + &guestnice)) { LOG_ERR("unable to parse /proc/stat line"); goto exit; } @@ -251,15 +238,11 @@ cpu_new(uint16_t interval, struct particle *template) p->interval = interval; p->core_count = nb_cores; - p->cpu_stats.prev_cores_nidle = calloc( - nb_cores + 1, sizeof(*p->cpu_stats.prev_cores_nidle)); - p->cpu_stats.prev_cores_idle = calloc( - nb_cores + 1, sizeof(*p->cpu_stats.prev_cores_idle)); + p->cpu_stats.prev_cores_nidle = calloc(nb_cores + 1, sizeof(*p->cpu_stats.prev_cores_nidle)); + p->cpu_stats.prev_cores_idle = calloc(nb_cores + 1, sizeof(*p->cpu_stats.prev_cores_idle)); - p->cpu_stats.cur_cores_nidle = calloc( - nb_cores + 1, sizeof(*p->cpu_stats.cur_cores_nidle)); - p->cpu_stats.cur_cores_idle = calloc( - nb_cores + 1, sizeof(*p->cpu_stats.cur_cores_idle)); + p->cpu_stats.cur_cores_nidle = calloc(nb_cores + 1, sizeof(*p->cpu_stats.cur_cores_nidle)); + p->cpu_stats.cur_cores_idle = calloc(nb_cores + 1, sizeof(*p->cpu_stats.cur_cores_idle)); struct module *mod = module_common_new(); mod->private = p; @@ -276,9 +259,7 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) const struct yml_node *interval = yml_get_value(node, "poll-interval"); const struct yml_node *c = yml_get_value(node, "content"); - return cpu_new( - interval == NULL ? min_poll_interval : yml_value_as_int(interval), - conf_to_particle(c, inherited)); + return cpu_new(interval == NULL ? min_poll_interval : yml_value_as_int(interval), conf_to_particle(c, inherited)); } static bool @@ -288,8 +269,7 @@ conf_verify_poll_interval(keychain_t *chain, const struct yml_node *node) return false; if (yml_value_as_int(node) < min_poll_interval) { - LOG_ERR("%s: interval value cannot be less than %ldms", - conf_err_prefix(chain, node), min_poll_interval); + LOG_ERR("%s: interval value cannot be less than %ldms", conf_err_prefix(chain, node), min_poll_interval); return false; } diff --git a/modules/disk-io.c b/modules/disk-io.c index 7bf48e8..90abdba 100644 --- a/modules/disk-io.c +++ b/modules/disk-io.c @@ -1,9 +1,9 @@ +#include #include #include #include #include #include -#include #include @@ -34,7 +34,8 @@ struct device_stats { bool exists; }; -struct private { +struct private +{ struct particle *label; uint16_t interval; tll(struct device_stats *) devices; @@ -63,7 +64,7 @@ is_disk(char const *name) return found; } -static struct device_stats* +static struct device_stats * new_device_stats(char const *name) { struct device_stats *dev = malloc(sizeof(*dev)); @@ -84,9 +85,7 @@ destroy(struct module *mod) { struct private *m = mod->private; m->label->destroy(m->label); - tll_foreach(m->devices, it) { - free_device_stats(it->item); - } + tll_foreach(m->devices, it) { free_device_stats(it->item); } tll_free(m->devices); free(m); module_default_destroy(mod); @@ -126,9 +125,7 @@ refresh_device_stats(struct private *m) * The 'exists' variable is what keep tracks of whether or not /proc/diskstats * is still reporting the device (i.e., it is still connected). */ - tll_foreach(m->devices, it) { - it->item->exists = false; - } + tll_foreach(m->devices, it) { it->item->exists = false; } while ((read = getline(&line, &len, fp)) != -1) { /* @@ -156,25 +153,23 @@ refresh_device_stats(struct private *m) uint32_t completed_flushes = 0; uint32_t flushing_time = 0; if (!sscanf(line, - " %" SCNu8 " %" SCNu8 " %ms %" SCNu32 " %" SCNu32 " %" SCNu64 " %" SCNu32 - " %" SCNu32 " %" SCNu32 " %" SCNu64 " %" SCNu32 " %" SCNu32 " %" SCNu32 - " %" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32 - " %" SCNu32, - &major_number, &minor_number, &device_name, &completed_reads, - &merged_reads, §ors_read, &reading_time, &completed_writes, - &merged_writes, §ors_written, &writting_time, &ios_in_progress, - &io_time, &io_weighted_time, &completed_discards, &merged_discards, - §ors_discarded, &discarding_time, &completed_flushes, &flushing_time)) - { + " %" SCNu8 " %" SCNu8 " %ms %" SCNu32 " %" SCNu32 " %" SCNu64 " %" SCNu32 " %" SCNu32 " %" SCNu32 + " %" SCNu64 " %" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32 + " %" SCNu32 " %" SCNu32 " %" SCNu32, + &major_number, &minor_number, &device_name, &completed_reads, &merged_reads, §ors_read, + &reading_time, &completed_writes, &merged_writes, §ors_written, &writting_time, + &ios_in_progress, &io_time, &io_weighted_time, &completed_discards, &merged_discards, + §ors_discarded, &discarding_time, &completed_flushes, &flushing_time)) { LOG_ERR("unable to parse /proc/diskstats line"); free(device_name); goto exit; } bool found = false; - tll_foreach(m->devices, it) { + tll_foreach(m->devices, it) + { struct device_stats *dev = it->item; - if (strcmp(dev->name, device_name) == 0){ + if (strcmp(dev->name, device_name) == 0) { dev->prev_sectors_read = dev->cur_sectors_read; dev->prev_sectors_written = dev->cur_sectors_written; dev->ios_in_progress = ios_in_progress; @@ -200,8 +195,9 @@ refresh_device_stats(struct private *m) free(device_name); } - tll_foreach(m->devices, it) { - if (!it->item->exists){ + tll_foreach(m->devices, it) + { + if (!it->item->exists) { free_device_stats(it->item); tll_remove(m->devices, it); } @@ -221,12 +217,13 @@ content(struct module *mod) mtx_lock(&mod->lock); struct exposable *tag_parts[p->devices.length + 1]; int i = 0; - tll_foreach(p->devices, it) { + tll_foreach(p->devices, it) + { struct device_stats *dev = it->item; uint64_t bytes_read = (dev->cur_sectors_read - dev->prev_sectors_read) * 512; uint64_t bytes_written = (dev->cur_sectors_written - dev->prev_sectors_written) * 512; - if (dev->is_disk){ + if (dev->is_disk) { total_bytes_read += bytes_read; total_bytes_written += bytes_written; total_ios_in_progress += dev->ios_in_progress; @@ -314,9 +311,8 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) const struct yml_node *interval = yml_get_value(node, "poll-interval"); const struct yml_node *c = yml_get_value(node, "content"); - return disk_io_new( - interval == NULL ? min_poll_interval : yml_value_as_int(interval), - conf_to_particle(c, inherited)); + return disk_io_new(interval == NULL ? min_poll_interval : yml_value_as_int(interval), + conf_to_particle(c, inherited)); } static bool @@ -326,9 +322,7 @@ conf_verify_poll_interval(keychain_t *chain, const struct yml_node *node) return false; if (yml_value_as_int(node) < min_poll_interval) { - LOG_ERR( - "%s: poll-interval value cannot be less than %ldms", - conf_err_prefix(chain, node), min_poll_interval); + LOG_ERR("%s: poll-interval value cannot be less than %ldms", conf_err_prefix(chain, node), min_poll_interval); return false; } diff --git a/modules/dwl.c b/modules/dwl.c index a9a5bab..4d9e8fa 100644 --- a/modules/dwl.c +++ b/modules/dwl.c @@ -249,13 +249,16 @@ process_line(char *line, struct module *module) assert(false); /* unreachable */ break; case LINE_MODE_FULLSCREEN: - private->fullscreen = (strcmp(string, "0") != 0); + private + ->fullscreen = (strcmp(string, "0") != 0); break; case LINE_MODE_FLOATING: - private->floating = (strcmp(string, "0") != 0); + private + ->floating = (strcmp(string, "0") != 0); break; case LINE_MODE_SELMON: - private->selmon = (strcmp(string, "0") != 0); + private + ->selmon = (strcmp(string, "0") != 0); break; case LINE_MODE_LAYOUT: free(private->layout); @@ -438,8 +441,7 @@ run(struct module *module) } static struct module * -dwl_new(struct particle *label, int number_of_tags, - struct yml_node const *name_of_tags, char const *dwl_info_filename) +dwl_new(struct particle *label, int number_of_tags, struct yml_node const *name_of_tags, char const *dwl_info_filename) { struct private *private = calloc(1, sizeof(struct private)); private->label = label; @@ -480,8 +482,8 @@ from_conf(struct yml_node const *node, struct conf_inherit inherited) struct yml_node const *name_of_tags = yml_get_value(node, "name-of-tags"); struct yml_node const *dwl_info_filename = yml_get_value(node, "dwl-info-filename"); - return dwl_new(conf_to_particle(content, inherited), yml_value_as_int(number_of_tags), - name_of_tags, yml_value_as_string(dwl_info_filename)); + return dwl_new(conf_to_particle(content, inherited), yml_value_as_int(number_of_tags), name_of_tags, + yml_value_as_string(dwl_info_filename)); } static bool diff --git a/modules/foreign-toplevel.c b/modules/foreign-toplevel.c index d454a39..ccd6d5b 100644 --- a/modules/foreign-toplevel.c +++ b/modules/foreign-toplevel.c @@ -1,7 +1,7 @@ -#include -#include #include #include +#include +#include #include @@ -11,8 +11,8 @@ #define LOG_MODULE "foreign-toplevel" #define LOG_ENABLE_DBG 0 #include "../log.h" -#include "../plugin.h" #include "../particles/dynlist.h" +#include "../plugin.h" #include "wlr-foreign-toplevel-management-unstable-v1.h" #include "xdg-output-unstable-v1.h" @@ -46,7 +46,8 @@ struct toplevel { tll(const struct output *) outputs; }; -struct private { +struct private +{ struct particle *template; uint32_t manager_wl_name; struct zwlr_foreign_toplevel_manager_v1 *manager; @@ -110,7 +111,8 @@ content(struct module *mod) const char *current_output = mod->bar->output_name(mod->bar); - tll_foreach(m->toplevels, it) { + tll_foreach(m->toplevels, it) + { const struct toplevel *top = &it->item; bool show = false; @@ -118,11 +120,10 @@ content(struct module *mod) if (m->all_monitors) show = true; else if (current_output != NULL) { - tll_foreach(top->outputs, it2) { + tll_foreach(top->outputs, it2) + { const struct output *output = it2->item; - if (output->name != NULL && - strcmp(output->name, current_output) == 0) - { + if (output->name != NULL && strcmp(output->name, current_output) == 0) { show = true; break; } @@ -158,22 +159,18 @@ verify_iface_version(const char *iface, uint32_t version, uint32_t wanted) if (version >= wanted) return true; - LOG_ERR("%s: need interface version %u, but compositor only implements %u", - iface, wanted, version); + LOG_ERR("%s: need interface version %u, but compositor only implements %u", iface, wanted, version); return false; } static void -xdg_output_handle_logical_position(void *data, - struct zxdg_output_v1 *xdg_output, - int32_t x, int32_t y) +xdg_output_handle_logical_position(void *data, struct zxdg_output_v1 *xdg_output, int32_t x, int32_t y) { } static void -xdg_output_handle_logical_size(void *data, struct zxdg_output_v1 *xdg_output, - int32_t width, int32_t height) +xdg_output_handle_logical_size(void *data, struct zxdg_output_v1 *xdg_output, int32_t width, int32_t height) { } @@ -183,8 +180,7 @@ xdg_output_handle_done(void *data, struct zxdg_output_v1 *xdg_output) } static void -xdg_output_handle_name(void *data, struct zxdg_output_v1 *xdg_output, - const char *name) +xdg_output_handle_name(void *data, struct zxdg_output_v1 *xdg_output, const char *name) { struct output *output = data; struct module *mod = output->mod; @@ -198,8 +194,7 @@ xdg_output_handle_name(void *data, struct zxdg_output_v1 *xdg_output, } static void -xdg_output_handle_description(void *data, struct zxdg_output_v1 *xdg_output, - const char *description) +xdg_output_handle_description(void *data, struct zxdg_output_v1 *xdg_output, const char *description) { } @@ -238,8 +233,7 @@ app_id(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, const char *a } static void -output_enter(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, - struct wl_output *wl_output) +output_enter(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, struct wl_output *wl_output) { struct toplevel *top = data; struct module *mod = top->mod; @@ -248,7 +242,8 @@ output_enter(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, mtx_lock(&mod->lock); const struct output *output = NULL; - tll_foreach(m->outputs, it) { + tll_foreach(m->outputs, it) + { if (it->item.wl_output == wl_output) { output = &it->item; break; @@ -260,7 +255,8 @@ output_enter(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, goto out; } - tll_foreach(top->outputs, it) { + tll_foreach(top->outputs, it) + { if (it->item == output) { LOG_ERR("output-enter event on output we're already on"); goto out; @@ -275,8 +271,7 @@ out: } static void -output_leave(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, - struct wl_output *wl_output) +output_leave(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, struct wl_output *wl_output) { struct toplevel *top = data; struct module *mod = top->mod; @@ -285,7 +280,8 @@ output_leave(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, mtx_lock(&mod->lock); const struct output *output = NULL; - tll_foreach(m->outputs, it) { + tll_foreach(m->outputs, it) + { if (it->item.wl_output == wl_output) { output = &it->item; break; @@ -298,10 +294,10 @@ output_leave(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, } bool output_removed = false; - tll_foreach(top->outputs, it) { + tll_foreach(top->outputs, it) + { if (it->item == output) { - LOG_DBG("unmapped: %s:%s from %s", - top->app_id, top->title, output->name); + LOG_DBG("unmapped: %s:%s from %s", top->app_id, top->title, output->name); tll_remove(top->outputs, it); output_removed = true; break; @@ -318,8 +314,7 @@ out: } static void -state(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, - struct wl_array *states) +state(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, struct wl_array *states) { struct toplevel *top = data; @@ -329,12 +324,21 @@ state(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, bool fullscreen = false; enum zwlr_foreign_toplevel_handle_v1_state *state; - wl_array_for_each(state, states) { + wl_array_for_each(state, states) + { switch (*state) { - case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED: maximized = true; break; - case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MINIMIZED: minimized = true; break; - case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED: activated = true; break; - case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN: fullscreen = true; break; + case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED: + maximized = true; + break; + case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MINIMIZED: + minimized = true; + break; + case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED: + activated = true; + break; + case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN: + fullscreen = true; + break; } } @@ -364,7 +368,8 @@ closed(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle) struct private *m = mod->private; mtx_lock(&mod->lock); - tll_foreach(m->toplevels, it) { + tll_foreach(m->toplevels, it) + { if (it->item.handle == handle) { toplevel_free(top); tll_remove(m->toplevels, it); @@ -378,9 +383,7 @@ closed(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle) } static void -parent(void *data, - struct zwlr_foreign_toplevel_handle_v1 *handle, - struct zwlr_foreign_toplevel_handle_v1 *parent) +parent(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, struct zwlr_foreign_toplevel_handle_v1 *parent) { } @@ -396,9 +399,7 @@ static const struct zwlr_foreign_toplevel_handle_v1_listener toplevel_listener = }; static void -toplevel(void *data, - struct zwlr_foreign_toplevel_manager_v1 *manager, - struct zwlr_foreign_toplevel_handle_v1 *handle) +toplevel(void *data, struct zwlr_foreign_toplevel_manager_v1 *manager, struct zwlr_foreign_toplevel_handle_v1 *handle) { struct module *mod = data; struct private *m = mod->private; @@ -412,15 +413,13 @@ toplevel(void *data, { tll_push_back(m->toplevels, toplevel); - zwlr_foreign_toplevel_handle_v1_add_listener( - handle, &toplevel_listener, &tll_back(m->toplevels)); + zwlr_foreign_toplevel_handle_v1_add_listener(handle, &toplevel_listener, &tll_back(m->toplevels)); } mtx_unlock(&mod->lock); } static void -finished(void *data, - struct zwlr_foreign_toplevel_manager_v1 *manager) +finished(void *data, struct zwlr_foreign_toplevel_manager_v1 *manager) { struct module *mod = data; struct private *m = mod->private; @@ -445,15 +444,12 @@ output_xdg_output(struct output *output) if (output->xdg_output != NULL) return; - output->xdg_output = zxdg_output_manager_v1_get_xdg_output( - m->xdg_output_manager, output->wl_output); - zxdg_output_v1_add_listener( - output->xdg_output, &xdg_output_listener, output); + output->xdg_output = zxdg_output_manager_v1_get_xdg_output(m->xdg_output_manager, output->wl_output); + zxdg_output_v1_add_listener(output->xdg_output, &xdg_output_listener, output); } static void -handle_global(void *data, struct wl_registry *registry, - uint32_t name, const char *interface, uint32_t version) +handle_global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { struct module *mod = data; struct private *m = mod->private; @@ -473,8 +469,7 @@ handle_global(void *data, struct wl_registry *registry, struct output output = { .mod = mod, .wl_name = name, - .wl_output = wl_registry_bind( - registry, name, &wl_output_interface, required), + .wl_output = wl_registry_bind(registry, name, &wl_output_interface, required), }; mtx_lock(&mod->lock); @@ -488,12 +483,10 @@ handle_global(void *data, struct wl_registry *registry, if (!verify_iface_version(interface, version, required)) return; - m->xdg_output_manager = wl_registry_bind( - registry, name, &zxdg_output_manager_v1_interface, required); + m->xdg_output_manager = wl_registry_bind(registry, name, &zxdg_output_manager_v1_interface, required); mtx_lock(&mod->lock); - tll_foreach(m->outputs, it) - output_xdg_output(&it->item); + tll_foreach(m->outputs, it) output_xdg_output(&it->item); mtx_unlock(&mod->lock); } } @@ -506,16 +499,19 @@ handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) mtx_lock(&mod->lock); - tll_foreach(m->outputs, it) { + tll_foreach(m->outputs, it) + { const struct output *output = &it->item; if (output->wl_name == name) { /* Loop all toplevels */ - tll_foreach(m->toplevels, it2) { + tll_foreach(m->toplevels, it2) + { /* And remove this output from their list of tracked * outputs */ - tll_foreach(it2->item.outputs, it3) { + tll_foreach(it2->item.outputs, it3) + { if (it3->item == output) { tll_remove(it2->item.outputs, it3); break; @@ -551,9 +547,8 @@ run(struct module *mod) goto out; } - if ((registry = wl_display_get_registry(display)) == NULL || - wl_registry_add_listener(registry, ®istry_listener, mod) != 0) - { + if ((registry = wl_display_get_registry(display)) == NULL + || wl_registry_add_listener(registry, ®istry_listener, mod) != 0) { LOG_ERR("failed to get Wayland registry"); goto out; } @@ -561,18 +556,14 @@ run(struct module *mod) wl_display_roundtrip(display); if (m->manager_wl_name == 0) { - LOG_ERR( - "compositor does not implement the foreign-toplevel-manager interface"); + LOG_ERR("compositor does not implement the foreign-toplevel-manager interface"); goto out; } - m->manager = wl_registry_bind( - registry, m->manager_wl_name, - &zwlr_foreign_toplevel_manager_v1_interface, - required_manager_interface_version); + m->manager = wl_registry_bind(registry, m->manager_wl_name, &zwlr_foreign_toplevel_manager_v1_interface, + required_manager_interface_version); - zwlr_foreign_toplevel_manager_v1_add_listener( - m->manager, &manager_listener, mod); + zwlr_foreign_toplevel_manager_v1_add_listener(m->manager, &manager_listener, mod); while (true) { wl_display_flush(display); @@ -606,12 +597,14 @@ run(struct module *mod) } out: - tll_foreach(m->toplevels, it) { + tll_foreach(m->toplevels, it) + { toplevel_free(&it->item); tll_remove(m->toplevels, it); } - tll_foreach(m->outputs, it) { + tll_foreach(m->outputs, it) + { output_free(&it->item); tll_remove(m->outputs, it); } @@ -649,9 +642,7 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) const struct yml_node *c = yml_get_value(node, "content"); const struct yml_node *all_monitors = yml_get_value(node, "all-monitors"); - return ftop_new( - conf_to_particle(c, inherited), - all_monitors != NULL ? yml_value_as_bool(all_monitors) : false); + return ftop_new(conf_to_particle(c, inherited), all_monitors != NULL ? yml_value_as_bool(all_monitors) : false); } static bool diff --git a/modules/i3-common.c b/modules/i3-common.c index a0769f2..957a4d2 100644 --- a/modules/i3-common.c +++ b/modules/i3-common.c @@ -1,15 +1,15 @@ #include "i3-common.h" +#include #include #include #include -#include #include #if defined(ENABLE_X11) - #include - #include +#include +#include #endif #include @@ -19,7 +19,7 @@ #include "../log.h" #if defined(ENABLE_X11) - #include "../xcb.h" +#include "../xcb.h" #endif #include "i3-ipc.h" @@ -41,14 +41,11 @@ get_socket_address_x11(struct sockaddr_un *addr) xcb_atom_t atom = get_atom(conn, "I3_SOCKET_PATH"); assert(atom != XCB_ATOM_NONE); - xcb_get_property_cookie_t cookie - = xcb_get_property_unchecked( - conn, false, screen->root, atom, - XCB_GET_PROPERTY_TYPE_ANY, 0, sizeof(addr->sun_path)); + xcb_get_property_cookie_t cookie = xcb_get_property_unchecked(conn, false, screen->root, atom, + XCB_GET_PROPERTY_TYPE_ANY, 0, sizeof(addr->sun_path)); xcb_generic_error_t *err = NULL; - xcb_get_property_reply_t *reply = - xcb_get_property_reply(conn, cookie, &err); + xcb_get_property_reply_t *reply = xcb_get_property_reply(conn, cookie, &err); bool ret = false; if (err != NULL) { @@ -102,11 +99,7 @@ bool i3_send_pkg(int sock, int cmd, char *data) { const size_t size = data != NULL ? strlen(data) : 0; - const i3_ipc_header_t hdr = { - .magic = I3_IPC_MAGIC, - .size = size, - .type = cmd - }; + const i3_ipc_header_t hdr = {.magic = I3_IPC_MAGIC, .size = size, .type = cmd}; if (write(sock, &hdr, sizeof(hdr)) != (ssize_t)sizeof(hdr)) return false; @@ -120,8 +113,7 @@ i3_send_pkg(int sock, int cmd, char *data) } bool -i3_receive_loop(int abort_fd, int sock, - const struct i3_ipc_callbacks *cbs, void *data) +i3_receive_loop(int abort_fd, int sock, const struct i3_ipc_callbacks *cbs, void *data) { /* Initial reply typically requires a couple of KB. But we often * need more later. For example, switching workspaces can result @@ -133,10 +125,7 @@ i3_receive_loop(int abort_fd, int sock, bool err = false; while (!err) { - struct pollfd fds[] = { - {.fd = abort_fd, .events = POLLIN}, - {.fd = sock, .events = POLLIN} - }; + struct pollfd fds[] = {{.fd = abort_fd, .events = POLLIN}, {.fd = sock, .events = POLLIN}}; int res = poll(fds, 2, -1); if (res <= 0) { @@ -159,13 +148,11 @@ i3_receive_loop(int abort_fd, int sock, /* Grow receive buffer, if necessary */ if (buf_idx == reply_buf_size) { - LOG_DBG("growing reply buffer: %zu -> %zu", - reply_buf_size, reply_buf_size * 2); + LOG_DBG("growing reply buffer: %zu -> %zu", reply_buf_size, reply_buf_size * 2); char *new_buf = realloc(buf, reply_buf_size * 2); if (new_buf == NULL) { - LOG_ERR("failed to grow reply buffer from %zu to %zu bytes", - reply_buf_size, reply_buf_size * 2); + LOG_ERR("failed to grow reply buffer from %zu to %zu bytes", reply_buf_size, reply_buf_size * 2); err = true; break; } @@ -188,10 +175,8 @@ i3_receive_loop(int abort_fd, int sock, while (!err && buf_idx >= sizeof(i3_ipc_header_t)) { const i3_ipc_header_t *hdr = (const i3_ipc_header_t *)buf; if (strncmp(hdr->magic, I3_IPC_MAGIC, sizeof(hdr->magic)) != 0) { - LOG_ERR( - "i3 IPC header magic mismatch: expected \"%.*s\", got \"%.*s\"", - (int)sizeof(hdr->magic), I3_IPC_MAGIC, - (int)sizeof(hdr->magic), hdr->magic); + LOG_ERR("i3 IPC header magic mismatch: expected \"%.*s\", got \"%.*s\"", (int)sizeof(hdr->magic), + I3_IPC_MAGIC, (int)sizeof(hdr->magic), hdr->magic); err = true; break; @@ -210,10 +195,10 @@ i3_receive_loop(int abort_fd, int sock, char json_str[hdr->size + 1]; memcpy(json_str, &buf[sizeof(*hdr)], hdr->size); json_str[hdr->size] = '\0'; - //printf("raw: %s\n", json_str); + // printf("raw: %s\n", json_str); LOG_DBG("raw: %s\n", json_str); - //json_tokener *tokener = json_tokener_new(); + // json_tokener *tokener = json_tokener_new(); struct json_object *json = json_tokener_parse(json_str); if (json == NULL) { LOG_ERR("failed to parse json"); @@ -262,13 +247,13 @@ i3_receive_loop(int abort_fd, int sock, break; #endif /* Sway extensions */ - case 100: /* IPC_GET_INPUTS */ + case 100: /* IPC_GET_INPUTS */ pkt_handler = cbs->reply_inputs; break; - /* - * Events - */ + /* + * Events + */ case I3_IPC_EVENT_WORKSPACE: pkt_handler = cbs->event_workspace; @@ -295,7 +280,7 @@ i3_receive_loop(int abort_fd, int sock, pkt_handler = cbs->event_tick; break; - /* Sway extensions */ + /* Sway extensions */ #define SWAY_IPC_EVENT_INPUT ((1u << 31) | 21) case SWAY_IPC_EVENT_INPUT: pkt_handler = cbs->event_input; diff --git a/modules/i3-common.h b/modules/i3-common.h index 0cbfa3e..6ba6721 100644 --- a/modules/i3-common.h +++ b/modules/i3-common.h @@ -2,8 +2,8 @@ #include -#include #include +#include #include #include @@ -43,6 +43,4 @@ struct i3_ipc_callbacks { i3_ipc_callback_t event_input; }; -bool i3_receive_loop( - int abort_fd, int sock, - const struct i3_ipc_callbacks *callbacks, void *data); +bool i3_receive_loop(int abort_fd, int sock, const struct i3_ipc_callbacks *callbacks, void *data); diff --git a/modules/i3.c b/modules/i3.c index d62db5e..73bd9d6 100644 --- a/modules/i3.c +++ b/modules/i3.c @@ -1,27 +1,27 @@ +#include #include #include -#include -#include #include +#include -#include #include +#include #include #define LOG_MODULE "i3" #define LOG_ENABLE_DBG 0 -#include "../log.h" #include "../bar/bar.h" -#include "../config.h" #include "../config-verify.h" +#include "../config.h" +#include "../log.h" #include "../particles/dynlist.h" #include "../plugin.h" -#include "i3-ipc.h" #include "i3-common.h" +#include "i3-ipc.h" -enum sort_mode {SORT_NONE, SORT_NATIVE, SORT_ASCENDING, SORT_DESCENDING}; +enum sort_mode { SORT_NONE, SORT_NATIVE, SORT_ASCENDING, SORT_DESCENDING }; struct ws_content { char *name; @@ -48,7 +48,8 @@ struct workspace { } window; }; -struct private { +struct private +{ int left_spacing; int right_spacing; @@ -105,10 +106,8 @@ workspace_from_json(const struct json_object *json, struct workspace *ws) { /* Always present */ struct json_object *id, *name, *output; - if (!json_object_object_get_ex(json, "id", &id) || - !json_object_object_get_ex(json, "name", &name) || - !json_object_object_get_ex(json, "output", &output)) - { + if (!json_object_object_get_ex(json, "id", &id) || !json_object_object_get_ex(json, "name", &name) + || !json_object_object_get_ex(json, "output", &output)) { LOG_ERR("workspace reply/event without 'name' and/or 'output' " "properties"); return false; @@ -126,14 +125,12 @@ workspace_from_json(const struct json_object *json, struct workspace *ws) const char *name_as_string = json_object_get_string(name); - const size_t node_count = focus != NULL - ? json_object_array_length(focus) - : 0; + const size_t node_count = focus != NULL ? json_object_array_length(focus) : 0; const bool is_empty = node_count == 0; int name_as_int = workspace_name_as_int(name_as_string); - *ws = (struct workspace) { + *ws = (struct workspace){ .id = json_object_get_int(id), .name = strdup(name_as_string), .name_as_int = name_as_int, @@ -152,9 +149,12 @@ workspace_from_json(const struct json_object *json, struct workspace *ws) static void workspace_free_persistent(struct workspace *ws) { - free(ws->output); ws->output = NULL; - free(ws->window.title); ws->window.title = NULL; - free(ws->window.application); ws->window.application = NULL; + free(ws->output); + ws->output = NULL; + free(ws->window.title); + ws->window.title = NULL; + free(ws->window.application); + ws->window.application = NULL; ws->id = -1; } @@ -162,13 +162,15 @@ static void workspace_free(struct workspace *ws) { workspace_free_persistent(ws); - free(ws->name); ws->name = NULL; + free(ws->name); + ws->name = NULL; } static void workspaces_free(struct private *m, bool free_persistent) { - tll_foreach(m->workspaces, it) { + tll_foreach(m->workspaces, it) + { if (free_persistent || !it->item.persistent) { workspace_free(&it->item); tll_remove(m->workspaces, it); @@ -176,7 +178,6 @@ workspaces_free(struct private *m, bool free_persistent) } } - static void workspace_add(struct private *m, struct workspace ws) { @@ -187,7 +188,8 @@ workspace_add(struct private *m, struct workspace ws) case SORT_NATIVE: if (ws.name_as_int >= 0) { - tll_foreach(m->workspaces, it) { + tll_foreach(m->workspaces, it) + { if (it->item.name_as_int < 0) continue; if (it->item.name_as_int > ws.name_as_int) { @@ -202,7 +204,8 @@ workspace_add(struct private *m, struct workspace ws) case SORT_ASCENDING: if (ws.name_as_int >= 0) { - tll_foreach(m->workspaces, it) { + tll_foreach(m->workspaces, it) + { if (it->item.name_as_int < 0) continue; if (it->item.name_as_int > ws.name_as_int) { @@ -211,10 +214,9 @@ workspace_add(struct private *m, struct workspace ws) } } } else { - tll_foreach(m->workspaces, it) { - if (strcoll(it->item.name, ws.name) > 0 || - it->item.name_as_int >= 0) - { + tll_foreach(m->workspaces, it) + { + if (strcoll(it->item.name, ws.name) > 0 || it->item.name_as_int >= 0) { tll_insert_before(m->workspaces, it, ws); return; } @@ -225,14 +227,16 @@ workspace_add(struct private *m, struct workspace ws) case SORT_DESCENDING: if (ws.name_as_int >= 0) { - tll_foreach(m->workspaces, it) { + tll_foreach(m->workspaces, it) + { if (it->item.name_as_int < ws.name_as_int) { tll_insert_before(m->workspaces, it, ws); return; } } } else { - tll_foreach(m->workspaces, it) { + tll_foreach(m->workspaces, it) + { if (it->item.name_as_int >= 0) continue; if (strcoll(it->item.name, ws.name) < 0) { @@ -249,7 +253,8 @@ workspace_add(struct private *m, struct workspace ws) static void workspace_del(struct private *m, int id) { - tll_foreach(m->workspaces, it) { + tll_foreach(m->workspaces, it) + { struct workspace *ws = &it->item; if (ws->id != id) @@ -264,7 +269,8 @@ workspace_del(struct private *m, int id) static struct workspace * workspace_lookup(struct private *m, int id) { - tll_foreach(m->workspaces, it) { + tll_foreach(m->workspaces, it) + { struct workspace *ws = &it->item; if (ws->id == id) return ws; @@ -275,7 +281,8 @@ workspace_lookup(struct private *m, int id) static struct workspace * workspace_lookup_by_name(struct private *m, const char *name) { - tll_foreach(m->workspaces, it) { + tll_foreach(m->workspaces, it) + { struct workspace *ws = &it->item; if (strcmp(ws->name, name) == 0) return ws; @@ -339,13 +346,9 @@ workspace_update_or_add(struct private *m, const struct json_object *ws_json) if (json_object_object_get_ex(ws_json, "name", &_name)) { const char *name = json_object_get_string(_name); if (name != NULL) { - struct workspace *maybe_persistent = - workspace_lookup_by_name(m, name); + struct workspace *maybe_persistent = workspace_lookup_by_name(m, name); - if (maybe_persistent != NULL && - maybe_persistent->persistent && - maybe_persistent->id < 0) - { + if (maybe_persistent != NULL && maybe_persistent->persistent && maybe_persistent->id < 0) { already_exists = maybe_persistent; } } @@ -421,10 +424,9 @@ handle_workspace_event(int sock, int type, const struct json_object *json, void bool is_reload = strcmp(change_str, "reload") == 0; struct json_object *current, *_current_id; - if ((!json_object_object_get_ex(json, "current", ¤t) || - !json_object_object_get_ex(current, "id", &_current_id)) && - !is_reload) - { + if ((!json_object_object_get_ex(json, "current", ¤t) + || !json_object_object_get_ex(current, "id", &_current_id)) + && !is_reload) { LOG_ERR("workspace event without 'current' and/or 'id' properties"); return false; } @@ -452,10 +454,8 @@ handle_workspace_event(int sock, int type, const struct json_object *json, void else if (is_focused) { struct json_object *old, *_old_id, *urgent; - if (!json_object_object_get_ex(json, "old", &old) || - !json_object_object_get_ex(old, "id", &_old_id) || - !json_object_object_get_ex(current, "urgent", &urgent)) - { + if (!json_object_object_get_ex(json, "old", &old) || !json_object_object_get_ex(old, "id", &_old_id) + || !json_object_object_get_ex(current, "urgent", &urgent)) { LOG_ERR("workspace 'focused' event without 'old', 'name' and/or 'urgent' property"); mtx_unlock(&mod->lock); return false; @@ -467,7 +467,8 @@ handle_workspace_event(int sock, int type, const struct json_object *json, void LOG_DBG("w: %s", w->name); /* Mark all workspaces on current's output invisible */ - tll_foreach(m->workspaces, it) { + tll_foreach(m->workspaces, it) + { struct workspace *ws = &it->item; if (ws->output != NULL && strcmp(ws->output, w->output) == 0) ws->visible = false; @@ -501,7 +502,8 @@ handle_workspace_event(int sock, int type, const struct json_object *json, void /* Re-add the workspace to ensure correct sorting */ struct workspace ws = *w; - tll_foreach(m->workspaces, it) { + tll_foreach(m->workspaces, it) + { if (it->item.id == current_id) { tll_remove(m->workspaces, it); break; @@ -588,7 +590,8 @@ handle_window_event(int sock, int type, const struct json_object *json, void *_m struct workspace *ws = NULL; size_t focused = 0; - tll_foreach(m->workspaces, it) { + tll_foreach(m->workspaces, it) + { if (it->item.focused) { ws = &it->item; focused++; @@ -599,10 +602,8 @@ handle_window_event(int sock, int type, const struct json_object *json, void *_m assert(ws != NULL); struct json_object *container, *id, *name; - if (!json_object_object_get_ex(json, "container", &container) || - !json_object_object_get_ex(container, "id", &id) || - !json_object_object_get_ex(container, "name", &name)) - { + if (!json_object_object_get_ex(json, "container", &container) || !json_object_object_get_ex(container, "id", &id) + || !json_object_object_get_ex(container, "name", &name)) { mtx_unlock(&mod->lock); LOG_ERR("window event without 'container' with 'id' and 'name'"); return false; @@ -645,18 +646,14 @@ handle_window_event(int sock, int type, const struct json_object *json, void *_m struct json_object *app_id; struct json_object *pid; - if (json_object_object_get_ex(container, "app_id", &app_id) && - json_object_get_string(app_id) != NULL) - { + if (json_object_object_get_ex(container, "app_id", &app_id) && json_object_get_string(app_id) != NULL) { free(ws->window.application); ws->window.application = strdup(json_object_get_string(app_id)); LOG_DBG("application: \"%s\", via 'app_id'", ws->window.application); } /* If PID has changed, update application name from /proc//comm */ - else if (json_object_object_get_ex(container, "pid", &pid) && - ws->window.pid != json_object_get_int(pid)) - { + else if (json_object_object_get_ex(container, "pid", &pid) && ws->window.pid != json_object_get_int(pid)) { ws->window.pid = json_object_get_int(pid); char path[64]; @@ -665,7 +662,8 @@ handle_window_event(int sock, int type, const struct json_object *json, void *_m int fd = open(path, O_RDONLY); if (fd == -1) { /* Application may simply have terminated */ - free(ws->window.application); ws->window.application = NULL; + free(ws->window.application); + ws->window.application = NULL; ws->window.pid = -1; m->dirty = true; @@ -837,7 +835,8 @@ content(struct module *mod) struct exposable *particles[tll_length(m->workspaces) + 1]; struct exposable *current = NULL; - tll_foreach(m->workspaces, it) { + tll_foreach(m->workspaces, it) + { struct workspace *ws = &it->item; const struct ws_content *template = NULL; @@ -853,21 +852,12 @@ content(struct module *mod) template = ws_content_for_name(m, ""); } - const char *state = - ws->urgent ? "urgent" : - ws->visible ? ws->focused ? "focused" : "unfocused" : - "invisible"; + const char *state = ws->urgent ? "urgent" : ws->visible ? ws->focused ? "focused" : "unfocused" : "invisible"; LOG_DBG("name=%s (name-as-int=%d): visible=%s, focused=%s, urgent=%s, empty=%s, state=%s, " "application=%s, title=%s, mode=%s", - ws->name, ws->name_as_int, - ws->visible ? "yes" : "no", - ws->focused ? "yes" : "no", - ws->urgent ? "yes" : "no", - ws->empty ? "yes" : "no", - state, - ws->window.application, - ws->window.title, + ws->name, ws->name_as_int, ws->visible ? "yes" : "no", ws->focused ? "yes" : "no", + ws->urgent ? "yes" : "no", ws->empty ? "yes" : "no", state, ws->window.application, ws->window.title, m->mode); const char *name = ws->name; @@ -902,12 +892,9 @@ content(struct module *mod) } if (template == NULL) { - LOG_WARN( - "no ws template for %s, and no default template available", - ws->name); + LOG_WARN("no ws template for %s, and no default template available", ws->name); } else { - particles[particle_count++] = template->content->instantiate( - template->content, &tags); + particles[particle_count++] = template->content->instantiate(template->content, &tags); } tag_set_destroy(&tags); @@ -917,8 +904,7 @@ content(struct module *mod) particles[particle_count++] = current; mtx_unlock(&mod->lock); - return dynlist_exposable_new( - particles, particle_count, m->left_spacing, m->right_spacing); + return dynlist_exposable_new(particles, particle_count, m->left_spacing, m->right_spacing); } /* Maps workspace name to a content particle. */ @@ -928,10 +914,8 @@ struct i3_workspaces { }; static struct module * -i3_new(struct i3_workspaces workspaces[], size_t workspace_count, - int left_spacing, int right_spacing, enum sort_mode sort_mode, - size_t persistent_count, - const char *persistent_workspaces[static persistent_count], +i3_new(struct i3_workspaces workspaces[], size_t workspace_count, int left_spacing, int right_spacing, + enum sort_mode sort_mode, size_t persistent_count, const char *persistent_workspaces[static persistent_count], bool strip_workspace_numbers) { struct private *m = calloc(1, sizeof(*m)); @@ -952,8 +936,7 @@ i3_new(struct i3_workspaces workspaces[], size_t workspace_count, m->sort_mode = sort_mode; m->persistent_count = persistent_count; - m->persistent_workspaces = calloc( - persistent_count, sizeof(m->persistent_workspaces[0])); + m->persistent_workspaces = calloc(persistent_count, sizeof(m->persistent_workspaces[0])); for (size_t i = 0; i < persistent_count; i++) m->persistent_workspaces[i] = strdup(persistent_workspaces[i]); @@ -976,31 +959,26 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) const struct yml_node *right_spacing = yml_get_value(node, "right-spacing"); const struct yml_node *sort = yml_get_value(node, "sort"); const struct yml_node *persistent = yml_get_value(node, "persistent"); - const struct yml_node *strip_workspace_number = yml_get_value( - node, "strip-workspace-numbers"); + const struct yml_node *strip_workspace_number = yml_get_value(node, "strip-workspace-numbers"); - int left = spacing != NULL ? yml_value_as_int(spacing) : - left_spacing != NULL ? yml_value_as_int(left_spacing) : 0; - int right = spacing != NULL ? yml_value_as_int(spacing) : - right_spacing != NULL ? yml_value_as_int(right_spacing) : 0; + int left = spacing != NULL ? yml_value_as_int(spacing) : left_spacing != NULL ? yml_value_as_int(left_spacing) : 0; + int right = spacing != NULL ? yml_value_as_int(spacing) + : right_spacing != NULL ? yml_value_as_int(right_spacing) + : 0; const char *sort_value = sort != NULL ? yml_value_as_string(sort) : NULL; - enum sort_mode sort_mode = - sort_value == NULL ? SORT_NONE : - strcmp(sort_value, "none") == 0 ? SORT_NONE : - strcmp(sort_value, "native") == 0 ? SORT_NATIVE : - strcmp(sort_value, "ascending") == 0 ? SORT_ASCENDING : SORT_DESCENDING; + enum sort_mode sort_mode = sort_value == NULL ? SORT_NONE + : strcmp(sort_value, "none") == 0 ? SORT_NONE + : strcmp(sort_value, "native") == 0 ? SORT_NATIVE + : strcmp(sort_value, "ascending") == 0 ? SORT_ASCENDING + : SORT_DESCENDING; - const size_t persistent_count = - persistent != NULL ? yml_list_length(persistent) : 0; + const size_t persistent_count = persistent != NULL ? yml_list_length(persistent) : 0; const char *persistent_workspaces[persistent_count]; if (persistent != NULL) { size_t idx = 0; - for (struct yml_list_iter it = yml_list_iter(persistent); - it.node != NULL; - yml_list_next(&it), idx++) - { + for (struct yml_list_iter it = yml_list_iter(persistent); it.node != NULL; yml_list_next(&it), idx++) { persistent_workspaces[idx] = yml_value_as_string(it.node); } } @@ -1008,38 +986,27 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) struct i3_workspaces workspaces[yml_dict_length(c)]; size_t idx = 0; - for (struct yml_dict_iter it = yml_dict_iter(c); - it.key != NULL; - yml_dict_next(&it), idx++) - { + for (struct yml_dict_iter it = yml_dict_iter(c); it.key != NULL; yml_dict_next(&it), idx++) { workspaces[idx].name = yml_value_as_string(it.key); workspaces[idx].content = conf_to_particle(it.value, inherited); } - return i3_new(workspaces, yml_dict_length(c), left, right, sort_mode, - persistent_count, persistent_workspaces, - (strip_workspace_number != NULL - ? yml_value_as_bool(strip_workspace_number) : false)); + return i3_new(workspaces, yml_dict_length(c), left, right, sort_mode, persistent_count, persistent_workspaces, + (strip_workspace_number != NULL ? yml_value_as_bool(strip_workspace_number) : false)); } static bool verify_content(keychain_t *chain, const struct yml_node *node) { if (!yml_is_dict(node)) { - LOG_ERR( - "%s: must be a dictionary of workspace-name: particle mappings", - conf_err_prefix(chain, node)); + LOG_ERR("%s: must be a dictionary of workspace-name: particle mappings", conf_err_prefix(chain, node)); return false; } - for (struct yml_dict_iter it = yml_dict_iter(node); - it.key != NULL; - yml_dict_next(&it)) - { + for (struct yml_dict_iter it = yml_dict_iter(node); it.key != NULL; yml_dict_next(&it)) { const char *key = yml_value_as_string(it.key); if (key == NULL) { - LOG_ERR("%s: key must be a string (a i3 workspace name)", - conf_err_prefix(chain, it.key)); + LOG_ERR("%s: key must be a string (a i3 workspace name)", conf_err_prefix(chain, it.key)); return false; } @@ -1055,8 +1022,7 @@ verify_content(keychain_t *chain, const struct yml_node *node) static bool verify_sort(keychain_t *chain, const struct yml_node *node) { - return conf_verify_enum( - chain, node, (const char *[]){"none", "native", "ascending", "descending"}, 4); + return conf_verify_enum(chain, node, (const char *[]){"none", "native", "ascending", "descending"}, 4); } static bool @@ -1089,5 +1055,5 @@ const struct module_iface module_i3_iface = { }; #if defined(CORE_PLUGINS_AS_SHARED_LIBRARIES) -extern const struct module_iface iface __attribute__((weak, alias("module_i3_iface"))) ; +extern const struct module_iface iface __attribute__((weak, alias("module_i3_iface"))); #endif diff --git a/modules/label.c b/modules/label.c index 7e8ee09..5f1f158 100644 --- a/modules/label.c +++ b/modules/label.c @@ -1,16 +1,14 @@ -#include #include +#include #include -#include "../config.h" #include "../config-verify.h" +#include "../config.h" #include "../module.h" #include "../plugin.h" -struct private { - struct particle *label; -}; +struct private { struct particle *label; }; static void destroy(struct module *mod) diff --git a/modules/mem.c b/modules/mem.c index 6a196c6..dc9bcf8 100644 --- a/modules/mem.c +++ b/modules/mem.c @@ -162,9 +162,7 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) const struct yml_node *interval = yml_get_value(node, "poll-interval"); const struct yml_node *c = yml_get_value(node, "content"); - return mem_new( - interval == NULL ? min_poll_interval : yml_value_as_int(interval), - conf_to_particle(c, inherited)); + return mem_new(interval == NULL ? min_poll_interval : yml_value_as_int(interval), conf_to_particle(c, inherited)); } static bool @@ -174,8 +172,7 @@ conf_verify_poll_interval(keychain_t *chain, const struct yml_node *node) return false; if (yml_value_as_int(node) < min_poll_interval) { - LOG_ERR("%s: interval value cannot be less than %ldms", - conf_err_prefix(chain, node), min_poll_interval); + LOG_ERR("%s: interval value cannot be less than %ldms", conf_err_prefix(chain, node), min_poll_interval); return false; } diff --git a/modules/mpd.c b/modules/mpd.c index 604bf88..22c0e6b 100644 --- a/modules/mpd.c +++ b/modules/mpd.c @@ -1,33 +1,34 @@ -#include -#include -#include -#include -#include -#include -#include #include #include +#include +#include +#include +#include +#include +#include +#include -#include #include +#include -#include -#include -#include #include #include +#include +#include +#include #include #define LOG_MODULE "mpd" #define LOG_ENABLE_DBG 0 -#include "../log.h" #include "../bar/bar.h" -#include "../config.h" #include "../config-verify.h" +#include "../config.h" +#include "../log.h" #include "../plugin.h" -struct private { +struct private +{ char *host; uint16_t port; struct particle *label; @@ -38,7 +39,7 @@ struct private { bool repeat; bool random; bool consume; - int volume; + int volume; char *album; char *artist; char *title; @@ -60,11 +61,9 @@ destroy(struct module *mod) struct private *m = mod->private; if (m->refresh_thread_id != 0) { assert(m->refresh_abort_fd != -1); - if (write(m->refresh_abort_fd, &(uint64_t){1}, sizeof(uint64_t)) - != sizeof(uint64_t)) - { + if (write(m->refresh_abort_fd, &(uint64_t){1}, sizeof(uint64_t)) != sizeof(uint64_t)) { LOG_ERRNO("failed to signal abort to refresher thread"); - } else{ + } else { int res; thrd_join(m->refresh_thread_id, &res); } @@ -132,12 +131,11 @@ content(struct module *mod) if (m->state == MPD_STATE_PLAY) { elapsed += timespec_diff_milli_seconds(&now, &m->elapsed.when); if (elapsed > m->duration) { - LOG_DBG( - "dynamic update of elapsed overflowed: " - "elapsed=%"PRIu64", duration=%"PRIu64, elapsed, m->duration); + LOG_DBG("dynamic update of elapsed overflowed: " + "elapsed=%" PRIu64 ", duration=%" PRIu64, + elapsed, m->duration); elapsed = m->duration; } - } unsigned elapsed_secs = elapsed / 1000; @@ -154,16 +152,23 @@ content(struct module *mod) state_str = "offline"; else { switch (m->state) { - case MPD_STATE_UNKNOWN: state_str = "unknown"; break; - case MPD_STATE_STOP: state_str = "stopped"; break; - case MPD_STATE_PAUSE: state_str = "paused"; break; - case MPD_STATE_PLAY: state_str = "playing"; break; + case MPD_STATE_UNKNOWN: + state_str = "unknown"; + break; + case MPD_STATE_STOP: + state_str = "stopped"; + break; + case MPD_STATE_PAUSE: + state_str = "paused"; + break; + case MPD_STATE_PLAY: + state_str = "playing"; + break; } } /* Tell particle to real-time track? */ - enum tag_realtime_unit realtime = m->state == MPD_STATE_PLAY - ? TAG_REALTIME_MSECS : TAG_REALTIME_NONE; + enum tag_realtime_unit realtime = m->state == MPD_STATE_PLAY ? TAG_REALTIME_MSECS : TAG_REALTIME_NONE; struct tag_set tags = { .tags = (struct tag *[]){ @@ -237,8 +242,7 @@ wait_for_socket_create(const struct module *mod) LOG_DBG("%s: already exists, and is connectable", m->host); have_mpd_socket = true; } else { - LOG_DBG("%s: already exists, but isn't connectable: %s", - m->host, strerror(errno)); + LOG_DBG("%s: already exists, but isn't connectable: %s", m->host, strerror(errno)); } close(s); @@ -249,10 +253,7 @@ wait_for_socket_create(const struct module *mod) bool ret = false; while (!have_mpd_socket) { - struct pollfd fds[] = { - {.fd = mod->abort_fd, .events = POLLIN}, - {.fd = fd, .events = POLLIN} - }; + struct pollfd fds[] = {{.fd = mod->abort_fd, .events = POLLIN}, {.fd = fd, .events = POLLIN}}; if (poll(fds, sizeof(fds) / sizeof(fds[0]), -1) < 0) { if (errno == EINTR) @@ -272,7 +273,7 @@ wait_for_socket_create(const struct module *mod) char buf[1024]; ssize_t len = read(fd, buf, sizeof(buf)); - for (const char *ptr = buf; ptr < buf + len; ) { + for (const char *ptr = buf; ptr < buf + len;) { const struct inotify_event *e = (const struct inotify_event *)ptr; LOG_DBG("inotify: CREATED: %s/%.*s", directory, e->len, e->name); @@ -282,7 +283,7 @@ wait_for_socket_create(const struct module *mod) break; } - ptr += sizeof(*e) + e->len; + ptr += sizeof(*e) + e->len; } } @@ -305,8 +306,7 @@ connect_to_mpd(const struct module *mod) enum mpd_error merr = mpd_connection_get_error(conn); if (merr != MPD_ERROR_SUCCESS) { - LOG_WARN("failed to connect to MPD: %s", - mpd_connection_get_error_message(conn)); + LOG_WARN("failed to connect to MPD: %s", mpd_connection_get_error_message(conn)); mpd_connection_free(conn); return NULL; } @@ -324,8 +324,7 @@ update_status(struct module *mod) struct mpd_status *status = mpd_run_status(m->conn); if (status == NULL) { - LOG_ERR("failed to get status: %s", - mpd_connection_get_error_message(m->conn)); + LOG_ERR("failed to get status: %s", mpd_connection_get_error_message(m->conn)); return false; } @@ -347,17 +346,20 @@ update_status(struct module *mod) struct mpd_song *song = mpd_run_current_song(m->conn); if (song == NULL && mpd_connection_get_error(m->conn) != MPD_ERROR_SUCCESS) { - LOG_ERR("failed to get current song: %s", - mpd_connection_get_error_message(m->conn)); + LOG_ERR("failed to get current song: %s", mpd_connection_get_error_message(m->conn)); return false; } if (song == NULL) { mtx_lock(&mod->lock); - free(m->album); m->album = NULL; - free(m->artist); m->artist = NULL; - free(m->title); m->title = NULL; - free(m->file); m->file = NULL; + free(m->album); + m->album = NULL; + free(m->artist); + m->artist = NULL; + free(m->title); + m->title = NULL; + free(m->file); + m->file = NULL; mtx_unlock(&mod->lock); } else { const char *album = mpd_song_get_tag(song, MPD_TAG_ALBUM, 0); @@ -401,10 +403,14 @@ run(struct module *mod) /* Reset state */ mtx_lock(&mod->lock); - free(m->album); m->album = NULL; - free(m->artist); m->artist = NULL; - free(m->title); m->title = NULL; - free(m->file); m->file = NULL; + free(m->album); + m->album = NULL; + free(m->artist); + m->artist = NULL; + free(m->title); + m->title = NULL; + free(m->file); + m->file = NULL; m->state = MPD_STATE_UNKNOWN; m->elapsed.value = m->duration = 0; m->elapsed.when.tv_sec = m->elapsed.when.tv_nsec = 0; @@ -467,8 +473,7 @@ run(struct module *mod) }; if (!mpd_send_idle(m->conn)) { - LOG_ERR("failed to send IDLE command: %s", - mpd_connection_get_error_message(m->conn)); + LOG_ERR("failed to send IDLE command: %s", mpd_connection_get_error_message(m->conn)); break; } @@ -492,8 +497,7 @@ run(struct module *mod) } if (fds[1].revents & POLLIN) { - enum mpd_idle idle __attribute__ ((unused)) = - mpd_recv_idle(m->conn, true); + enum mpd_idle idle __attribute__((unused)) = mpd_recv_idle(m->conn, true); LOG_DBG("IDLE mask: %d", idle); @@ -565,9 +569,7 @@ refresh_in(struct module *mod, long milli_seconds) /* Signal abort to thread */ assert(m->refresh_abort_fd != -1); - if (write(m->refresh_abort_fd, &(uint64_t){1}, sizeof(uint64_t)) - != sizeof(uint64_t)) - { + if (write(m->refresh_abort_fd, &(uint64_t){1}, sizeof(uint64_t)) != sizeof(uint64_t)) { LOG_ERRNO("failed to signal abort to refresher thread"); return false; } @@ -607,7 +609,7 @@ refresh_in(struct module *mod, long milli_seconds) } /* Detach - we don't want to have to thrd_join() it */ - //thrd_detach(tid); + // thrd_detach(tid); return r == 0; } @@ -638,10 +640,8 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) const struct yml_node *port = yml_get_value(node, "port"); const struct yml_node *c = yml_get_value(node, "content"); - return mpd_new( - yml_value_as_string(host), - port != NULL ? yml_value_as_int(port) : 0, - conf_to_particle(c, inherited)); + return mpd_new(yml_value_as_string(host), port != NULL ? yml_value_as_int(port) : 0, + conf_to_particle(c, inherited)); } static bool diff --git a/modules/network.c b/modules/network.c index 24e811f..4bb0fb2 100644 --- a/modules/network.c +++ b/modules/network.c @@ -1,34 +1,34 @@ +#include +#include +#include #include #include -#include #include #include -#include #include -#include -#include #include #include +#include -#include #include +#include #include +#include #include #include -#include -#include #include +#include #include #define LOG_MODULE "network" #define LOG_ENABLE_DBG 0 -#include "../log.h" #include "../bar/bar.h" -#include "../config.h" #include "../config-verify.h" +#include "../config.h" +#include "../log.h" #include "../module.h" #include "../particles/dynlist.h" #include "../plugin.h" @@ -58,7 +58,7 @@ struct iface { int index; uint8_t mac[6]; bool carrier; - uint8_t state; /* IFLA_OPERSTATE */ + uint8_t state; /* IFLA_OPERSTATE */ /* IPv4 and IPv6 addresses */ tll(struct af_addr) addrs; @@ -76,7 +76,8 @@ struct iface { uint64_t dl_bits; }; -struct private { +struct private +{ struct particle *label; int poll_interval; @@ -118,8 +119,7 @@ destroy(struct module *mod) if (m->urandom_fd >= 0) close(m->urandom_fd); - tll_foreach(m->ifaces, it) - free_iface(it->item); + tll_foreach(m->ifaces, it) free_iface(it->item); free(m); module_default_destroy(mod); @@ -141,35 +141,53 @@ content(struct module *mod) struct exposable *exposables[max(tll_length(m->ifaces), 1)]; size_t idx = 0; - tll_foreach(m->ifaces, it) { + tll_foreach(m->ifaces, it) + { struct iface *iface = &it->item; const char *state = NULL; switch (iface->state) { - case IF_OPER_UNKNOWN: state = "unknown"; break; - case IF_OPER_NOTPRESENT: state = "not present"; break; - case IF_OPER_DOWN: state = "down"; break; - case IF_OPER_LOWERLAYERDOWN: state = "lower layers down"; break; - case IF_OPER_TESTING: state = "testing"; break; - case IF_OPER_DORMANT: state = "dormant"; break; - case IF_OPER_UP: state = "up"; break; - default: state = "unknown"; break; + case IF_OPER_UNKNOWN: + state = "unknown"; + break; + case IF_OPER_NOTPRESENT: + state = "not present"; + break; + case IF_OPER_DOWN: + state = "down"; + break; + case IF_OPER_LOWERLAYERDOWN: + state = "lower layers down"; + break; + case IF_OPER_TESTING: + state = "testing"; + break; + case IF_OPER_DORMANT: + state = "dormant"; + break; + case IF_OPER_UP: + state = "up"; + break; + default: + state = "unknown"; + break; } char mac_str[6 * 2 + 5 + 1]; char ipv4_str[INET_ADDRSTRLEN] = {0}; char ipv6_str[INET6_ADDRSTRLEN] = {0}; - snprintf(mac_str, sizeof(mac_str), "%02x:%02x:%02x:%02x:%02x:%02x", - iface->mac[0], iface->mac[1], iface->mac[2], iface->mac[3], iface->mac[4], iface->mac[5]); + snprintf(mac_str, sizeof(mac_str), "%02x:%02x:%02x:%02x:%02x:%02x", iface->mac[0], iface->mac[1], iface->mac[2], + iface->mac[3], iface->mac[4], iface->mac[5]); /* TODO: this exposes the *last* added address of each kind. Can * we expose all in some way? */ - tll_foreach(iface->addrs, it) { + tll_foreach(iface->addrs, it) + { if (it->item.family == AF_INET) inet_ntop(AF_INET, &it->item.addr.ipv4, ipv4_str, sizeof(ipv4_str)); else if (it->item.family == AF_INET6) - if(!IN6_IS_ADDR_LINKLOCAL(&it->item.addr.ipv6)) + if (!IN6_IS_ADDR_LINKLOCAL(&it->item.addr.ipv6)) inet_ntop(AF_INET6, &it->item.addr.ipv6, ipv6_str, sizeof(ipv6_str)); } @@ -208,8 +226,7 @@ content(struct module *mod) mtx_unlock(&mod->lock); - return dynlist_exposable_new( - exposables, idx, m->left_spacing, m->right_spacing); + return dynlist_exposable_new(exposables, idx, m->left_spacing, m->right_spacing); } /* Returns a value suitable for nl_pid/nlmsg_pid */ @@ -254,8 +271,7 @@ netlink_connect_genl(void) } const struct sockaddr_nl addr = { - .nl_family = AF_NETLINK, - .nl_pid = nl_pid_value(), + .nl_family = AF_NETLINK, .nl_pid = nl_pid_value(), /* no multicast notifications by default, will be added later */ }; @@ -271,10 +287,8 @@ netlink_connect_genl(void) static bool send_nlmsg(int sock, const void *nlmsg, size_t len) { - int r = sendto( - sock, nlmsg, len, 0, - (struct sockaddr *)&(struct sockaddr_nl){.nl_family = AF_NETLINK}, - sizeof(struct sockaddr_nl)); + int r = sendto(sock, nlmsg, len, 0, (struct sockaddr *)&(struct sockaddr_nl){.nl_family = AF_NETLINK}, + sizeof(struct sockaddr_nl)); return r == len; } @@ -300,8 +314,7 @@ send_rt_request(struct private *m, int request) }; if (!send_nlmsg(m->rt_sock, &req, req.hdr.nlmsg_len)) { - LOG_ERRNO("failed to send netlink RT request (%d)", - request); + LOG_ERRNO("failed to send netlink RT request (%d)", request); return false; } @@ -312,8 +325,7 @@ static bool send_rt_getstats_request(struct private *m, struct iface *iface) { if (iface->get_stats_seq_nr > 0) { - LOG_DBG( - "%s: RT get-stats request already in progress", iface->name); + LOG_DBG("%s: RT get-stats request already in progress", iface->name); return true; } @@ -345,8 +357,7 @@ send_rt_getstats_request(struct private *m, struct iface *iface) }; if (!send_nlmsg(m->rt_sock, &req, req.hdr.nlmsg_len)) { - LOG_ERRNO("%s: failed to send netlink RT getstats request (%d)", - iface->name, RTM_GETSTATS); + LOG_ERRNO("%s: failed to send netlink RT getstats request (%d)", iface->name, RTM_GETSTATS); return false; } iface->get_stats_seq_nr = seq; @@ -391,10 +402,8 @@ send_ctrl_get_family_request(struct private *m) }, }; - _Static_assert( - sizeof(req.msg.family_name_attr) == - NLA_HDRLEN + NLA_ALIGN(sizeof(req.msg.family_name_attr.data)), - ""); + _Static_assert(sizeof(req.msg.family_name_attr) == NLA_HDRLEN + NLA_ALIGN(sizeof(req.msg.family_name_attr.data)), + ""); if (!send_nlmsg(m->genl_sock, &req, req.hdr.nlmsg_len)) { LOG_ERRNO("failed to send netlink ctrl-get-family request"); @@ -538,15 +547,15 @@ send_nl80211_get_scan(struct private *m) } static void -handle_link(struct module *mod, uint16_t type, - const struct ifinfomsg *msg, size_t len) +handle_link(struct module *mod, uint16_t type, const struct ifinfomsg *msg, size_t len) { assert(type == RTM_NEWLINK || type == RTM_DELLINK); struct private *m = mod->private; if (type == RTM_DELLINK) { - tll_foreach(m->ifaces, it) { + tll_foreach(m->ifaces, it) + { if (msg->ifi_index != it->item.index) continue; mtx_lock(&mod->lock); @@ -560,7 +569,8 @@ handle_link(struct module *mod, uint16_t type, } struct iface *iface = NULL; - tll_foreach(m->ifaces, it) { + tll_foreach(m->ifaces, it) + { if (msg->ifi_index != it->item.index) continue; iface = &it->item; @@ -568,21 +578,17 @@ handle_link(struct module *mod, uint16_t type, } if (iface == NULL) { - mtx_lock(&mod->lock); - tll_push_back(m->ifaces, - ((struct iface){ - .index = msg->ifi_index, - .state = IF_OPER_DOWN, - .addrs = tll_init(), - })); - mtx_unlock(&mod->lock); - iface = &tll_back(m->ifaces); + mtx_lock(&mod->lock); + tll_push_back(m->ifaces, ((struct iface){ + .index = msg->ifi_index, + .state = IF_OPER_DOWN, + .addrs = tll_init(), + })); + mtx_unlock(&mod->lock); + iface = &tll_back(m->ifaces); } - for (const struct rtattr *attr = IFLA_RTA(msg); - RTA_OK(attr, len); - attr = RTA_NEXT(attr, len)) - { + for (const struct rtattr *attr = IFLA_RTA(msg); RTA_OK(attr, len); attr = RTA_NEXT(attr, len)) { switch (attr->rta_type) { case IFLA_IFNAME: mtx_lock(&mod->lock); @@ -623,9 +629,8 @@ handle_link(struct module *mod, uint16_t type, if (memcmp(iface->mac, mac, sizeof(iface->mac)) == 0) break; - LOG_DBG("%s: IFLA_ADDRESS: %02x:%02x:%02x:%02x:%02x:%02x", - iface->name, - mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + LOG_DBG("%s: IFLA_ADDRESS: %02x:%02x:%02x:%02x:%02x:%02x", iface->name, mac[0], mac[1], mac[2], mac[3], + mac[4], mac[5]); mtx_lock(&mod->lock); memcpy(iface->mac, mac, sizeof(iface->mac)); @@ -645,8 +650,7 @@ handle_link(struct module *mod, uint16_t type, } static void -handle_address(struct module *mod, uint16_t type, - const struct ifaddrmsg *msg, size_t len) +handle_address(struct module *mod, uint16_t type, const struct ifaddrmsg *msg, size_t len) { assert(type == RTM_NEWADDR || type == RTM_DELADDR); @@ -656,7 +660,8 @@ handle_address(struct module *mod, uint16_t type, struct iface *iface = NULL; - tll_foreach(m->ifaces, it) { + tll_foreach(m->ifaces, it) + { if (msg->ifa_index != it->item.index) continue; iface = &it->item; @@ -668,10 +673,7 @@ handle_address(struct module *mod, uint16_t type, return; } - for (const struct rtattr *attr = IFA_RTA(msg); - RTA_OK(attr, len); - attr = RTA_NEXT(attr, len)) - { + for (const struct rtattr *attr = IFA_RTA(msg); RTA_OK(attr, len); attr = RTA_NEXT(attr, len)) { switch (attr->rta_type) { case IFA_ADDRESS: { const void *raw_addr = RTA_DATA(attr); @@ -681,14 +683,14 @@ handle_address(struct module *mod, uint16_t type, char s[INET6_ADDRSTRLEN]; inet_ntop(msg->ifa_family, raw_addr, s, sizeof(s)); #endif - LOG_DBG("%s: IFA_ADDRESS (%s): %s", iface->name, - type == RTM_NEWADDR ? "add" : "del", s); + LOG_DBG("%s: IFA_ADDRESS (%s): %s", iface->name, type == RTM_NEWADDR ? "add" : "del", s); mtx_lock(&mod->lock); if (type == RTM_DELADDR) { /* Find address in our list and remove it */ - tll_foreach(iface->addrs, it) { + tll_foreach(iface->addrs, it) + { if (it->item.family != msg->ifa_family) continue; @@ -719,19 +721,18 @@ handle_address(struct module *mod, uint16_t type, static bool foreach_nlattr(struct module *mod, struct iface *iface, const struct genlmsghdr *genl, size_t len, - bool (*cb)(struct module *mod, struct iface *iface, uint16_t type, bool nested, - const void *payload, size_t len, void *ctx), + bool (*cb)(struct module *mod, struct iface *iface, uint16_t type, bool nested, const void *payload, + size_t len, void *ctx), void *ctx) { const uint8_t *raw = (const uint8_t *)genl + GENL_HDRLEN; const uint8_t *end = (const uint8_t *)genl + len; - for (const struct nlattr *attr = (const struct nlattr *)raw; - raw < end; - raw += NLA_ALIGN(attr->nla_len), attr = (const struct nlattr *)raw) - { + for (const struct nlattr *attr = (const struct nlattr *)raw; raw < end; + raw += NLA_ALIGN(attr->nla_len), attr = (const struct nlattr *)raw) { uint16_t type = attr->nla_type & NLA_TYPE_MASK; - bool nested = (attr->nla_type & NLA_F_NESTED) != 0;; + bool nested = (attr->nla_type & NLA_F_NESTED) != 0; + ; const void *payload = raw + NLA_HDRLEN; if (!cb(mod, iface, type, nested, payload, attr->nla_len - NLA_HDRLEN, ctx)) @@ -743,18 +744,15 @@ foreach_nlattr(struct module *mod, struct iface *iface, const struct genlmsghdr static bool foreach_nlattr_nested(struct module *mod, struct iface *iface, const void *parent_payload, size_t len, - bool (*cb)(struct module *mod, struct iface *iface, uint16_t type, - bool nested, const void *payload, size_t len, - void *ctx), + bool (*cb)(struct module *mod, struct iface *iface, uint16_t type, bool nested, + const void *payload, size_t len, void *ctx), void *ctx) { const uint8_t *raw = parent_payload; const uint8_t *end = parent_payload + len; - for (const struct nlattr *attr = (const struct nlattr *)raw; - raw < end; - raw += NLA_ALIGN(attr->nla_len), attr = (const struct nlattr *)raw) - { + for (const struct nlattr *attr = (const struct nlattr *)raw; raw < end; + raw += NLA_ALIGN(attr->nla_len), attr = (const struct nlattr *)raw) { uint16_t type = attr->nla_type & NLA_TYPE_MASK; bool nested = (attr->nla_type & NLA_F_NESTED) != 0; const void *payload = raw + NLA_HDRLEN; @@ -772,8 +770,8 @@ struct mcast_group { }; static bool -parse_mcast_group(struct module *mod, struct iface *iface, uint16_t type, bool nested, - const void *payload, size_t len, void *_ctx) +parse_mcast_group(struct module *mod, struct iface *iface, uint16_t type, bool nested, const void *payload, size_t len, + void *_ctx) { struct mcast_group *ctx = _ctx; @@ -790,7 +788,8 @@ parse_mcast_group(struct module *mod, struct iface *iface, uint16_t type, bool n default: LOG_WARN("unrecognized GENL MCAST GRP attribute: " - "type=%hu, nested=%d, len=%zu", type, nested, len); + "type=%hu, nested=%d, len=%zu", + type, nested, len); break; } @@ -798,8 +797,8 @@ parse_mcast_group(struct module *mod, struct iface *iface, uint16_t type, bool n } static bool -parse_mcast_groups(struct module *mod, struct iface *iface, uint16_t type, bool nested, - const void *payload, size_t len, void *_ctx) +parse_mcast_groups(struct module *mod, struct iface *iface, uint16_t type, bool nested, const void *payload, size_t len, + void *_ctx) { struct private *m = mod->private; @@ -814,9 +813,7 @@ parse_mcast_groups(struct module *mod, struct iface *iface, uint16_t type, bool * CONNECT/DISCONNECT events. */ - int r = setsockopt( - m->genl_sock, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, - &group.id, sizeof(int)); + int r = setsockopt(m->genl_sock, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &group.id, sizeof(int)); if (r < 0) LOG_ERRNO("failed to joint the nl80211 MLME mcast group"); @@ -826,8 +823,8 @@ parse_mcast_groups(struct module *mod, struct iface *iface, uint16_t type, bool } static bool -handle_genl_ctrl(struct module *mod, struct iface *iface, uint16_t type, bool nested, - const void *payload, size_t len, void *_ctx) +handle_genl_ctrl(struct module *mod, struct iface *iface, uint16_t type, bool nested, const void *payload, size_t len, + void *_ctx) { struct private *m = mod->private; @@ -839,7 +836,7 @@ handle_genl_ctrl(struct module *mod, struct iface *iface, uint16_t type, bool ne } case CTRL_ATTR_FAMILY_NAME: - //LOG_INFO("NAME: %.*s (%zu bytes)", (int)len, (const char *)payload, len); + // LOG_INFO("NAME: %.*s (%zu bytes)", (int)len, (const char *)payload, len); break; case CTRL_ATTR_MCAST_GROUPS: @@ -848,17 +845,17 @@ handle_genl_ctrl(struct module *mod, struct iface *iface, uint16_t type, bool ne default: LOG_DBG("unrecognized GENL CTRL attribute: " - "type=%hu, nested=%d, len=%zu", type, nested, len); + "type=%hu, nested=%d, len=%zu", + type, nested, len); break; } return true; } - static bool -find_nl80211_iface(struct module *mod, struct iface *_iface, uint16_t type, bool nested, - const void *payload, size_t len, void *ctx) +find_nl80211_iface(struct module *mod, struct iface *_iface, uint16_t type, bool nested, const void *payload, + size_t len, void *ctx) { struct private *m = mod->private; struct iface **iface = ctx; @@ -868,7 +865,8 @@ find_nl80211_iface(struct module *mod, struct iface *_iface, uint16_t type, bool if (*iface != NULL) if (*(uint32_t *)payload == (*iface)->index) return false; - tll_foreach(m->ifaces, it) { + tll_foreach(m->ifaces, it) + { if (*(uint32_t *)payload != it->item.index) continue; *iface = &it->item; @@ -882,8 +880,8 @@ find_nl80211_iface(struct module *mod, struct iface *_iface, uint16_t type, bool } static bool -handle_nl80211_new_interface(struct module *mod, struct iface *iface, uint16_t type, bool nested, - const void *payload, size_t len, void *_ctx) +handle_nl80211_new_interface(struct module *mod, struct iface *iface, uint16_t type, bool nested, const void *payload, + size_t len, void *_ctx) { switch (type) { case NL80211_ATTR_IFINDEX: @@ -907,7 +905,8 @@ handle_nl80211_new_interface(struct module *mod, struct iface *iface, uint16_t t default: LOG_DBG("%s: unrecognized nl80211 attribute: " - "type=%hu, nested=%d, len=%zu", iface->name, type, nested, len); + "type=%hu, nested=%d, len=%zu", + iface->name, type, nested, len); break; } @@ -919,8 +918,8 @@ struct rate_info_ctx { }; static bool -handle_nl80211_rate_info(struct module *mod, struct iface *iface, uint16_t type, bool nested, - const void *payload, size_t len, void *_ctx) +handle_nl80211_rate_info(struct module *mod, struct iface *iface, uint16_t type, bool nested, const void *payload, + size_t len, void *_ctx) { struct rate_info_ctx *ctx = _ctx; @@ -942,7 +941,8 @@ handle_nl80211_rate_info(struct module *mod, struct iface *iface, uint16_t type, default: LOG_DBG("%s: unrecognized nl80211 rate info attribute: " - "type=%hu, nested=%d, len=%zu", iface->name, type, nested, len); + "type=%hu, nested=%d, len=%zu", + iface->name, type, nested, len); break; } @@ -954,8 +954,8 @@ struct station_info_ctx { }; static bool -handle_nl80211_station_info(struct module *mod, struct iface *iface, uint16_t type, bool nested, - const void *payload, size_t len, void *_ctx) +handle_nl80211_station_info(struct module *mod, struct iface *iface, uint16_t type, bool nested, const void *payload, + size_t len, void *_ctx) { struct station_info_ctx *ctx = _ctx; @@ -974,8 +974,7 @@ handle_nl80211_station_info(struct module *mod, struct iface *iface, uint16_t ty case NL80211_STA_INFO_TX_BITRATE: { struct rate_info_ctx rctx = {0}; - foreach_nlattr_nested( - mod, iface, payload, len, &handle_nl80211_rate_info, &rctx); + foreach_nlattr_nested(mod, iface, payload, len, &handle_nl80211_rate_info, &rctx); LOG_DBG("TX bitrate: %.1f Mbit/s", rctx.bitrate / 1000. / 1000.); mtx_lock(&mod->lock); @@ -987,8 +986,7 @@ handle_nl80211_station_info(struct module *mod, struct iface *iface, uint16_t ty case NL80211_STA_INFO_RX_BITRATE: { struct rate_info_ctx rctx = {0}; - foreach_nlattr_nested( - mod, iface, payload, len, &handle_nl80211_rate_info, &rctx); + foreach_nlattr_nested(mod, iface, payload, len, &handle_nl80211_rate_info, &rctx); LOG_DBG("RX bitrate: %.1f Mbit/s", rctx.bitrate / 1000. / 1000.); mtx_lock(&mod->lock); @@ -1000,7 +998,8 @@ handle_nl80211_station_info(struct module *mod, struct iface *iface, uint16_t ty default: LOG_DBG("%s: unrecognized nl80211 station info attribute: " - "type=%hu, nested=%d, len=%zu", iface->name, type, nested, len); + "type=%hu, nested=%d, len=%zu", + iface->name, type, nested, len); break; } @@ -1008,8 +1007,8 @@ handle_nl80211_station_info(struct module *mod, struct iface *iface, uint16_t ty } static bool -handle_nl80211_new_station(struct module *mod, struct iface *iface, uint16_t type, bool nested, - const void *payload, size_t len, void *_ctx) +handle_nl80211_new_station(struct module *mod, struct iface *iface, uint16_t type, bool nested, const void *payload, + size_t len, void *_ctx) { switch (type) { case NL80211_ATTR_IFINDEX: @@ -1017,8 +1016,7 @@ handle_nl80211_new_station(struct module *mod, struct iface *iface, uint16_t typ case NL80211_ATTR_STA_INFO: { struct station_info_ctx ctx = {0}; - foreach_nlattr_nested( - mod, iface, payload, len, &handle_nl80211_station_info, &ctx); + foreach_nlattr_nested(mod, iface, payload, len, &handle_nl80211_station_info, &ctx); if (ctx.update_bar) mod->bar->refresh(mod->bar); @@ -1027,7 +1025,8 @@ handle_nl80211_new_station(struct module *mod, struct iface *iface, uint16_t typ default: LOG_DBG("%s: unrecognized nl80211 attribute: " - "type=%hu, nested=%d, len=%zu", iface->name, type, nested, len); + "type=%hu, nested=%d, len=%zu", + iface->name, type, nested, len); break; } @@ -1041,7 +1040,7 @@ handle_ies(struct module *mod, struct iface *iface, const void *_ies, size_t len while (len >= 2 && len - 2 >= ies[1]) { switch (ies[0]) { - case 0: { /* SSID */ + case 0: { /* SSID */ const char *ssid = (const char *)&ies[2]; const size_t ssid_len = ies[1]; @@ -1072,8 +1071,8 @@ struct scan_results_context { }; static bool -handle_nl80211_bss(struct module *mod, struct iface *iface, uint16_t type, bool nested, - const void *payload, size_t len, void *_ctx) +handle_nl80211_bss(struct module *mod, struct iface *iface, uint16_t type, bool nested, const void *payload, size_t len, + void *_ctx) { struct scan_results_context *ctx = _ctx; @@ -1113,8 +1112,8 @@ handle_nl80211_bss(struct module *mod, struct iface *iface, uint16_t type, bool } static bool -handle_nl80211_scan_results(struct module *mod, struct iface *iface, uint16_t type, bool nested, - const void *payload, size_t len, void *_ctx) +handle_nl80211_scan_results(struct module *mod, struct iface *iface, uint16_t type, bool nested, const void *payload, + size_t len, void *_ctx) { struct scan_results_context ctx = {0}; @@ -1172,7 +1171,8 @@ handle_stats(struct module *mod, uint32_t seq, struct rt_stats_msg *msg) struct iface *iface = NULL; - tll_foreach(m->ifaces, it) { + tll_foreach(m->ifaces, it) + { if (seq != it->item.get_stats_seq_nr) continue; iface = &it->item; @@ -1241,16 +1241,14 @@ parse_rt_reply(struct module *mod, const struct nlmsghdr *hdr, size_t len) break; } - case NLMSG_ERROR:{ + case NLMSG_ERROR: { const struct nlmsgerr *err = NLMSG_DATA(hdr); LOG_ERRNO_P(-err->error, "netlink RT reply"); return false; } default: - LOG_WARN( - "unrecognized netlink message type: 0x%x", - hdr->nlmsg_type); + LOG_WARN("unrecognized netlink message type: 0x%x", hdr->nlmsg_type); return false; } } @@ -1288,8 +1286,7 @@ parse_genl_reply(struct module *mod, const struct nlmsghdr *hdr, size_t len) /* Current request is now considered complete */ m->nl80211.get_scan_seq_nr = 0; - tll_foreach(m->ifaces, it) - send_nl80211_get_station(m, &it->item); + tll_foreach(m->ifaces, it) send_nl80211_get_station(m, &it->item); } } @@ -1300,11 +1297,10 @@ parse_genl_reply(struct module *mod, const struct nlmsghdr *hdr, size_t len) switch (genl->cmd) { case NL80211_CMD_NEW_INTERFACE: if (foreach_nlattr(mod, NULL, genl, msg_size, &find_nl80211_iface, &iface)) - continue; + continue; LOG_DBG("%s: got interface information", iface->name); - foreach_nlattr( - mod, iface, genl, msg_size, &handle_nl80211_new_interface, NULL); + foreach_nlattr(mod, iface, genl, msg_size, &handle_nl80211_new_interface, NULL); break; case NL80211_CMD_CONNECT: @@ -1324,7 +1320,7 @@ parse_genl_reply(struct module *mod, const struct nlmsghdr *hdr, size_t len) case NL80211_CMD_DISCONNECT: if (foreach_nlattr(mod, NULL, genl, msg_size, &find_nl80211_iface, &iface)) - continue; + continue; LOG_DBG("%s: disconnected, resetting SSID etc", iface->name); @@ -1338,20 +1334,18 @@ parse_genl_reply(struct module *mod, const struct nlmsghdr *hdr, size_t len) case NL80211_CMD_NEW_STATION: if (foreach_nlattr(mod, NULL, genl, msg_size, &find_nl80211_iface, &iface)) - continue; + continue; LOG_DBG("%s: got station information", iface->name); foreach_nlattr(mod, iface, genl, msg_size, &handle_nl80211_new_station, NULL); - LOG_DBG("%s: signal: %d dBm, RX=%u Mbit/s, TX=%u Mbit/s", - iface->name, iface->signal_strength_dbm, - iface->rx_bitrate / 1000 / 1000, - iface->tx_bitrate / 1000 / 1000); + LOG_DBG("%s: signal: %d dBm, RX=%u Mbit/s, TX=%u Mbit/s", iface->name, iface->signal_strength_dbm, + iface->rx_bitrate / 1000 / 1000, iface->tx_bitrate / 1000 / 1000); break; case NL80211_CMD_NEW_SCAN_RESULTS: if (foreach_nlattr(mod, NULL, genl, msg_size, &find_nl80211_iface, &iface)) - continue; + continue; LOG_DBG("%s: got scan results", iface->name); foreach_nlattr(mod, iface, genl, msg_size, &handle_nl80211_scan_results, NULL); @@ -1372,15 +1366,12 @@ parse_genl_reply(struct module *mod, const struct nlmsghdr *hdr, size_t len) else if (nl_errno == ENOENT) ; /* iface down? */ else { - LOG_ERRNO_P(nl_errno, "nl80211 reply (seq-nr: %u)", - hdr->nlmsg_seq); + LOG_ERRNO_P(nl_errno, "nl80211 reply (seq-nr: %u)", hdr->nlmsg_seq); } } else { - LOG_WARN( - "unrecognized netlink message type: 0x%x", - hdr->nlmsg_type); + LOG_WARN("unrecognized netlink message type: 0x%x", hdr->nlmsg_type); return false; } } @@ -1422,9 +1413,7 @@ run(struct module *mod) if (m->rt_sock < 0 || m->genl_sock < 0) goto out; - if (!send_rt_request(m, RTM_GETLINK) || - !send_ctrl_get_family_request(m)) - { + if (!send_rt_request(m, RTM_GETLINK) || !send_ctrl_get_family_request(m)) { goto out; } @@ -1442,9 +1431,7 @@ run(struct module *mod) if (fds[0].revents & (POLLIN | POLLHUP)) break; - if ((fds[1].revents & POLLHUP) || - (fds[2].revents & POLLHUP)) - { + if ((fds[1].revents & POLLHUP) || (fds[2].revents & POLLHUP)) { LOG_ERR("disconnected from netlink socket"); break; } @@ -1493,7 +1480,8 @@ run(struct module *mod) break; } - tll_foreach(m->ifaces, it) { + tll_foreach(m->ifaces, it) + { send_nl80211_get_station(m, &it->item); send_rt_getstats_request(m, &it->item); }; @@ -1502,7 +1490,7 @@ run(struct module *mod) ret = 0; - out: +out: if (m->rt_sock >= 0) close(m->rt_sock); if (m->genl_sock >= 0) @@ -1552,14 +1540,12 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) const struct yml_node *left_spacing = yml_get_value(node, "left-spacing"); const struct yml_node *right_spacing = yml_get_value(node, "right-spacing"); - int left = spacing != NULL ? yml_value_as_int(spacing) : - left_spacing != NULL ? yml_value_as_int(left_spacing) : 0; - int right = spacing != NULL ? yml_value_as_int(spacing) : - right_spacing != NULL ? yml_value_as_int(right_spacing) : 0; + int left = spacing != NULL ? yml_value_as_int(spacing) : left_spacing != NULL ? yml_value_as_int(left_spacing) : 0; + int right = spacing != NULL ? yml_value_as_int(spacing) + : right_spacing != NULL ? yml_value_as_int(right_spacing) + : 0; - return network_new( - conf_to_particle(content, inherited), - poll != NULL ? yml_value_as_int(poll) : 0, left, right); + return network_new(conf_to_particle(content, inherited), poll != NULL ? yml_value_as_int(poll) : 0, left, right); } static bool @@ -1570,8 +1556,7 @@ conf_verify_poll_interval(keychain_t *chain, const struct yml_node *node) int interval = yml_value_as_int(node); if (interval > 0 && interval < min_poll_interval) { - LOG_ERR("%s: interval value cannot be less than %ldms", - conf_err_prefix(chain, node), min_poll_interval); + LOG_ERR("%s: interval value cannot be less than %ldms", conf_err_prefix(chain, node), min_poll_interval); return false; } diff --git a/modules/pipewire.c b/modules/pipewire.c index 2300e7c..1a6ab1f 100644 --- a/modules/pipewire.c +++ b/modules/pipewire.c @@ -867,9 +867,7 @@ content(struct module *module) /* sink */ output_informations - = (private->data->target_sink == NULL - ? &output_informations_null - : &private->sink_informations); + = (private->data->target_sink == NULL ? &output_informations_null : &private->sink_informations); struct tag_set sink_tag_set = (struct tag_set){ .tags = (struct tag *[]){ @@ -888,9 +886,7 @@ content(struct module *module) /* source */ output_informations - = (private->data->target_source == NULL - ? &output_informations_null - : &private->source_informations); + = (private->data->target_source == NULL ? &output_informations_null : &private->source_informations); struct tag_set source_tag_set = (struct tag_set){ .tags = (struct tag *[]){ diff --git a/modules/pulse.c b/modules/pulse.c index 9eba3a5..e605dea 100644 --- a/modules/pulse.c +++ b/modules/pulse.c @@ -4,8 +4,8 @@ #include #include -#include #include +#include #include @@ -17,7 +17,8 @@ #include "../log.h" #include "../plugin.h" -struct private { +struct private +{ char *sink_name; char *source_name; struct particle *label; @@ -69,9 +70,9 @@ content(struct module *mod) mtx_lock(&mod->lock); - pa_volume_t sink_volume_max = pa_cvolume_max(&priv->sink_volume); + pa_volume_t sink_volume_max = pa_cvolume_max(&priv->sink_volume); pa_volume_t source_volume_max = pa_cvolume_max(&priv->source_volume); - int sink_percent = round(100.0 * sink_volume_max / PA_VOLUME_NORM); + int sink_percent = round(100.0 * sink_volume_max / PA_VOLUME_NORM); int source_percent = round(100.0 * source_volume_max / PA_VOLUME_NORM); struct tag_set tags = { @@ -106,11 +107,7 @@ context_error(pa_context *c) } static void -abort_event_cb(pa_mainloop_api *api, - pa_io_event *event, - int fd, - pa_io_event_flags_t flags, - void *userdata) +abort_event_cb(pa_mainloop_api *api, pa_io_event *event, int fd, pa_io_event_flags_t flags, void *userdata) { struct module *mod = userdata; struct private *priv = mod->private; @@ -119,11 +116,7 @@ abort_event_cb(pa_mainloop_api *api, } static void -refresh_timer_cb(pa_mainloop_api *api, - pa_io_event *event, - int fd, - pa_io_event_flags_t flags, - void *userdata) +refresh_timer_cb(pa_mainloop_api *api, pa_io_event *event, int fd, pa_io_event_flags_t flags, void *userdata) { struct module *mod = userdata; struct private *priv = mod->private; @@ -155,8 +148,8 @@ schedule_refresh(struct module *mod) // Start the refresh timer. struct itimerspec t = { - .it_interval = { .tv_sec = 0, .tv_nsec = 0 }, - .it_value = { .tv_sec = 0, .tv_nsec = 50000000 }, + .it_interval = {.tv_sec = 0, .tv_nsec = 0}, + .it_value = {.tv_sec = 0, .tv_nsec = 50000000}, }; timerfd_settime(priv->refresh_timer_fd, 0, &t, NULL); @@ -200,12 +193,10 @@ set_sink_info(struct module *mod, const pa_sink_info *sink_info) free(priv->sink_port); priv->sink_online = true; - priv->sink_index = sink_info->index; + priv->sink_index = sink_info->index; priv->sink_volume = sink_info->volume; - priv->sink_muted = sink_info->mute; - priv->sink_port = sink_info->active_port != NULL - ? strdup(sink_info->active_port->description) - : NULL; + priv->sink_muted = sink_info->mute; + priv->sink_port = sink_info->active_port != NULL ? strdup(sink_info->active_port->description) : NULL; mtx_unlock(&mod->lock); @@ -234,12 +225,10 @@ set_source_info(struct module *mod, const pa_source_info *source_info) free(priv->source_port); priv->source_online = true; - priv->source_index = source_info->index; + priv->source_index = source_info->index; priv->source_volume = source_info->volume; - priv->source_muted = source_info->mute; - priv->source_port = source_info->active_port != NULL - ? strdup(source_info->active_port->description) - : NULL; + priv->source_muted = source_info->mute; + priv->source_port = source_info->active_port != NULL ? strdup(source_info->active_port->description) : NULL; mtx_unlock(&mod->lock); @@ -293,32 +282,28 @@ server_info_cb(pa_context *c, const pa_server_info *i, void *userdata) static void get_sink_info_by_name(pa_context *c, const char *name, void *userdata) { - pa_operation *o = - pa_context_get_sink_info_by_name(c, name, sink_info_cb, userdata); + pa_operation *o = pa_context_get_sink_info_by_name(c, name, sink_info_cb, userdata); pa_operation_unref(o); } static void get_source_info_by_name(pa_context *c, const char *name, void *userdata) { - pa_operation *o = - pa_context_get_source_info_by_name(c, name, source_info_cb, userdata); + pa_operation *o = pa_context_get_source_info_by_name(c, name, source_info_cb, userdata); pa_operation_unref(o); } static void get_sink_info_by_index(pa_context *c, uint32_t index, void *userdata) { - pa_operation *o = - pa_context_get_sink_info_by_index(c, index, sink_info_cb, userdata); + pa_operation *o = pa_context_get_sink_info_by_index(c, index, sink_info_cb, userdata); pa_operation_unref(o); } static void get_source_info_by_index(pa_context *c, uint32_t index, void *userdata) { - pa_operation *o = - pa_context_get_source_info_by_index(c, index, source_info_cb, userdata); + pa_operation *o = pa_context_get_source_info_by_index(c, index, source_info_cb, userdata); pa_operation_unref(o); } @@ -332,15 +317,12 @@ get_server_info(pa_context *c, void *userdata) static void subscribe(pa_context *c, void *userdata) { - pa_subscription_mask_t mask = PA_SUBSCRIPTION_MASK_SERVER - | PA_SUBSCRIPTION_MASK_SINK - | PA_SUBSCRIPTION_MASK_SOURCE; + pa_subscription_mask_t mask = PA_SUBSCRIPTION_MASK_SERVER | PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SOURCE; pa_operation *o = pa_context_subscribe(c, mask, NULL, userdata); pa_operation_unref(o); } -static pa_context * -connect_to_server(struct module *mod); +static pa_context *connect_to_server(struct module *mod); static void context_state_change_cb(pa_context *c, void *userdata) @@ -380,16 +362,13 @@ context_state_change_cb(pa_context *c, void *userdata) } static void -subscription_event_cb(pa_context *c, - pa_subscription_event_type_t event_type, - uint32_t index, - void *userdata) +subscription_event_cb(pa_context *c, pa_subscription_event_type_t event_type, uint32_t index, void *userdata) { struct module *mod = userdata; struct private *priv = mod->private; int facility = event_type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK; - int type = event_type & PA_SUBSCRIPTION_EVENT_TYPE_MASK; + int type = event_type & PA_SUBSCRIPTION_EVENT_TYPE_MASK; switch (facility) { case PA_SUBSCRIPTION_EVENT_SERVER: @@ -435,8 +414,7 @@ connect_to_server(struct module *mod) pa_context_set_subscribe_callback(c, subscription_event_cb, mod); // Connect to server. - pa_context_flags_t flags = PA_CONTEXT_NOFAIL - | PA_CONTEXT_NOAUTOSPAWN; + pa_context_flags_t flags = PA_CONTEXT_NOFAIL | PA_CONTEXT_NOAUTOSPAWN; if (pa_context_connect(c, NULL, flags, NULL) < 0) { LOG_ERR("failed to connect to PulseAudio server: %s", context_error(c)); pa_context_unref(c); @@ -477,10 +455,8 @@ run(struct module *mod) // Poll refresh timer and abort event. pa_mainloop_api *api = pa_mainloop_get_api(priv->mainloop); - api->io_new(api, priv->refresh_timer_fd, PA_IO_EVENT_INPUT, - refresh_timer_cb, mod); - api->io_new(api, mod->abort_fd, PA_IO_EVENT_INPUT | PA_IO_EVENT_HANGUP, - abort_event_cb, mod); + api->io_new(api, priv->refresh_timer_fd, PA_IO_EVENT_INPUT, refresh_timer_cb, mod); + api->io_new(api, mod->abort_fd, PA_IO_EVENT_INPUT | PA_IO_EVENT_HANGUP, abort_event_cb, mod); // Run main loop. if (pa_mainloop_run(priv->mainloop, &ret) < 0) { @@ -497,9 +473,7 @@ run(struct module *mod) } static struct module * -pulse_new(const char *sink_name, - const char *source_name, - struct particle *label) +pulse_new(const char *sink_name, const char *source_name, struct particle *label) { struct private *priv = calloc(1, sizeof *priv); priv->label = label; @@ -522,10 +496,9 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) const struct yml_node *source = yml_get_value(node, "source"); const struct yml_node *content = yml_get_value(node, "content"); - return pulse_new( - sink != NULL ? yml_value_as_string(sink) : "@DEFAULT_SINK@", - source != NULL ? yml_value_as_string(source) : "@DEFAULT_SOURCE@", - conf_to_particle(content, inherited)); + return pulse_new(sink != NULL ? yml_value_as_string(sink) : "@DEFAULT_SINK@", + source != NULL ? yml_value_as_string(source) : "@DEFAULT_SOURCE@", + conf_to_particle(content, inherited)); } static bool diff --git a/modules/removables.c b/modules/removables.c index 4d1d508..e4ef98e 100644 --- a/modules/removables.c +++ b/modules/removables.c @@ -1,15 +1,15 @@ -#include -#include -#include -#include -#include -#include #include #include +#include +#include +#include +#include +#include +#include +#include #include #include -#include #include @@ -17,10 +17,10 @@ #define LOG_MODULE "removables" #define LOG_ENABLE_DBG 0 -#include "../log.h" #include "../bar/bar.h" -#include "../config.h" #include "../config-verify.h" +#include "../config.h" +#include "../log.h" #include "../particles/dynlist.h" #include "../plugin.h" @@ -54,7 +54,8 @@ struct block_device { tll(struct partition) partitions; }; -struct private { +struct private +{ struct particle *label; int left_spacing; int right_spacing; @@ -75,8 +76,7 @@ free_partition(struct partition *p) static void free_device(struct block_device *b) { - tll_foreach(b->partitions, it) - free_partition(&it->item); + tll_foreach(b->partitions, it) free_partition(&it->item); tll_free(b->partitions); free(b->sys_path); @@ -91,8 +91,7 @@ destroy(struct module *mod) struct private *m = mod->private; m->label->destroy(m->label); - tll_foreach(m->devices, it) - free_device(&it->item); + tll_foreach(m->devices, it) free_device(&it->item); tll_free(m->devices); tll_free_and_free(m->ignore, free); @@ -113,24 +112,23 @@ content(struct module *mod) tll(const struct partition *) partitions = tll_init(); - tll_foreach(m->devices, dev) { - tll_foreach(dev->item.partitions, part) { - tll_push_back(partitions, &part->item); - } + tll_foreach(m->devices, dev) + { + tll_foreach(dev->item.partitions, part) { tll_push_back(partitions, &part->item); } } struct exposable *exposables[max(tll_length(partitions), 1)]; size_t idx = 0; - tll_foreach(partitions, it) { + tll_foreach(partitions, it) + { const struct partition *p = it->item; char dummy_label[16]; const char *label = p->label; if (label == NULL) { - snprintf(dummy_label, sizeof(dummy_label), - "%.1f GB", (double)p->size / 1024 / 1024 / 1024 * 512); + snprintf(dummy_label, sizeof(dummy_label), "%.1f GB", (double)p->size / 1024 / 1024 / 1024 * 512); label = dummy_label; } @@ -157,8 +155,7 @@ content(struct module *mod) } tll_free(partitions); - return dynlist_exposable_new( - exposables, idx, m->left_spacing, m->right_spacing); + return dynlist_exposable_new(exposables, idx, m->left_spacing, m->right_spacing); } static void @@ -178,9 +175,7 @@ find_mount_points(const char *dev_path, mount_point_list_t *mount_points) while (fgets(line, sizeof(line), f) != NULL) { char *dev = NULL, *path = NULL; - if (sscanf(line, "%*u %*u %*u:%*u %*s %ms %*[^-] - %*s %ms %*s", - &path, &dev) != 2) - { + if (sscanf(line, "%*u %*u %*u:%*u %*s %ms %*[^-] - %*s %ms %*s", &path, &dev) != 2) { LOG_ERR("failed to parse /proc/self/mountinfo: %s", line); free(dev); free(path); @@ -207,9 +202,11 @@ update_mount_points(struct partition *partition) /* Remove mount points that no longer exists (i.e. old mount * points that aren't in the new list) */ - tll_foreach(partition->mount_points, old) { + tll_foreach(partition->mount_points, old) + { bool gone = true; - tll_foreach(new_mounts, new) { + tll_foreach(new_mounts, new) + { if (strcmp(new->item, old->item) == 0) { /* Remove from new list, as it's already in the * partitions list */ @@ -228,7 +225,8 @@ update_mount_points(struct partition *partition) /* Add new mount points (i.e. mount points in the new list, that * aren't in the old list) */ - tll_foreach(new_mounts, new) { + tll_foreach(new_mounts, new) + { LOG_DBG("%s: mounted on %s", partition->dev_path, new->item); tll_push_back(partition->mount_points, new->item); @@ -242,14 +240,13 @@ update_mount_points(struct partition *partition) } static struct partition * -add_partition(struct module *mod, struct block_device *block, - struct udev_device *dev) +add_partition(struct module *mod, struct block_device *block, struct udev_device *dev) { struct private *m = mod->private; const char *_size = udev_device_get_sysattr_value(dev, "size"); uint64_t size = 0; if (_size != NULL) - sscanf(_size, "%"SCNu64, &size); + sscanf(_size, "%" SCNu64, &size); #if 0 struct udev_list_entry *e = NULL; @@ -260,7 +257,8 @@ add_partition(struct module *mod, struct block_device *block, const char *devname = udev_device_get_property_value(dev, "DEVNAME"); if (devname != NULL) { - tll_foreach(m->ignore, it) { + tll_foreach(m->ignore, it) + { if (strcmp(it->item, devname) == 0) { LOG_DBG("ignoring %s because it is on the ignore list", devname); return NULL; @@ -272,21 +270,17 @@ add_partition(struct module *mod, struct block_device *block, if (label == NULL) label = udev_device_get_property_value(dev, "ID_LABEL"); - LOG_INFO("partition: add: %s: label=%s, size=%"PRIu64, - udev_device_get_devnode(dev), label, size); + LOG_INFO("partition: add: %s: label=%s, size=%" PRIu64, udev_device_get_devnode(dev), label, size); mtx_lock(&mod->lock); - tll_push_back( - block->partitions, - ((struct partition){ - .block = block, - .sys_path = strdup(udev_device_get_devpath(dev)), - .dev_path = strdup(udev_device_get_devnode(dev)), - .label = label != NULL ? strdup(label) : NULL, - .size = size, - .audio_cd = false, - .mount_points = tll_init()})); + tll_push_back(block->partitions, ((struct partition){.block = block, + .sys_path = strdup(udev_device_get_devpath(dev)), + .dev_path = strdup(udev_device_get_devnode(dev)), + .label = label != NULL ? strdup(label) : NULL, + .size = size, + .audio_cd = false, + .mount_points = tll_init()})); struct partition *p = &tll_back(block->partitions); update_mount_points(p); @@ -296,14 +290,13 @@ add_partition(struct module *mod, struct block_device *block, } static struct partition * -add_audio_cd(struct module *mod, struct block_device *block, - struct udev_device *dev) +add_audio_cd(struct module *mod, struct block_device *block, struct udev_device *dev) { struct private *m = mod->private; const char *_size = udev_device_get_sysattr_value(dev, "size"); uint64_t size = 0; if (_size != NULL) - sscanf(_size, "%"SCNu64, &size); + sscanf(_size, "%" SCNu64, &size); #if 0 struct udev_list_entry *e = NULL; @@ -314,7 +307,8 @@ add_audio_cd(struct module *mod, struct block_device *block, const char *devname = udev_device_get_property_value(dev, "DEVNAME"); if (devname != NULL) { - tll_foreach(m->ignore, it) { + tll_foreach(m->ignore, it) + { if (strcmp(it->item, devname) == 0) { LOG_DBG("ignoring %s because it is on the ignore list", devname); return NULL; @@ -322,28 +316,24 @@ add_audio_cd(struct module *mod, struct block_device *block, } } - const char *_track_count = udev_device_get_property_value( - dev, "ID_CDROM_MEDIA_TRACK_COUNT_AUDIO"); + const char *_track_count = udev_device_get_property_value(dev, "ID_CDROM_MEDIA_TRACK_COUNT_AUDIO"); unsigned long track_count = strtoul(_track_count, NULL, 10); char label[64]; snprintf(label, sizeof(label), "Audio CD - %lu tracks", track_count); - LOG_INFO("audio CD: add: %s: tracks=%lu, label=%s, size=%"PRIu64, - udev_device_get_devnode(dev), track_count, label, size); + LOG_INFO("audio CD: add: %s: tracks=%lu, label=%s, size=%" PRIu64, udev_device_get_devnode(dev), track_count, label, + size); mtx_lock(&mod->lock); - tll_push_back( - block->partitions, - ((struct partition){ - .block = block, - .sys_path = strdup(udev_device_get_devpath(dev)), - .dev_path = strdup(udev_device_get_devnode(dev)), - .label = label != NULL ? strdup(label) : NULL, - .size = size, - .audio_cd = true, - .mount_points = tll_init()})); + tll_push_back(block->partitions, ((struct partition){.block = block, + .sys_path = strdup(udev_device_get_devpath(dev)), + .dev_path = strdup(udev_device_get_devnode(dev)), + .label = label != NULL ? strdup(label) : NULL, + .size = size, + .audio_cd = true, + .mount_points = tll_init()})); struct partition *p = &tll_back(block->partitions); update_mount_points(p); @@ -353,17 +343,15 @@ add_audio_cd(struct module *mod, struct block_device *block, } static bool -del_partition(struct module *mod, struct block_device *block, - struct udev_device *dev) +del_partition(struct module *mod, struct block_device *block, struct udev_device *dev) { const char *sys_path = udev_device_get_devpath(dev); mtx_lock(&mod->lock); - tll_foreach(block->partitions, it) { + tll_foreach(block->partitions, it) + { if (strcmp(it->item.sys_path, sys_path) == 0) { - LOG_INFO("%s: del: %s", - it->item.audio_cd ? "audio CD" : "partition", - it->item.dev_path); + LOG_INFO("%s: del: %s", it->item.audio_cd ? "audio CD" : "partition", it->item.dev_path); free_partition(&it->item); tll_remove(block->partitions, it); @@ -392,7 +380,8 @@ add_device(struct module *mod, struct udev_device *dev) const char *devname = udev_device_get_property_value(dev, "DEVNAME"); if (devname != NULL) { - tll_foreach(m->ignore, it) { + tll_foreach(m->ignore, it) + { if (strcmp(it->item, devname) == 0) { LOG_DBG("ignoring %s because it is on the ignore list", devname); return NULL; @@ -403,11 +392,12 @@ add_device(struct module *mod, struct udev_device *dev) const char *_size = udev_device_get_sysattr_value(dev, "size"); uint64_t size = 0; if (_size != NULL) - sscanf(_size, "%"SCNu64, &size); + sscanf(_size, "%" SCNu64, &size); #if 1 struct udev_list_entry *e = NULL; - udev_list_entry_foreach(e, udev_device_get_properties_list_entry(dev)) { + udev_list_entry_foreach(e, udev_device_get_properties_list_entry(dev)) + { LOG_DBG("%s -> %s", udev_list_entry_get_name(e), udev_list_entry_get_value(e)); } #endif @@ -424,27 +414,22 @@ add_device(struct module *mod, struct udev_device *dev) const char *_fs_usage = udev_device_get_property_value(dev, "ID_FS_USAGE"); bool have_fs = _fs_usage != NULL && strcmp(_fs_usage, "filesystem") == 0; - const char *_audio_track_count = udev_device_get_property_value( - dev, "ID_CDROM_MEDIA_TRACK_COUNT_AUDIO"); - unsigned long audio_track_count = - _audio_track_count != NULL ? strtoul(_audio_track_count, NULL, 10) : 0; + const char *_audio_track_count = udev_device_get_property_value(dev, "ID_CDROM_MEDIA_TRACK_COUNT_AUDIO"); + unsigned long audio_track_count = _audio_track_count != NULL ? strtoul(_audio_track_count, NULL, 10) : 0; - LOG_DBG("device: add: %s: vendor=%s, model=%s, optical=%d, size=%"PRIu64, - udev_device_get_devnode(dev), vendor, model, optical, size); + LOG_DBG("device: add: %s: vendor=%s, model=%s, optical=%d, size=%" PRIu64, udev_device_get_devnode(dev), vendor, + model, optical, size); mtx_lock(&mod->lock); - tll_push_back( - m->devices, - ((struct block_device){ - .sys_path = strdup(udev_device_get_devpath(dev)), - .dev_path = strdup(udev_device_get_devnode(dev)), - .size = size, - .vendor = vendor != NULL ? strdup(vendor) : NULL, - .model = model != NULL ? strdup(model) : NULL, - .optical = optical, - .media = media, - .partitions = tll_init()})); + tll_push_back(m->devices, ((struct block_device){.sys_path = strdup(udev_device_get_devpath(dev)), + .dev_path = strdup(udev_device_get_devnode(dev)), + .size = size, + .vendor = vendor != NULL ? strdup(vendor) : NULL, + .model = model != NULL ? strdup(model) : NULL, + .optical = optical, + .media = media, + .partitions = tll_init()})); mtx_unlock(&mod->lock); @@ -466,7 +451,8 @@ del_device(struct module *mod, struct udev_device *dev) const char *sys_path = udev_device_get_devpath(dev); mtx_lock(&mod->lock); - tll_foreach(m->devices, it) { + tll_foreach(m->devices, it) + { if (strcmp(it->item.sys_path, sys_path) == 0) { LOG_DBG("device: del: %s", it->item.dev_path); @@ -490,7 +476,8 @@ change_device(struct module *mod, struct udev_device *dev) struct block_device *block = NULL; - tll_foreach(m->devices, it) { + tll_foreach(m->devices, it) + { if (strcmp(it->item.sys_path, sys_path) == 0) { block = &it->item; break; @@ -511,10 +498,8 @@ change_device(struct module *mod, struct udev_device *dev) const char *_fs_usage = udev_device_get_property_value(dev, "ID_FS_USAGE"); bool have_fs = _fs_usage != NULL && strcmp(_fs_usage, "filesystem") == 0; - const char *_audio_track_count = udev_device_get_property_value( - dev, "ID_CDROM_MEDIA_TRACK_COUNT_AUDIO"); - unsigned long audio_track_count = - _audio_track_count != NULL ? strtoul(_audio_track_count, NULL, 10) : 0; + const char *_audio_track_count = udev_device_get_property_value(dev, "ID_CDROM_MEDIA_TRACK_COUNT_AUDIO"); + unsigned long audio_track_count = _audio_track_count != NULL ? strtoul(_audio_track_count, NULL, 10) : 0; bool media_change = media != block->media; @@ -522,8 +507,7 @@ change_device(struct module *mod, struct udev_device *dev) mtx_unlock(&mod->lock); if (media_change) { - LOG_INFO("device: change: %s: media %s", - block->dev_path, media ? "inserted" : "removed"); + LOG_INFO("device: change: %s: media %s", block->dev_path, media ? "inserted" : "removed"); if (media) { if (have_fs) @@ -569,7 +553,8 @@ handle_udev_event(struct module *mod, struct udev_device *dev) struct udev_device *parent = udev_device_get_parent(dev); const char *parent_sys_path = udev_device_get_devpath(parent); - tll_foreach(m->devices, it) { + tll_foreach(m->devices, it) + { if (strcmp(it->item.sys_path, parent_sys_path) != 0) continue; @@ -578,8 +563,7 @@ handle_udev_event(struct module *mod, struct udev_device *dev) else if (del) return del_partition(mod, &it->item, dev); else { - LOG_ERR("unimplemented: 'change' event on partition: %s", - udev_device_get_devpath(dev)); + LOG_ERR("unimplemented: 'change' event on partition: %s", udev_device_get_devpath(dev)); return false; } break; @@ -606,15 +590,15 @@ run(struct module *mod) udev_enumerate_add_match_subsystem(dev_enum, "block"); /* TODO: verify how an optical presents itself */ - //udev_enumerate_add_match_sysattr(dev_enum, "removable", "1"); + // udev_enumerate_add_match_sysattr(dev_enum, "removable", "1"); udev_enumerate_add_match_property(dev_enum, "DEVTYPE", "disk"); udev_enumerate_scan_devices(dev_enum); /* Loop list, and for each device, enumerate its partitions */ struct udev_list_entry *entry = NULL; - udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(dev_enum)) { - struct udev_device *dev = udev_device_new_from_syspath( - udev, udev_list_entry_get_name(entry)); + udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(dev_enum)) + { + struct udev_device *dev = udev_device_new_from_syspath(udev, udev_list_entry_get_name(entry)); struct block_device *block = add_device(mod, dev); if (block == NULL) { @@ -631,9 +615,9 @@ run(struct module *mod) udev_enumerate_scan_devices(part_enum); struct udev_list_entry *sub_entry = NULL; - udev_list_entry_foreach(sub_entry, udev_enumerate_get_list_entry(part_enum)) { - struct udev_device *partition = udev_device_new_from_syspath( - udev, udev_list_entry_get_name(sub_entry)); + udev_list_entry_foreach(sub_entry, udev_enumerate_get_list_entry(part_enum)) + { + struct udev_device *partition = udev_device_new_from_syspath(udev, udev_list_entry_get_name(sub_entry)); add_partition(mod, block, partition); udev_device_unref(partition); } @@ -673,8 +657,10 @@ run(struct module *mod) bool update = false; if (fds[2].revents & POLLPRI) { - tll_foreach(m->devices, dev) { - tll_foreach(dev->item.partitions, part) { + tll_foreach(m->devices, dev) + { + tll_foreach(dev->item.partitions, part) + { if (update_mount_points(&part->item)) update = true; } @@ -703,8 +689,8 @@ run(struct module *mod) } static struct module * -removables_new(struct particle *label, int left_spacing, int right_spacing, - size_t ignore_count, const char *ignore[static ignore_count]) +removables_new(struct particle *label, int left_spacing, int right_spacing, size_t ignore_count, + const char *ignore[static ignore_count]) { struct private *priv = calloc(1, sizeof(*priv)); priv->label = label; @@ -732,26 +718,22 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) const struct yml_node *right_spacing = yml_get_value(node, "right-spacing"); const struct yml_node *ignore_list = yml_get_value(node, "ignore"); - int left = spacing != NULL ? yml_value_as_int(spacing) : - left_spacing != NULL ? yml_value_as_int(left_spacing) : 0; - int right = spacing != NULL ? yml_value_as_int(spacing) : - right_spacing != NULL ? yml_value_as_int(right_spacing) : 0; + int left = spacing != NULL ? yml_value_as_int(spacing) : left_spacing != NULL ? yml_value_as_int(left_spacing) : 0; + int right = spacing != NULL ? yml_value_as_int(spacing) + : right_spacing != NULL ? yml_value_as_int(right_spacing) + : 0; size_t ignore_count = ignore_list != NULL ? yml_list_length(ignore_list) : 0; const char *ignore[max(ignore_count, 1)]; if (ignore_list != NULL) { size_t i = 0; - for (struct yml_list_iter iter = yml_list_iter(ignore_list); - iter.node != NULL; - yml_list_next(&iter), i++) - { + for (struct yml_list_iter iter = yml_list_iter(ignore_list); iter.node != NULL; yml_list_next(&iter), i++) { ignore[i] = yml_value_as_string(iter.node); } } - return removables_new( - conf_to_particle(content, inherited), left, right, ignore_count, ignore); + return removables_new(conf_to_particle(content, inherited), left, right, ignore_count, ignore); } static bool diff --git a/modules/river.c b/modules/river.c index 2273166..2619c62 100644 --- a/modules/river.c +++ b/modules/river.c @@ -1,17 +1,17 @@ +#include +#include +#include #include #include -#include -#include -#include -#include #include +#include #define LOG_MODULE "river" #define LOG_ENABLE_DBG 0 #include "../log.h" -#include "../plugin.h" #include "../particles/dynlist.h" +#include "../plugin.h" #include "river-status-unstable-v1.h" #include "xdg-output-unstable-v1.h" @@ -49,7 +49,8 @@ struct seat { struct output *output; }; -struct private { +struct private +{ struct module *mod; struct zxdg_output_manager_v1 *xdg_output_manager; struct zriver_status_manager_v1 *status_manager; @@ -92,13 +93,12 @@ content(struct module *mod) uint32_t output_focused = 0; uint32_t seat_focused = 0; - tll_foreach(m->outputs, it) { + tll_foreach(m->outputs, it) + { const struct output *output = &it->item; - if (!m->all_monitors && - output_bar_is_on != NULL && output->name != NULL && - strcmp(output->name, output_bar_is_on) != 0) - { + if (!m->all_monitors && output_bar_is_on != NULL && output->name != NULL + && strcmp(output->name, output_bar_is_on) != 0) { continue; } @@ -106,7 +106,8 @@ content(struct module *mod) urgent |= output->urgent; occupied |= output->occupied; - tll_foreach(m->seats, it2) { + tll_foreach(m->seats, it2) + { const struct seat *seat = &it2->item; if (seat->output == output) { seat_focused |= output->focused; @@ -127,10 +128,7 @@ content(struct module *mod) bool is_urgent = urgent & (1u << i); bool is_occupied = occupied & (1u << i); - const char *state = - is_urgent ? "urgent" : - is_visible ? is_focused ? "focused" : "unfocused" : - "invisible"; + const char *state = is_urgent ? "urgent" : is_visible ? is_focused ? "focused" : "unfocused" : "invisible"; #if 0 LOG_DBG("tag: #%u, visible=%d, focused=%d, occupied=%d, state=%s", @@ -155,12 +153,10 @@ content(struct module *mod) if (m->title != NULL) { size_t i = 32; - tll_foreach(m->seats, it) { + tll_foreach(m->seats, it) + { const struct seat *seat = &it->item; - const char *layout = - seat->output != NULL && seat->output->layout != NULL - ? seat->output->layout - : ""; + const char *layout = seat->output != NULL && seat->output->layout != NULL ? seat->output->layout : ""; struct tag_set tags = { .tags = (struct tag *[]){ @@ -187,15 +183,15 @@ verify_iface_version(const char *iface, uint32_t version, uint32_t wanted) if (version >= wanted) return true; - LOG_ERR("%s: need interface version %u, but compositor only implements %u", - iface, wanted, version); + LOG_ERR("%s: need interface version %u, but compositor only implements %u", iface, wanted, version); return false; } static void output_destroy(struct output *output) { - tll_foreach(output->m->seats, it) { + tll_foreach(output->m->seats, it) + { struct seat *seat = &it->item; if (seat->output == output) seat->output = NULL; @@ -223,8 +219,7 @@ seat_destroy(struct seat *seat) } static void -focused_tags(void *data, struct zriver_output_status_v1 *zriver_output_status_v1, - uint32_t tags) +focused_tags(void *data, struct zriver_output_status_v1 *zriver_output_status_v1, uint32_t tags) { struct output *output = data; @@ -241,8 +236,7 @@ focused_tags(void *data, struct zriver_output_status_v1 *zriver_output_status_v1 } static void -view_tags(void *data, struct zriver_output_status_v1 *zriver_output_status_v1, - struct wl_array *tags) +view_tags(void *data, struct zriver_output_status_v1 *zriver_output_status_v1, struct wl_array *tags) { struct output *output = data; struct module *mod = output->m->mod; @@ -254,9 +248,7 @@ view_tags(void *data, struct zriver_output_status_v1 *zriver_output_status_v1, /* Each entry in the list is a view, and the value is the tags * associated with that view */ uint32_t *set; - wl_array_for_each(set, tags) { - output->occupied |= *set; - } + wl_array_for_each(set, tags) { output->occupied |= *set; } LOG_DBG("output: %s: occupied tags: 0x%0x", output->name, output->occupied); } @@ -265,8 +257,7 @@ view_tags(void *data, struct zriver_output_status_v1 *zriver_output_status_v1, } static void -urgent_tags(void *data, struct zriver_output_status_v1 *zriver_output_status_v1, - uint32_t tags) +urgent_tags(void *data, struct zriver_output_status_v1 *zriver_output_status_v1, uint32_t tags) { struct output *output = data; struct module *mod = output->m->mod; @@ -281,9 +272,7 @@ urgent_tags(void *data, struct zriver_output_status_v1 *zriver_output_status_v1, #if defined(ZRIVER_OUTPUT_STATUS_V1_LAYOUT_NAME_SINCE_VERSION) static void -layout_name(void *data, - struct zriver_output_status_v1 *zriver_output_status_v1, - const char *name) +layout_name(void *data, struct zriver_output_status_v1 *zriver_output_status_v1, const char *name) { struct output *output = data; struct module *mod = output->m->mod; @@ -300,8 +289,7 @@ layout_name(void *data, #if defined(ZRIVER_OUTPUT_STATUS_V1_LAYOUT_NAME_CLEAR_SINCE_VERSION) static void -layout_name_clear(void *data, - struct zriver_output_status_v1 *zriver_output_status_v1) +layout_name_clear(void *data, struct zriver_output_status_v1 *zriver_output_status_v1) { struct output *output = data; struct module *mod = output->m->mod; @@ -329,15 +317,12 @@ static const struct zriver_output_status_v1_listener river_status_output_listene }; static void -xdg_output_handle_logical_position(void *data, - struct zxdg_output_v1 *xdg_output, - int32_t x, int32_t y) +xdg_output_handle_logical_position(void *data, struct zxdg_output_v1 *xdg_output, int32_t x, int32_t y) { } static void -xdg_output_handle_logical_size(void *data, struct zxdg_output_v1 *xdg_output, - int32_t width, int32_t height) +xdg_output_handle_logical_size(void *data, struct zxdg_output_v1 *xdg_output, int32_t width, int32_t height) { } @@ -347,8 +332,7 @@ xdg_output_handle_done(void *data, struct zxdg_output_v1 *xdg_output) } static void -xdg_output_handle_name(void *data, struct zxdg_output_v1 *xdg_output, - const char *name) +xdg_output_handle_name(void *data, struct zxdg_output_v1 *xdg_output, const char *name) { struct output *output = data; struct module *mod = output->m->mod; @@ -363,8 +347,7 @@ xdg_output_handle_name(void *data, struct zxdg_output_v1 *xdg_output, } static void -xdg_output_handle_description(void *data, struct zxdg_output_v1 *xdg_output, - const char *description) +xdg_output_handle_description(void *data, struct zxdg_output_v1 *xdg_output, const char *description) { } @@ -391,36 +374,32 @@ update_output(struct output *output) output->status = NULL; } - output->status = zriver_status_manager_v1_get_river_output_status( - output->m->status_manager, output->wl_output); + output->status = zriver_status_manager_v1_get_river_output_status(output->m->status_manager, output->wl_output); if (output->status != NULL) { - zriver_output_status_v1_add_listener( - output->status, &river_status_output_listener, output); + zriver_output_status_v1_add_listener(output->status, &river_status_output_listener, output); } } if (output->m->xdg_output_manager != NULL && output->xdg_output == NULL) { - output->xdg_output = zxdg_output_manager_v1_get_xdg_output( - output->m->xdg_output_manager, output->wl_output); + output->xdg_output = zxdg_output_manager_v1_get_xdg_output(output->m->xdg_output_manager, output->wl_output); if (output->xdg_output != NULL) { - zxdg_output_v1_add_listener( - output->xdg_output, &xdg_output_listener, output); + zxdg_output_v1_add_listener(output->xdg_output, &xdg_output_listener, output); } } } static void -focused_output(void *data, struct zriver_seat_status_v1 *zriver_seat_status_v1, - struct wl_output *wl_output) +focused_output(void *data, struct zriver_seat_status_v1 *zriver_seat_status_v1, struct wl_output *wl_output) { struct seat *seat = data; struct private *m = seat->m; struct module *mod = m->mod; struct output *output = NULL; - tll_foreach(m->outputs, it) { + tll_foreach(m->outputs, it) + { if (it->item.wl_output == wl_output) { output = &it->item; break; @@ -441,8 +420,7 @@ focused_output(void *data, struct zriver_seat_status_v1 *zriver_seat_status_v1, } static void -unfocused_output(void *data, struct zriver_seat_status_v1 *zriver_seat_status_v1, - struct wl_output *wl_output) +unfocused_output(void *data, struct zriver_seat_status_v1 *zriver_seat_status_v1, struct wl_output *wl_output) { struct seat *seat = data; struct private *m = seat->m; @@ -451,7 +429,8 @@ unfocused_output(void *data, struct zriver_seat_status_v1 *zriver_seat_status_v1 mtx_lock(&mod->lock); { struct output *output = NULL; - tll_foreach(m->outputs, it) { + tll_foreach(m->outputs, it) + { if (it->item.wl_output == wl_output) { output = &it->item; break; @@ -469,8 +448,7 @@ unfocused_output(void *data, struct zriver_seat_status_v1 *zriver_seat_status_v1 } static void -focused_view(void *data, struct zriver_seat_status_v1 *zriver_seat_status_v1, - const char *title) +focused_view(void *data, struct zriver_seat_status_v1 *zriver_seat_status_v1, const char *title) { struct seat *seat = data; struct module *mod = seat->m->mod; @@ -485,11 +463,9 @@ focused_view(void *data, struct zriver_seat_status_v1 *zriver_seat_status_v1, const char *output_bar_is_on = mod->bar->output_name(mod->bar); - if (seat->m->all_monitors || - (output_bar_is_on != NULL && - seat->output != NULL && seat->output->name != NULL && - strcmp(output_bar_is_on, seat->output->name) == 0)) - { + if (seat->m->all_monitors + || (output_bar_is_on != NULL && seat->output != NULL && seat->output->name != NULL + && strcmp(output_bar_is_on, seat->output->name) == 0)) { mtx_lock(&mod->lock); { free(seat->title); @@ -502,8 +478,7 @@ focused_view(void *data, struct zriver_seat_status_v1 *zriver_seat_status_v1, #if defined(ZRIVER_SEAT_STATUS_V1_MODE_SINCE_VERSION) static void -mode(void *data, struct zriver_seat_status_v1 *zriver_seat_status_v1, - const char *name) +mode(void *data, struct zriver_seat_status_v1 *zriver_seat_status_v1, const char *name) { struct seat *seat = data; struct module *mod = seat->m->mod; @@ -531,8 +506,7 @@ static const struct zriver_seat_status_v1_listener river_seat_status_listener = }; static void -seat_handle_capabilities(void *data, struct wl_seat *wl_seat, - enum wl_seat_capability caps) +seat_handle_capabilities(void *data, struct wl_seat *wl_seat, enum wl_seat_capability caps) { } @@ -569,19 +543,16 @@ update_seat(struct seat *seat) seat->status = NULL; } - seat->status = zriver_status_manager_v1_get_river_seat_status( - seat->m->status_manager, seat->wl_seat); + seat->status = zriver_status_manager_v1_get_river_seat_status(seat->m->status_manager, seat->wl_seat); if (seat->status == NULL) return; - zriver_seat_status_v1_add_listener( - seat->status, &river_seat_status_listener, seat); + zriver_seat_status_v1_add_listener(seat->status, &river_seat_status_listener, seat); } static void -handle_global(void *data, struct wl_registry *registry, - uint32_t name, const char *interface, uint32_t version) +handle_global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { struct private *m = data; @@ -590,8 +561,7 @@ handle_global(void *data, struct wl_registry *registry, if (!verify_iface_version(interface, version, required)) return; - struct wl_output *wl_output = wl_registry_bind( - registry, name, &wl_output_interface, required); + struct wl_output *wl_output = wl_registry_bind(registry, name, &wl_output_interface, required); if (wl_output == NULL) return; @@ -605,8 +575,7 @@ handle_global(void *data, struct wl_registry *registry, mtx_lock(&m->mod->lock); tll_push_back(m->outputs, output); update_output(&tll_back(m->outputs)); - tll_foreach(m->seats, it) - update_seat(&it->item); + tll_foreach(m->seats, it) update_seat(&it->item); mtx_unlock(&m->mod->lock); } @@ -615,12 +584,10 @@ handle_global(void *data, struct wl_registry *registry, if (!verify_iface_version(interface, version, required)) return; - m->xdg_output_manager = wl_registry_bind( - registry, name, &zxdg_output_manager_v1_interface, required); + m->xdg_output_manager = wl_registry_bind(registry, name, &zxdg_output_manager_v1_interface, required); mtx_lock(&m->mod->lock); - tll_foreach(m->outputs, it) - update_output(&it->item); + tll_foreach(m->outputs, it) update_output(&it->item); mtx_unlock(&m->mod->lock); } @@ -629,8 +596,7 @@ handle_global(void *data, struct wl_registry *registry, if (!verify_iface_version(interface, version, required)) return; - struct wl_seat *wl_seat = wl_registry_bind( - registry, name, &wl_seat_interface, required); + struct wl_seat *wl_seat = wl_registry_bind(registry, name, &wl_seat_interface, required); if (wl_seat == NULL) return; @@ -649,14 +615,11 @@ handle_global(void *data, struct wl_registry *registry, if (!verify_iface_version(interface, version, required)) return; - m->status_manager = wl_registry_bind( - registry, name, &zriver_status_manager_v1_interface, min(version, 4)); + m->status_manager = wl_registry_bind(registry, name, &zriver_status_manager_v1_interface, min(version, 4)); mtx_lock(&m->mod->lock); - tll_foreach(m->outputs, it) - update_output(&it->item); - tll_foreach(m->seats, it) - update_seat(&it->item); + tll_foreach(m->outputs, it) update_output(&it->item); + tll_foreach(m->seats, it) update_seat(&it->item); mtx_unlock(&m->mod->lock); } } @@ -667,7 +630,8 @@ handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) struct private *m = data; mtx_lock(&m->mod->lock); - tll_foreach(m->outputs, it) { + tll_foreach(m->outputs, it) + { if (it->item.wl_name == name) { output_destroy(&it->item); tll_remove(m->outputs, it); @@ -676,7 +640,8 @@ handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) } } - tll_foreach(m->seats, it) { + tll_foreach(m->seats, it) + { if (it->item.wl_name == name) { seat_destroy(&it->item); tll_remove(m->seats, it); @@ -707,9 +672,8 @@ run(struct module *mod) goto out; } - if ((registry = wl_display_get_registry(display)) == NULL || - wl_registry_add_listener(registry, ®istry_listener, m) != 0) - { + if ((registry = wl_display_get_registry(display)) == NULL + || wl_registry_add_listener(registry, ®istry_listener, m) != 0) { LOG_ERR("failed to get Wayland registry"); goto out; } @@ -754,11 +718,9 @@ run(struct module *mod) ret = 0; out: - tll_foreach(m->seats, it) - seat_destroy(&it->item); + tll_foreach(m->seats, it) seat_destroy(&it->item); tll_free(m->seats); - tll_foreach(m->outputs, it) - output_destroy(&it->item); + tll_foreach(m->outputs, it) output_destroy(&it->item); tll_free(m->outputs); if (m->xdg_output_manager != NULL) @@ -797,10 +759,8 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) const struct yml_node *title = yml_get_value(node, "title"); const struct yml_node *all_monitors = yml_get_value(node, "all-monitors"); - return river_new( - conf_to_particle(c, inherited), - title != NULL ? conf_to_particle(title, inherited) : NULL, - all_monitors != NULL ? yml_value_as_bool(all_monitors) : false); + return river_new(conf_to_particle(c, inherited), title != NULL ? conf_to_particle(title, inherited) : NULL, + all_monitors != NULL ? yml_value_as_bool(all_monitors) : false); } static bool diff --git a/modules/script.c b/modules/script.c index 0932cb2..9f9b40a 100644 --- a/modules/script.c +++ b/modules/script.c @@ -1,14 +1,14 @@ -#include -#include -#include -#include #include -#include +#include #include #include +#include +#include +#include +#include -#include #include +#include #include #include @@ -16,15 +16,16 @@ #define LOG_MODULE "script" #define LOG_ENABLE_DBG 0 -#include "../log.h" -#include "../config.h" #include "../config-verify.h" +#include "../config.h" +#include "../log.h" #include "../module.h" #include "../plugin.h" static const long min_poll_interval = 250; -struct private { +struct private +{ char *path; size_t argc; char **argv; @@ -110,9 +111,8 @@ process_line(struct module *mod, const char *line, size_t len) size_t value_len = line + len - _value; - LOG_DBG("%.*s: name=\"%.*s\", type=\"%.*s\", value=\"%.*s\"", - (int)len, line, - (int)name_len, _name, (int)type_len, type, (int)value_len, _value); + LOG_DBG("%.*s: name=\"%.*s\", type=\"%.*s\", value=\"%.*s\"", (int)len, line, (int)name_len, _name, (int)type_len, + type, (int)value_len, _value); name = malloc(name_len + 1); memcpy(name, _name, name_len); @@ -165,16 +165,12 @@ process_line(struct module *mod, const char *line, size_t len) tag = tag_new_float(mod, name, v); } - else if ((type_len > 6 && memcmp(type, "range:", 6) == 0) || - (type_len > 9 && memcmp(type, "realtime:", 9) == 0)) - { + else if ((type_len > 6 && memcmp(type, "range:", 6) == 0) || (type_len > 9 && memcmp(type, "realtime:", 9) == 0)) { const char *_start = type + 6; const char *split = memchr(_start, '-', type_len - 6); if (split == NULL || split == _start || (split + 1) - type >= type_len) { - LOG_ERR( - "tag range delimiter ('-') not found in type: %.*s", - (int)type_len, type); + LOG_ERR("tag range delimiter ('-') not found in type: %.*s", (int)type_len, type); goto bad_tag; } @@ -186,9 +182,7 @@ process_line(struct module *mod, const char *line, size_t len) long start = 0; for (size_t i = 0; i < start_len; i++) { if (!(_start[i] >= '0' && _start[i] <= '9')) { - LOG_ERR( - "tag range start is not an integer: %.*s", - (int)start_len, _start); + LOG_ERR("tag range start is not an integer: %.*s", (int)start_len, _start); goto bad_tag; } @@ -199,9 +193,7 @@ process_line(struct module *mod, const char *line, size_t len) long end = 0; for (size_t i = 0; i < end_len; i++) { if (!(_end[i] >= '0' && _end[i] <= '9')) { - LOG_ERR( - "tag range end is not an integer: %.*s", - (int)end_len, _end); + LOG_ERR("tag range end is not an integer: %.*s", (int)end_len, _end); goto bad_tag; } @@ -223,8 +215,7 @@ process_line(struct module *mod, const char *line, size_t len) } if (v < start || v > end) { - LOG_ERR("tag value is outside range: %ld <= %ld <= %ld", - start, v, end); + LOG_ERR("tag value is outside range: %ld <= %ld <= %ld", start, v, end); goto bad_tag; } @@ -326,9 +317,7 @@ data_received(struct module *mod, const char *data, size_t len) process_transaction(mod, transaction_size); assert(m->recv_buf.idx >= transaction_size + 1); - memmove(m->recv_buf.data, - &m->recv_buf.data[transaction_size + 1], - m->recv_buf.idx - (transaction_size + 1)); + memmove(m->recv_buf.data, &m->recv_buf.data[transaction_size + 1], m->recv_buf.idx - (transaction_size + 1)); m->recv_buf.idx -= transaction_size + 1; } @@ -432,11 +421,8 @@ execute_script(struct module *mod) sigemptyset(&mask); const struct sigaction sa = {.sa_handler = SIG_DFL}; - if (sigaction(SIGINT, &sa, NULL) < 0 || - sigaction(SIGTERM, &sa, NULL) < 0 || - sigaction(SIGCHLD, &sa, NULL) < 0 || - sigprocmask(SIG_SETMASK, &mask, NULL) < 0) - { + if (sigaction(SIGINT, &sa, NULL) < 0 || sigaction(SIGTERM, &sa, NULL) < 0 || sigaction(SIGCHLD, &sa, NULL) < 0 + || sigprocmask(SIG_SETMASK, &mask, NULL) < 0) { goto fail; } @@ -452,9 +438,7 @@ execute_script(struct module *mod) if (dev_null < 0) goto fail; - if (dup2(dev_null, STDIN_FILENO) < 0 || - dup2(comm_pipe[1], STDOUT_FILENO) < 0) - { + if (dup2(dev_null, STDIN_FILENO) < 0 || dup2(comm_pipe[1], STDOUT_FILENO) < 0) { goto fail; } @@ -533,9 +517,7 @@ execute_script(struct module *mod) usleep(10000); pid_t waited_pid; - while ((waited_pid = waitpid( - pid, NULL, timeout > 0 ? WNOHANG : 0)) == 0) - { + while ((waited_pid = waitpid(pid, NULL, timeout > 0 ? WNOHANG : 0)) == 0) { struct timeval now; gettimeofday(&now, NULL); @@ -547,7 +529,7 @@ execute_script(struct module *mod) /* Don't spinning */ thrd_yield(); - usleep(100000); /* 100ms */ + usleep(100000); /* 100ms */ } if (waited_pid == pid) { @@ -580,7 +562,7 @@ run(struct module *mod) break; struct timeval now; - if (gettimeofday(&now, NULL) < 0) { + if (gettimeofday(&now, NULL) < 0) { LOG_ERRNO("failed to get current time"); break; } @@ -631,8 +613,7 @@ run(struct module *mod) } static struct module * -script_new(char *path, size_t argc, const char *const argv[static argc], - int poll_interval, struct particle *_content) +script_new(char *path, size_t argc, const char *const argv[static argc], int poll_interval, struct particle *_content) { struct private *m = calloc(1, sizeof(*m)); m->path = path; @@ -665,10 +646,7 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) if (args != NULL) { size_t i = 0; - for (struct yml_list_iter iter = yml_list_iter(args); - iter.node != NULL; - yml_list_next(&iter), i++) - { + for (struct yml_list_iter iter = yml_list_iter(args); iter.node != NULL; yml_list_next(&iter), i++) { argv[i] = yml_value_as_string(iter.node); } } @@ -691,10 +669,8 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) } else path = strdup(yml_path); - return script_new( - path, argc, argv, - poll_interval != NULL ? yml_value_as_int(poll_interval) : 0, - conf_to_particle(c, inherited)); + return script_new(path, argc, argv, poll_interval != NULL ? yml_value_as_int(poll_interval) : 0, + conf_to_particle(c, inherited)); } static bool @@ -709,8 +685,7 @@ conf_verify_path(keychain_t *chain, const struct yml_node *node) const bool is_absolute = path[0] == '/'; if (!is_tilde && !is_absolute) { - LOG_ERR("%s: path must either be absolute, or begin with '~/'", - conf_err_prefix(chain, node)); + LOG_ERR("%s: path must either be absolute, or begin with '~/'", conf_err_prefix(chain, node)); return false; } @@ -730,8 +705,7 @@ conf_verify_poll_interval(keychain_t *chain, const struct yml_node *node) return false; if (yml_value_as_int(node) < min_poll_interval) { - LOG_ERR("%s: interval value cannot be less than %ldms", - conf_err_prefix(chain, node), min_poll_interval); + LOG_ERR("%s: interval value cannot be less than %ldms", conf_err_prefix(chain, node), min_poll_interval); return false; } diff --git a/modules/sway-xkb.c b/modules/sway-xkb.c index 96d8388..1507241 100644 --- a/modules/sway-xkb.c +++ b/modules/sway-xkb.c @@ -3,15 +3,15 @@ #define LOG_MODULE "sway-xkb" #define LOG_ENABLE_DBG 0 -#include "../log.h" #include "../bar/bar.h" #include "../config-verify.h" #include "../config.h" +#include "../log.h" #include "../particles/dynlist.h" #include "../plugin.h" -#include "i3-ipc.h" #include "i3-common.h" +#include "i3-ipc.h" #define max(x, y) ((x) > (y) ? (x) : (y)) @@ -21,7 +21,8 @@ struct input { char *layout; }; -struct private { +struct private +{ struct particle *template; int left_spacing; int right_spacing; @@ -89,8 +90,7 @@ content(struct module *mod) } mtx_unlock(&mod->lock); - return dynlist_exposable_new( - particles, m->num_existing_inputs, m->left_spacing, m->right_spacing); + return dynlist_exposable_new(particles, m->num_existing_inputs, m->left_spacing, m->right_spacing); } static bool @@ -121,8 +121,7 @@ handle_input_reply(int sock, int type, const struct json_object *json, void *_mo struct input *input = NULL; for (size_t i = 0; i < m->num_inputs; i++) { struct input *maybe_input = &m->inputs[i]; - if (strcmp(maybe_input->identifier, id) == 0 && !maybe_input->exists) - { + if (strcmp(maybe_input->identifier, id) == 0 && !maybe_input->exists) { input = maybe_input; LOG_DBG("adding: %s", id); @@ -142,8 +141,7 @@ handle_input_reply(int sock, int type, const struct json_object *json, void *_mo /* Get current/active layout */ struct json_object *layout; - if (!json_object_object_get_ex( - obj, "xkb_active_layout_name", &layout)) + if (!json_object_object_get_ex(obj, "xkb_active_layout_name", &layout)) return false; const char *new_layout_str = json_object_get_string(layout); @@ -240,8 +238,7 @@ handle_input_event(int sock, int type, const struct json_object *json, void *_mo /* Get current/active layout */ struct json_object *layout; - if (!json_object_object_get_ex( - obj, "xkb_active_layout_name", &layout)) + if (!json_object_object_get_ex(obj, "xkb_active_layout_name", &layout)) return false; const char *new_layout_str = json_object_get_string(layout); @@ -309,8 +306,8 @@ run(struct module *mod) } static struct module * -sway_xkb_new(struct particle *template, const char *identifiers[], - size_t num_identifiers, int left_spacing, int right_spacing) +sway_xkb_new(struct particle *template, const char *identifiers[], size_t num_identifiers, int left_spacing, + int right_spacing) { struct private *m = calloc(1, sizeof(*m)); m->template = template; @@ -343,40 +340,32 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) const struct yml_node *left_spacing = yml_get_value(node, "left-spacing"); const struct yml_node *right_spacing = yml_get_value(node, "right-spacing"); - int left = spacing != NULL ? yml_value_as_int(spacing) : - left_spacing != NULL ? yml_value_as_int(left_spacing) : 0; - int right = spacing != NULL ? yml_value_as_int(spacing) : - right_spacing != NULL ? yml_value_as_int(right_spacing) : 0; + int left = spacing != NULL ? yml_value_as_int(spacing) : left_spacing != NULL ? yml_value_as_int(left_spacing) : 0; + int right = spacing != NULL ? yml_value_as_int(spacing) + : right_spacing != NULL ? yml_value_as_int(right_spacing) + : 0; const struct yml_node *ids = yml_get_value(node, "identifiers"); const size_t num_ids = yml_list_length(ids); const char *identifiers[num_ids]; size_t i = 0; - for (struct yml_list_iter it = yml_list_iter(ids); - it.node != NULL; - yml_list_next(&it), i++) - { + for (struct yml_list_iter it = yml_list_iter(ids); it.node != NULL; yml_list_next(&it), i++) { identifiers[i] = yml_value_as_string(it.node); } - return sway_xkb_new( - conf_to_particle(c, inherited), identifiers, num_ids, left, right); + return sway_xkb_new(conf_to_particle(c, inherited), identifiers, num_ids, left, right); } static bool verify_identifiers(keychain_t *chain, const struct yml_node *node) { if (!yml_is_list(node)) { - LOG_ERR("%s: identifiers must be a list of strings", - conf_err_prefix(chain, node)); + LOG_ERR("%s: identifiers must be a list of strings", conf_err_prefix(chain, node)); return false; } - for (struct yml_list_iter it = yml_list_iter(node); - it.node != NULL; - yml_list_next(&it)) - { + for (struct yml_list_iter it = yml_list_iter(node); it.node != NULL; yml_list_next(&it)) { if (!conf_verify_string(chain, it.node)) return false; } @@ -404,5 +393,5 @@ const struct module_iface module_sway_xkb_iface = { }; #if defined(CORE_PLUGINS_AS_SHARED_LIBRARIES) -extern const struct module_iface iface __attribute__((weak, alias("module_sway_xkb_iface"))) ; +extern const struct module_iface iface __attribute__((weak, alias("module_sway_xkb_iface"))); #endif diff --git a/modules/xkb.c b/modules/xkb.c index edd0afe..e8e3c91 100644 --- a/modules/xkb.c +++ b/modules/xkb.c @@ -1,7 +1,7 @@ -#include -#include #include #include +#include +#include #include @@ -10,10 +10,10 @@ #define LOG_MODULE "xkb" #define LOG_ENABLE_DBG 0 -#include "../log.h" #include "../bar/bar.h" -#include "../config.h" #include "../config-verify.h" +#include "../config.h" +#include "../log.h" #include "../plugin.h" #include "../xcb.h" @@ -32,7 +32,8 @@ struct indicators { char **names; }; -struct private { +struct private +{ struct particle *label; struct indicators indicators; struct layouts layouts; @@ -117,10 +118,8 @@ xkb_enable(xcb_connection_t *conn) { xcb_generic_error_t *err; - xcb_xkb_use_extension_cookie_t cookie = xcb_xkb_use_extension( - conn, XCB_XKB_MAJOR_VERSION, XCB_XKB_MINOR_VERSION); - xcb_xkb_use_extension_reply_t *reply = xcb_xkb_use_extension_reply( - conn, cookie, &err); + xcb_xkb_use_extension_cookie_t cookie = xcb_xkb_use_extension(conn, XCB_XKB_MAJOR_VERSION, XCB_XKB_MINOR_VERSION); + xcb_xkb_use_extension_reply_t *reply = xcb_xkb_use_extension_reply(conn, cookie, &err); if (err != NULL) { LOG_ERR("failed to query for XKB extension: %s", xcb_error(err)); @@ -142,8 +141,7 @@ xkb_enable(xcb_connection_t *conn) static int get_xkb_event_base(xcb_connection_t *conn) { - const struct xcb_query_extension_reply_t *reply = xcb_get_extension_data( - conn, &xcb_xkb_id); + const struct xcb_query_extension_reply_t *reply = xcb_get_extension_data(conn, &xcb_xkb_id); if (reply == NULL) { LOG_ERR("failed to get XKB extension data"); @@ -159,19 +157,14 @@ get_xkb_event_base(xcb_connection_t *conn) } static bool -get_layouts_and_indicators(xcb_connection_t *conn, struct layouts *layouts, - struct indicators *indicators) +get_layouts_and_indicators(xcb_connection_t *conn, struct layouts *layouts, struct indicators *indicators) { xcb_generic_error_t *err; - xcb_xkb_get_names_cookie_t cookie = xcb_xkb_get_names( - conn, - XCB_XKB_ID_USE_CORE_KBD, - XCB_XKB_NAME_DETAIL_GROUP_NAMES | - XCB_XKB_NAME_DETAIL_SYMBOLS | - XCB_XKB_NAME_DETAIL_INDICATOR_NAMES); + xcb_xkb_get_names_cookie_t cookie = xcb_xkb_get_names(conn, XCB_XKB_ID_USE_CORE_KBD, + XCB_XKB_NAME_DETAIL_GROUP_NAMES | XCB_XKB_NAME_DETAIL_SYMBOLS + | XCB_XKB_NAME_DETAIL_INDICATOR_NAMES); - xcb_xkb_get_names_reply_t *reply = xcb_xkb_get_names_reply( - conn, cookie, &err); + xcb_xkb_get_names_reply_t *reply = xcb_xkb_get_names_reply(conn, cookie, &err); if (err != NULL) { LOG_ERR("failed to get layouts and indicators: %s", xcb_error(err)); @@ -181,22 +174,18 @@ get_layouts_and_indicators(xcb_connection_t *conn, struct layouts *layouts, xcb_xkb_get_names_value_list_t vlist; void *buf = xcb_xkb_get_names_value_list(reply); - xcb_xkb_get_names_value_list_unpack( - buf, reply->nTypes, reply->indicators, reply->virtualMods, - reply->groupNames, reply->nKeys, reply->nKeyAliases, - reply->nRadioGroups, reply->which, &vlist); + xcb_xkb_get_names_value_list_unpack(buf, reply->nTypes, reply->indicators, reply->virtualMods, reply->groupNames, + reply->nKeys, reply->nKeyAliases, reply->nRadioGroups, reply->which, &vlist); /* Number of groups (aka layouts) */ layouts->count = xcb_xkb_get_names_value_list_groups_length(reply, &vlist); layouts->layouts = calloc(layouts->count, sizeof(layouts->layouts[0])); /* Number of indicators */ - indicators->count = xcb_xkb_get_names_value_list_indicator_names_length( - reply, &vlist); + indicators->count = xcb_xkb_get_names_value_list_indicator_names_length(reply, &vlist); indicators->names = calloc(indicators->count, sizeof(indicators->names[0])); - xcb_get_atom_name_cookie_t symbols_name_cookie = xcb_get_atom_name( - conn, vlist.symbolsName); + xcb_get_atom_name_cookie_t symbols_name_cookie = xcb_get_atom_name(conn, vlist.symbolsName); xcb_get_atom_name_cookie_t group_name_cookies[layouts->count]; for (size_t i = 0; i < layouts->count; i++) @@ -209,17 +198,14 @@ get_layouts_and_indicators(xcb_connection_t *conn, struct layouts *layouts, char *symbols = NULL; /* Get layout short names (e.g. "us") */ - xcb_get_atom_name_reply_t *atom_name = xcb_get_atom_name_reply( - conn, symbols_name_cookie, &err); + xcb_get_atom_name_reply_t *atom_name = xcb_get_atom_name_reply(conn, symbols_name_cookie, &err); if (err != NULL) { LOG_ERR("failed to get 'symbols' atom name: %s", xcb_error(err)); free(err); goto err; } - symbols = strndup( - xcb_get_atom_name_name(atom_name), - xcb_get_atom_name_name_length(atom_name)); + symbols = strndup(xcb_get_atom_name_name(atom_name), xcb_get_atom_name_name_length(atom_name)); LOG_DBG("symbols: %s", symbols); free(atom_name); @@ -232,9 +218,7 @@ get_layouts_and_indicators(xcb_connection_t *conn, struct layouts *layouts, goto err; } - layouts->layouts[i].name = strndup( - xcb_get_atom_name_name(atom_name), - xcb_get_atom_name_name_length(atom_name)); + layouts->layouts[i].name = strndup(xcb_get_atom_name_name(atom_name), xcb_get_atom_name_name_length(atom_name)); LOG_DBG("layout #%zd: long name: %s", i, layouts->layouts[i].name); free(atom_name); @@ -249,9 +233,7 @@ get_layouts_and_indicators(xcb_connection_t *conn, struct layouts *layouts, goto err; } - indicators->names[i] = strndup( - xcb_get_atom_name_name(atom_name), - xcb_get_atom_name_name_length(atom_name)); + indicators->names[i] = strndup(xcb_get_atom_name_name(atom_name), xcb_get_atom_name_name_length(atom_name)); LOG_DBG("indicator #%zd: %s", i, indicators->names[i]); free(atom_name); @@ -259,8 +241,7 @@ get_layouts_and_indicators(xcb_connection_t *conn, struct layouts *layouts, /* e.g. pc+us+inet(evdev)+group(..) */ size_t layout_idx = 0; - for (char *tok_ctx = NULL, *tok = strtok_r(symbols, "+", &tok_ctx); - tok != NULL; + for (char *tok_ctx = NULL, *tok = strtok_r(symbols, "+", &tok_ctx); tok != NULL; tok = strtok_r(NULL, "+", &tok_ctx)) { char *fname = strtok(tok, "()"); @@ -279,8 +260,7 @@ get_layouts_and_indicators(xcb_connection_t *conn, struct layouts *layouts, continue; if (layout_idx >= layouts->count) { - LOG_ERR("layout vs group name count mismatch: %zd > %zd", - layout_idx + 1, layouts->count); + LOG_ERR("layout vs group name count mismatch: %zd > %zd", layout_idx + 1, layouts->count); goto err; } @@ -290,8 +270,7 @@ get_layouts_and_indicators(xcb_connection_t *conn, struct layouts *layouts, } if (layout_idx != layouts->count) { - LOG_ERR("layout vs group name count mismatch: %zd != %zd", - layout_idx, layouts->count); + LOG_ERR("layout vs group name count mismatch: %zd != %zd", layout_idx, layouts->count); goto err; } @@ -312,10 +291,8 @@ get_current_layout(xcb_connection_t *conn) { xcb_generic_error_t *err; - xcb_xkb_get_state_cookie_t cookie = xcb_xkb_get_state( - conn, XCB_XKB_ID_USE_CORE_KBD); - xcb_xkb_get_state_reply_t *reply = xcb_xkb_get_state_reply( - conn, cookie, &err); + xcb_xkb_get_state_cookie_t cookie = xcb_xkb_get_state(conn, XCB_XKB_ID_USE_CORE_KBD); + xcb_xkb_get_state_reply_t *reply = xcb_xkb_get_state_reply(conn, cookie, &err); if (err != NULL) { LOG_ERR("failed to get XKB state: %s", xcb_error(err)); @@ -332,10 +309,8 @@ static uint32_t get_indicator_state(xcb_connection_t *conn) { xcb_generic_error_t *err; - xcb_xkb_get_indicator_state_cookie_t cookie = xcb_xkb_get_indicator_state( - conn, XCB_XKB_ID_USE_CORE_KBD); - xcb_xkb_get_indicator_state_reply_t *reply = xcb_xkb_get_indicator_state_reply( - conn, cookie, &err); + xcb_xkb_get_indicator_state_cookie_t cookie = xcb_xkb_get_indicator_state(conn, XCB_XKB_ID_USE_CORE_KBD); + xcb_xkb_get_indicator_state_reply_t *reply = xcb_xkb_get_indicator_state_reply(conn, cookie, &err); if (err != NULL) { LOG_ERR("failed to get indicator state: %s", xcb_error(err)); @@ -353,23 +328,14 @@ get_indicator_state(xcb_connection_t *conn) static bool register_for_events(xcb_connection_t *conn) { - xcb_void_cookie_t cookie = xcb_xkb_select_events_checked( - conn, - XCB_XKB_ID_USE_CORE_KBD, - ( - XCB_XKB_EVENT_TYPE_NEW_KEYBOARD_NOTIFY | - XCB_XKB_EVENT_TYPE_STATE_NOTIFY | - XCB_XKB_EVENT_TYPE_MAP_NOTIFY | - XCB_XKB_EVENT_TYPE_INDICATOR_STATE_NOTIFY - ), - 0, - ( - XCB_XKB_EVENT_TYPE_NEW_KEYBOARD_NOTIFY | - XCB_XKB_EVENT_TYPE_STATE_NOTIFY | - XCB_XKB_EVENT_TYPE_MAP_NOTIFY | - XCB_XKB_EVENT_TYPE_INDICATOR_STATE_NOTIFY - ), - 0, 0, NULL); + xcb_void_cookie_t cookie + = xcb_xkb_select_events_checked(conn, XCB_XKB_ID_USE_CORE_KBD, + (XCB_XKB_EVENT_TYPE_NEW_KEYBOARD_NOTIFY | XCB_XKB_EVENT_TYPE_STATE_NOTIFY + | XCB_XKB_EVENT_TYPE_MAP_NOTIFY | XCB_XKB_EVENT_TYPE_INDICATOR_STATE_NOTIFY), + 0, + (XCB_XKB_EVENT_TYPE_NEW_KEYBOARD_NOTIFY | XCB_XKB_EVENT_TYPE_STATE_NOTIFY + | XCB_XKB_EVENT_TYPE_MAP_NOTIFY | XCB_XKB_EVENT_TYPE_INDICATOR_STATE_NOTIFY), + 0, 0, NULL); xcb_generic_error_t *err = xcb_request_check(conn, cookie); if (err != NULL) { @@ -393,10 +359,7 @@ event_loop(struct module *mod, xcb_connection_t *conn, int xkb_event_base) assert(xcb_fd >= 0); while (!has_error) { - struct pollfd pfds[] = { - {.fd = mod->abort_fd, .events = POLLIN }, - {.fd = xcb_fd, .events = POLLIN | POLLHUP } - }; + struct pollfd pfds[] = {{.fd = mod->abort_fd, .events = POLLIN}, {.fd = xcb_fd, .events = POLLIN | POLLHUP}}; /* Use poll() since xcb_wait_for_events() doesn't return on signals */ if (poll(pfds, sizeof(pfds) / sizeof(pfds[0]), -1) < 0) { @@ -425,9 +388,7 @@ event_loop(struct module *mod, xcb_connection_t *conn, int xkb_event_base) * not for long though... */ - for (xcb_generic_event_t *_evt = xcb_wait_for_event(conn); - _evt != NULL; - _evt = xcb_poll_for_event(conn)) { + for (xcb_generic_event_t *_evt = xcb_wait_for_event(conn); _evt != NULL; _evt = xcb_poll_for_event(conn)) { if (_evt->response_type != xkb_event_base) { LOG_WARN("non-XKB event ignored: %d", _evt->response_type); @@ -435,7 +396,7 @@ event_loop(struct module *mod, xcb_connection_t *conn, int xkb_event_base) continue; } - switch(_evt->pad0) { + switch (_evt->pad0) { default: LOG_WARN("unimplemented XKB event: %d", _evt->pad0); break; @@ -463,7 +424,7 @@ event_loop(struct module *mod, xcb_connection_t *conn, int xkb_event_base) mtx_unlock(&mod->lock); bar->refresh(bar); } else { - /* Can happen while transitioning to a new map */ + /* Can happen while transitioning to a new map */ free_layouts(layouts); free_indicators(indicators); } @@ -472,8 +433,7 @@ event_loop(struct module *mod, xcb_connection_t *conn, int xkb_event_base) } case XCB_XKB_STATE_NOTIFY: { - const xcb_xkb_state_notify_event_t *evt = - (const xcb_xkb_state_notify_event_t *)_evt; + const xcb_xkb_state_notify_event_t *evt = (const xcb_xkb_state_notify_event_t *)_evt; if (evt->changed & XCB_XKB_STATE_PART_GROUP_STATE) { mtx_lock(&mod->lock); @@ -490,8 +450,8 @@ event_loop(struct module *mod, xcb_connection_t *conn, int xkb_event_base) break; case XCB_XKB_INDICATOR_STATE_NOTIFY: { - const xcb_xkb_indicator_state_notify_event_t *evt = - (const xcb_xkb_indicator_state_notify_event_t *)_evt; + const xcb_xkb_indicator_state_notify_event_t *evt + = (const xcb_xkb_indicator_state_notify_event_t *)_evt; #if 0 size_t idx = __builtin_ctz(evt->stateChanged); @@ -508,8 +468,7 @@ event_loop(struct module *mod, xcb_connection_t *conn, int xkb_event_base) continue; bool enabled = (evt->state >> i) & 1; - LOG_DBG("%s: %s", m->indicators.names[i], - enabled ? "enabled" : "disabled"); + LOG_DBG("%s: %s", m->indicators.names[i], enabled ? "enabled" : "disabled"); const char *name = m->indicators.names[i]; bool is_caps = strcasecmp(name, "caps lock") == 0; @@ -609,18 +568,12 @@ talk_to_xkb(struct module *mod, xcb_connection_t *conn) size_t idx = 0; for (size_t i = 0; i < layouts.count; i++) { - idx += snprintf(&buf[idx], sizeof(buf) - idx, "%s%s (%s)%s", - i == m->current ? "*" : "", - layouts.layouts[i].name, - layouts.layouts[i].symbol, - i + 1 < layouts.count ? ", " : ""); + idx += snprintf(&buf[idx], sizeof(buf) - idx, "%s%s (%s)%s", i == m->current ? "*" : "", + layouts.layouts[i].name, layouts.layouts[i].symbol, i + 1 < layouts.count ? ", " : ""); } - LOG_INFO("layouts: %s, caps-lock:%s, num-lock:%s, scroll-lock:%s", - buf, - caps_lock ? "on" : "off", - num_lock ? "on" : "off", - scroll_lock ? "on" : "off"); + LOG_INFO("layouts: %s, caps-lock:%s, num-lock:%s, scroll-lock:%s", buf, caps_lock ? "on" : "off", + num_lock ? "on" : "off", scroll_lock ? "on" : "off"); } mtx_lock(&mod->lock); diff --git a/modules/xwindow.c b/modules/xwindow.c index e53114e..ffae527 100644 --- a/modules/xwindow.c +++ b/modules/xwindow.c @@ -1,29 +1,30 @@ +#include +#include +#include #include #include #include -#include -#include -#include #include -#include +#include -#include #include #include +#include #include #include #include #define LOG_MODULE "xwindow" -#include "../log.h" #include "../bar/bar.h" -#include "../config.h" #include "../config-verify.h" +#include "../config.h" +#include "../log.h" #include "../plugin.h" #include "../xcb.h" -struct private { +struct private +{ /* Accessed from bar thread only */ struct particle *label; @@ -48,23 +49,19 @@ static void update_active_window(struct private *m) { if (m->active_win != 0) { - xcb_void_cookie_t c = xcb_change_window_attributes_checked( - m->conn, m->active_win, XCB_CW_EVENT_MASK, - (const uint32_t []){XCB_EVENT_MASK_NO_EVENT}); + xcb_void_cookie_t c = xcb_change_window_attributes_checked(m->conn, m->active_win, XCB_CW_EVENT_MASK, + (const uint32_t[]){XCB_EVENT_MASK_NO_EVENT}); xcb_generic_error_t *e = xcb_request_check(m->conn, c); if (e != NULL) { - LOG_DBG( - "failed to de-register events on previous active window: %s", - xcb_error(e)); + LOG_DBG("failed to de-register events on previous active window: %s", xcb_error(e)); free(e); } m->active_win = 0; } - xcb_get_property_cookie_t c = xcb_get_property( - m->conn, 0, m->root_win, _NET_ACTIVE_WINDOW, XCB_ATOM_WINDOW, 0, 32); + xcb_get_property_cookie_t c = xcb_get_property(m->conn, 0, m->root_win, _NET_ACTIVE_WINDOW, XCB_ATOM_WINDOW, 0, 32); xcb_generic_error_t *e; xcb_get_property_reply_t *r = xcb_get_property_reply(m->conn, c, &e); @@ -86,9 +83,8 @@ update_active_window(struct private *m) free(r); if (m->active_win != 0) { - xcb_change_window_attributes( - m->conn, m->active_win, XCB_CW_EVENT_MASK, - (const uint32_t []){XCB_EVENT_MASK_PROPERTY_CHANGE}); + xcb_change_window_attributes(m->conn, m->active_win, XCB_CW_EVENT_MASK, + (const uint32_t[]){XCB_EVENT_MASK_PROPERTY_CHANGE}); } } @@ -105,8 +101,7 @@ update_application(struct module *mod) if (m->active_win == 0) return; - xcb_get_property_cookie_t c = xcb_get_property( - m->conn, 0, m->active_win, _NET_WM_PID, XCB_ATOM_CARDINAL, 0, 32); + xcb_get_property_cookie_t c = xcb_get_property(m->conn, 0, m->active_win, _NET_WM_PID, XCB_ATOM_CARDINAL, 0, 32); xcb_generic_error_t *e; xcb_get_property_reply_t *r = xcb_get_property_reply(m->conn, c, &e); @@ -164,12 +159,11 @@ update_title(struct module *mod) if (m->active_win == 0) return; - xcb_get_property_cookie_t c1 = xcb_get_property( - m->conn, 0, m->active_win, _NET_WM_VISIBLE_NAME, UTF8_STRING, 0, 1000); - xcb_get_property_cookie_t c2 = xcb_get_property( - m->conn, 0, m->active_win, _NET_WM_NAME, UTF8_STRING, 0, 1000); - xcb_get_property_cookie_t c3 = xcb_get_property( - m->conn, 0, m->active_win, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 0, 1000); + xcb_get_property_cookie_t c1 + = xcb_get_property(m->conn, 0, m->active_win, _NET_WM_VISIBLE_NAME, UTF8_STRING, 0, 1000); + xcb_get_property_cookie_t c2 = xcb_get_property(m->conn, 0, m->active_win, _NET_WM_NAME, UTF8_STRING, 0, 1000); + xcb_get_property_cookie_t c3 + = xcb_get_property(m->conn, 0, m->active_win, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 0, 1000); xcb_generic_error_t *e1, *e2, *e3; xcb_get_property_reply_t *r1 = xcb_get_property_reply(m->conn, c1, &e1); @@ -207,7 +201,7 @@ update_title(struct module *mod) free(r1); free(r2); free(r3); - } +} static int run(struct module *mod) @@ -227,19 +221,16 @@ run(struct module *mod) /* Need a window(?) to be able to process events */ m->monitor_win = xcb_generate_id(m->conn); - xcb_create_window(m->conn, screen->root_depth, m->monitor_win, screen->root, - -1, -1, 1, 1, - 0, - XCB_WINDOW_CLASS_INPUT_OUTPUT, screen->root_visual, - XCB_CW_OVERRIDE_REDIRECT, (const uint32_t []){1}); + xcb_create_window(m->conn, screen->root_depth, m->monitor_win, screen->root, -1, -1, 1, 1, 0, + XCB_WINDOW_CLASS_INPUT_OUTPUT, screen->root_visual, XCB_CW_OVERRIDE_REDIRECT, + (const uint32_t[]){1}); xcb_map_window(m->conn, m->monitor_win); /* Register for property changes on root window. This allows us to * catch e.g. window switches etc */ - xcb_change_window_attributes( - m->conn, screen->root, XCB_CW_EVENT_MASK, - (const uint32_t []){XCB_EVENT_MASK_PROPERTY_CHANGE}); + xcb_change_window_attributes(m->conn, screen->root, XCB_CW_EVENT_MASK, + (const uint32_t[]){XCB_EVENT_MASK_PROPERTY_CHANGE}); xcb_flush(m->conn); @@ -252,8 +243,7 @@ run(struct module *mod) int xcb_fd = xcb_get_file_descriptor(m->conn); while (true) { - struct pollfd fds[] = {{.fd = mod->abort_fd, .events = POLLIN}, - {.fd = xcb_fd, .events = POLLIN}}; + struct pollfd fds[] = {{.fd = mod->abort_fd, .events = POLLIN}, {.fd = xcb_fd, .events = POLLIN}}; if (poll(fds, sizeof(fds) / sizeof(fds[0]), -1) < 0) { if (errno == EINTR) continue; @@ -267,10 +257,7 @@ run(struct module *mod) break; } - for (xcb_generic_event_t *_e = xcb_wait_for_event(m->conn); - _e != NULL; - _e = xcb_poll_for_event(m->conn)) - { + for (xcb_generic_event_t *_e = xcb_wait_for_event(m->conn); _e != NULL; _e = xcb_poll_for_event(m->conn)) { switch (XCB_EVENT_RESPONSE_TYPE(_e)) { case 0: LOG_ERR("XCB: %s", xcb_error((const xcb_generic_error_t *)_e)); @@ -278,18 +265,13 @@ run(struct module *mod) case XCB_PROPERTY_NOTIFY: { xcb_property_notify_event_t *e = (xcb_property_notify_event_t *)_e; - if (e->atom == _NET_ACTIVE_WINDOW || - e->atom == _NET_CURRENT_DESKTOP) - { + if (e->atom == _NET_ACTIVE_WINDOW || e->atom == _NET_CURRENT_DESKTOP) { /* Active desktop and/or window changed */ update_active_window(m); update_application(mod); update_title(mod); mod->bar->refresh(mod->bar); - } else if (e->atom == _NET_WM_VISIBLE_NAME || - e->atom == _NET_WM_NAME || - e->atom == XCB_ATOM_WM_NAME) - { + } else if (e->atom == _NET_WM_VISIBLE_NAME || e->atom == _NET_WM_NAME || e->atom == XCB_ATOM_WM_NAME) { assert(e->window == m->active_win); update_title(mod); mod->bar->refresh(mod->bar); diff --git a/particle.c b/particle.c index 2035d9a..f35b5d1 100644 --- a/particle.c +++ b/particle.c @@ -1,21 +1,21 @@ #include "particle.h" -#include -#include -#include #include #include #include +#include +#include +#include -#include -#include -#include #include +#include +#include +#include #define LOG_MODULE "particle" #define LOG_ENABLE_DBG 0 -#include "log.h" #include "bar/bar.h" +#include "log.h" void particle_default_destroy(struct particle *particle) @@ -29,10 +29,8 @@ particle_default_destroy(struct particle *particle) } struct particle * -particle_common_new(int left_margin, int right_margin, - char **on_click_templates, - struct fcft_font *font, enum font_shaping font_shaping, - pixman_color_t foreground, struct deco *deco) +particle_common_new(int left_margin, int right_margin, char **on_click_templates, struct fcft_font *font, + enum font_shaping font_shaping, pixman_color_t foreground, struct deco *deco) { struct particle *p = calloc(1, sizeof(*p)); p->left_margin = left_margin; @@ -63,13 +61,11 @@ exposable_default_destroy(struct exposable *exposable) } void -exposable_render_deco(const struct exposable *exposable, - pixman_image_t *pix, int x, int y, int height) +exposable_render_deco(const struct exposable *exposable, pixman_image_t *pix, int x, int y, int height) { const struct deco *deco = exposable->particle->deco; if (deco != NULL) deco->expose(deco, pix, x, y, exposable->width, height); - } static bool @@ -114,9 +110,7 @@ tokenize_cmdline(char *cmdline, char ***argv) return false; } - if (!push_argv(argv, &argv_size, p, &idx) || - !push_argv(argv, &argv_size, NULL, &idx)) - { + if (!push_argv(argv, &argv_size, p, &idx) || !push_argv(argv, &argv_size, NULL, &idx)) { goto err; } else return true; @@ -152,8 +146,7 @@ err: } void -exposable_default_on_mouse(struct exposable *exposable, struct bar *bar, - enum mouse_event event, enum mouse_button btn, +exposable_default_on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event, enum mouse_button btn, int x, int y) { #if defined(LOG_ENABLE_DBG) && LOG_ENABLE_DBG @@ -168,17 +161,13 @@ exposable_default_on_mouse(struct exposable *exposable, struct bar *bar, [MOUSE_BTN_PREVIOUS] = "previous", [MOUSE_BTN_NEXT] = "next", }; - LOG_DBG("on_mouse: exposable=%p, event=%s, btn=%s, x=%d, y=%d (on-click=%s)", - exposable, event == ON_MOUSE_MOTION ? "motion" : "click", - button_name[btn], x, y, exposable->on_click[btn]); + LOG_DBG("on_mouse: exposable=%p, event=%s, btn=%s, x=%d, y=%d (on-click=%s)", exposable, + event == ON_MOUSE_MOTION ? "motion" : "click", button_name[btn], x, y, exposable->on_click[btn]); #endif /* If we have a handler, change cursor to a hand */ - const char *cursor = - (exposable->particle != NULL && - exposable->particle->have_on_click_template) - ? "hand2" - : "left_ptr"; + const char *cursor + = (exposable->particle != NULL && exposable->particle->have_on_click_template) ? "hand2" : "left_ptr"; bar->set_cursor(bar, cursor); /* If this is a mouse click, and we have a handler, execute it */ @@ -246,7 +235,7 @@ exposable_default_on_mouse(struct exposable *exposable, struct bar *bar, case 0: /* Child */ - close(pipe_fds[0]); /* Close read end */ + close(pipe_fds[0]); /* Close read end */ LOG_DBG("executing on-click handler: %s", cmd); @@ -254,11 +243,8 @@ exposable_default_on_mouse(struct exposable *exposable, struct bar *bar, sigemptyset(&mask); const struct sigaction sa = {.sa_handler = SIG_DFL}; - if (sigaction(SIGINT, &sa, NULL) < 0 || - sigaction(SIGTERM, &sa, NULL) < 0 || - sigaction(SIGCHLD, &sa, NULL) < 0 || - sigprocmask(SIG_SETMASK, &mask, NULL) < 0) - { + if (sigaction(SIGINT, &sa, NULL) < 0 || sigaction(SIGTERM, &sa, NULL) < 0 + || sigaction(SIGCHLD, &sa, NULL) < 0 || sigprocmask(SIG_SETMASK, &mask, NULL) < 0) { goto fail; } @@ -271,10 +257,8 @@ exposable_default_on_mouse(struct exposable *exposable, struct bar *bar, goto fail; } - if (dup2(dev_null_r, STDIN_FILENO) == -1 || - dup2(dev_null_w, STDOUT_FILENO) == -1 || - dup2(dev_null_w, STDERR_FILENO) == -1) - { + if (dup2(dev_null_r, STDIN_FILENO) == -1 || dup2(dev_null_w, STDOUT_FILENO) == -1 + || dup2(dev_null_w, STDERR_FILENO) == -1) { LOG_ERRNO("failed to redirect stdin/stdout/stderr"); goto fail; } @@ -316,10 +300,7 @@ exposable_common_new(const struct particle *particle, const struct tag_set *tags exposable->particle = particle; if (particle != NULL && particle->have_on_click_template) { - tags_expand_templates( - exposable->on_click, - (const char **)particle->on_click_templates, - MOUSE_BTN_COUNT, tags); + tags_expand_templates(exposable->on_click, (const char **)particle->on_click_templates, MOUSE_BTN_COUNT, tags); } exposable->destroy = &exposable_default_destroy; exposable->on_mouse = &exposable_default_on_mouse; diff --git a/particle.h b/particle.h index a2b55a8..bc8648d 100644 --- a/particle.h +++ b/particle.h @@ -42,11 +42,9 @@ struct particle { struct deco *deco; void (*destroy)(struct particle *particle); - struct exposable *(*instantiate)(const struct particle *particle, - const struct tag_set *tags); + struct exposable *(*instantiate)(const struct particle *particle, const struct tag_set *tags); }; - struct exposable { const struct particle *particle; void *private; @@ -56,38 +54,31 @@ struct exposable { void (*destroy)(struct exposable *exposable); int (*begin_expose)(struct exposable *exposable); - void (*expose)(const struct exposable *exposable, pixman_image_t *pix, - int x, int y, int height); + void (*expose)(const struct exposable *exposable, pixman_image_t *pix, int x, int y, int height); - void (*on_mouse)(struct exposable *exposable, struct bar *bar, - enum mouse_event event, enum mouse_button btn, int x, int y); + void (*on_mouse)(struct exposable *exposable, struct bar *bar, enum mouse_event event, enum mouse_button btn, int x, + int y); }; -struct particle *particle_common_new( - int left_margin, int right_margin, char *on_click_templates[], - struct fcft_font *font, enum font_shaping font_shaping, - pixman_color_t foreground, struct deco *deco); +struct particle *particle_common_new(int left_margin, int right_margin, char *on_click_templates[], + struct fcft_font *font, enum font_shaping font_shaping, pixman_color_t foreground, + struct deco *deco); void particle_default_destroy(struct particle *particle); -struct exposable *exposable_common_new( - const struct particle *particle, const struct tag_set *tags); +struct exposable *exposable_common_new(const struct particle *particle, const struct tag_set *tags); void exposable_default_destroy(struct exposable *exposable); -void exposable_render_deco( - const struct exposable *exposable, pixman_image_t *pix, int x, int y, int height); +void exposable_render_deco(const struct exposable *exposable, pixman_image_t *pix, int x, int y, int height); -void exposable_default_on_mouse( - struct exposable *exposable, struct bar *bar, - enum mouse_event event, enum mouse_button btn, int x, int y); +void exposable_default_on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event, + enum mouse_button btn, int x, int y); /* List of attributes *all* particles implement */ -#define PARTICLE_COMMON_ATTRS \ - {"margin", false, &conf_verify_unsigned}, \ - {"left-margin", false, &conf_verify_unsigned}, \ - {"right-margin", false, &conf_verify_unsigned}, \ - {"on-click", false, &conf_verify_on_click}, \ - {"font", false, &conf_verify_font}, \ - {"font-shaping", false, &conf_verify_font_shaping}, \ - {"foreground", false, &conf_verify_color}, \ - {"deco", false, &conf_verify_decoration}, \ - {NULL, false, NULL} +#define PARTICLE_COMMON_ATTRS \ + {"margin", false, &conf_verify_unsigned}, {"left-margin", false, &conf_verify_unsigned}, \ + {"right-margin", false, &conf_verify_unsigned}, {"on-click", false, &conf_verify_on_click}, \ + {"font", false, &conf_verify_font}, {"font-shaping", false, &conf_verify_font_shaping}, \ + {"foreground", false, &conf_verify_color}, {"deco", false, &conf_verify_decoration}, \ + { \ + NULL, false, NULL \ + } diff --git a/particles/dynlist.c b/particles/dynlist.c index 85cad7b..fcd0066 100644 --- a/particles/dynlist.c +++ b/particles/dynlist.c @@ -1,13 +1,14 @@ #include "dynlist.h" -#include #include +#include #define LOG_MODULE "dynlist" #include "../log.h" #include "../particle.h" -struct private { +struct private +{ int left_spacing; int right_spacing; @@ -77,8 +78,7 @@ dynlist_expose(const struct exposable *exposable, pixman_image_t *pix, int x, in } static void -on_mouse(struct exposable *exposable, struct bar *bar, - enum mouse_event event, enum mouse_button btn, int x, int y) +on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event, enum mouse_button btn, int x, int y) { const struct private *e = exposable->private; @@ -87,12 +87,11 @@ on_mouse(struct exposable *exposable, struct bar *bar, return; } - int px = /*p->left_margin;*/0; + int px = /*p->left_margin;*/ 0; for (size_t i = 0; i < e->count; i++) { if (x >= px && x < px + e->exposables[i]->width) { if (e->exposables[i]->on_mouse != NULL) { - e->exposables[i]->on_mouse( - e->exposables[i], bar, event, btn, x - px, y); + e->exposables[i]->on_mouse(e->exposables[i], bar, event, btn, x - px, y); } return; } @@ -105,8 +104,7 @@ on_mouse(struct exposable *exposable, struct bar *bar, } struct exposable * -dynlist_exposable_new(struct exposable **exposables, size_t count, - int left_spacing, int right_spacing) +dynlist_exposable_new(struct exposable **exposables, size_t count, int left_spacing, int right_spacing) { struct private *e = calloc(1, sizeof(*e)); e->count = count; diff --git a/particles/dynlist.h b/particles/dynlist.h index 4867997..810df45 100644 --- a/particles/dynlist.h +++ b/particles/dynlist.h @@ -3,5 +3,5 @@ #include struct particle; -struct exposable *dynlist_exposable_new( - struct exposable **exposables, size_t count, int left_spacing, int right_spacing); +struct exposable *dynlist_exposable_new(struct exposable **exposables, size_t count, int left_spacing, + int right_spacing); diff --git a/particles/empty.c b/particles/empty.c index 5c0be16..052eacd 100644 --- a/particles/empty.c +++ b/particles/empty.c @@ -1,15 +1,14 @@ #include -#include "../config.h" #include "../config-verify.h" +#include "../config.h" #include "../particle.h" #include "../plugin.h" static int begin_expose(struct exposable *exposable) { - exposable->width = exposable->particle->left_margin + - exposable->particle->right_margin; + exposable->width = exposable->particle->left_margin + exposable->particle->right_margin; return exposable->width; } diff --git a/particles/list.c b/particles/list.c index 2887d3c..83b5d0c 100644 --- a/particles/list.c +++ b/particles/list.c @@ -2,13 +2,14 @@ #define LOG_MODULE "list" #define LOG_ENABLE_DBG 0 -#include "../log.h" -#include "../config.h" #include "../config-verify.h" +#include "../config.h" +#include "../log.h" #include "../particle.h" #include "../plugin.h" -struct private { +struct private +{ struct particle **particles; size_t count; int left_spacing, right_spacing; @@ -21,7 +22,6 @@ struct eprivate { int left_spacing, right_spacing; }; - static void exposable_destroy(struct exposable *exposable) { @@ -86,16 +86,12 @@ expose(const struct exposable *exposable, pixman_image_t *pix, int x, int y, int } static void -on_mouse(struct exposable *exposable, struct bar *bar, - enum mouse_event event, enum mouse_button btn, int x, int y) +on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event, enum mouse_button btn, int x, int y) { const struct particle *p = exposable->particle; const struct eprivate *e = exposable->private; - if ((event == ON_MOUSE_MOTION && - exposable->particle->have_on_click_template) || - exposable->on_click[btn] != NULL) - { + if ((event == ON_MOUSE_MOTION && exposable->particle->have_on_click_template) || exposable->on_click[btn] != NULL) { /* We have our own handler */ exposable_default_on_mouse(exposable, bar, event, btn, x, y); return; @@ -105,8 +101,7 @@ on_mouse(struct exposable *exposable, struct bar *bar, for (size_t i = 0; i < e->count; i++) { if (x >= px && x < px + e->exposables[i]->width) { if (e->exposables[i]->on_mouse != NULL) { - e->exposables[i]->on_mouse( - e->exposables[i], bar, event, btn, x - px, y); + e->exposables[i]->on_mouse(e->exposables[i], bar, event, btn, x - px, y); } return; } @@ -157,9 +152,8 @@ particle_destroy(struct particle *particle) } struct particle * -particle_list_new(struct particle *common, - struct particle *particles[], size_t count, - int left_spacing, int right_spacing) +particle_list_new(struct particle *common, struct particle *particles[], size_t count, int left_spacing, + int right_spacing) { struct private *p = calloc(1, sizeof(*p)); p->particles = malloc(count * sizeof(p->particles[0])); @@ -184,21 +178,20 @@ from_conf(const struct yml_node *node, struct particle *common) const struct yml_node *_left_spacing = yml_get_value(node, "left-spacing"); const struct yml_node *_right_spacing = yml_get_value(node, "right-spacing"); - int left_spacing = spacing != NULL ? yml_value_as_int(spacing) : - _left_spacing != NULL ? yml_value_as_int(_left_spacing) : 0; - int right_spacing = spacing != NULL ? yml_value_as_int(spacing) : - _right_spacing != NULL ? yml_value_as_int(_right_spacing) : 2; + int left_spacing = spacing != NULL ? yml_value_as_int(spacing) + : _left_spacing != NULL ? yml_value_as_int(_left_spacing) + : 0; + int right_spacing = spacing != NULL ? yml_value_as_int(spacing) + : _right_spacing != NULL ? yml_value_as_int(_right_spacing) + : 2; size_t count = yml_list_length(items); struct particle *parts[count]; size_t idx = 0; - for (struct yml_list_iter it = yml_list_iter(items); - it.node != NULL; - yml_list_next(&it), idx++) - { - parts[idx] = conf_to_particle( - it.node, (struct conf_inherit){common->font, common->font_shaping, common->foreground}); + for (struct yml_list_iter it = yml_list_iter(items); it.node != NULL; yml_list_next(&it), idx++) { + parts[idx] + = conf_to_particle(it.node, (struct conf_inherit){common->font, common->font_shaping, common->foreground}); } return particle_list_new(common, parts, count, left_spacing, right_spacing); diff --git a/particles/map.c b/particles/map.c index 1c1cc9a..51fc744 100644 --- a/particles/map.c +++ b/particles/map.c @@ -1,12 +1,12 @@ +#include +#include #include #include -#include -#include #define LOG_MODULE "map" -#include "../log.h" -#include "../config.h" #include "../config-verify.h" +#include "../config.h" +#include "../log.h" #include "../particle.h" #include "../plugin.h" #include "dynlist.h" @@ -17,14 +17,22 @@ static bool int_condition(const long tag_value, const long cond_value, enum map_op op) { switch (op) { - case MAP_OP_EQ: return tag_value == cond_value; - case MAP_OP_NE: return tag_value != cond_value; - case MAP_OP_LE: return tag_value <= cond_value; - case MAP_OP_LT: return tag_value < cond_value; - case MAP_OP_GE: return tag_value >= cond_value; - case MAP_OP_GT: return tag_value > cond_value; - case MAP_OP_SELF: LOG_WARN("using int tag as bool"); - default: return false; + case MAP_OP_EQ: + return tag_value == cond_value; + case MAP_OP_NE: + return tag_value != cond_value; + case MAP_OP_LE: + return tag_value <= cond_value; + case MAP_OP_LT: + return tag_value < cond_value; + case MAP_OP_GE: + return tag_value >= cond_value; + case MAP_OP_GT: + return tag_value > cond_value; + case MAP_OP_SELF: + LOG_WARN("using int tag as bool"); + default: + return false; } } @@ -32,34 +40,50 @@ static bool float_condition(const double tag_value, const double cond_value, enum map_op op) { switch (op) { - case MAP_OP_EQ: return tag_value == cond_value; - case MAP_OP_NE: return tag_value != cond_value; - case MAP_OP_LE: return tag_value <= cond_value; - case MAP_OP_LT: return tag_value < cond_value; - case MAP_OP_GE: return tag_value >= cond_value; - case MAP_OP_GT: return tag_value > cond_value; - case MAP_OP_SELF: LOG_WARN("using float tag as bool"); - default: return false; + case MAP_OP_EQ: + return tag_value == cond_value; + case MAP_OP_NE: + return tag_value != cond_value; + case MAP_OP_LE: + return tag_value <= cond_value; + case MAP_OP_LT: + return tag_value < cond_value; + case MAP_OP_GE: + return tag_value >= cond_value; + case MAP_OP_GT: + return tag_value > cond_value; + case MAP_OP_SELF: + LOG_WARN("using float tag as bool"); + default: + return false; } } static bool -str_condition(const char* tag_value, const char* cond_value, enum map_op op) +str_condition(const char *tag_value, const char *cond_value, enum map_op op) { switch (op) { - case MAP_OP_EQ: return strcmp(tag_value, cond_value) == 0; - case MAP_OP_NE: return strcmp(tag_value, cond_value) != 0; - case MAP_OP_LE: return strcmp(tag_value, cond_value) <= 0; - case MAP_OP_LT: return strcmp(tag_value, cond_value) < 0; - case MAP_OP_GE: return strcmp(tag_value, cond_value) >= 0; - case MAP_OP_GT: return strcmp(tag_value, cond_value) > 0; - case MAP_OP_SELF: LOG_WARN("using String tag as bool"); - default: return false; + case MAP_OP_EQ: + return strcmp(tag_value, cond_value) == 0; + case MAP_OP_NE: + return strcmp(tag_value, cond_value) != 0; + case MAP_OP_LE: + return strcmp(tag_value, cond_value) <= 0; + case MAP_OP_LT: + return strcmp(tag_value, cond_value) < 0; + case MAP_OP_GE: + return strcmp(tag_value, cond_value) >= 0; + case MAP_OP_GT: + return strcmp(tag_value, cond_value) > 0; + case MAP_OP_SELF: + LOG_WARN("using String tag as bool"); + default: + return false; } } static bool -eval_comparison(const struct map_condition* map_cond, const struct tag_set *tags) +eval_comparison(const struct map_condition *map_cond, const struct tag_set *tags) { const struct tag *tag = tag_for_name(tags, map_cond->tag); if (tag == NULL) { @@ -108,7 +132,7 @@ eval_comparison(const struct map_condition* map_cond, const struct tag_set *tags return false; } case TAG_TYPE_STRING: { - const char* tag_value = tag->as_string(tag); + const char *tag_value = tag->as_string(tag); return str_condition(tag_value, map_cond->value, map_cond->op); } } @@ -116,19 +140,17 @@ eval_comparison(const struct map_condition* map_cond, const struct tag_set *tags } static bool -eval_map_condition(const struct map_condition* map_cond, const struct tag_set *tags) +eval_map_condition(const struct map_condition *map_cond, const struct tag_set *tags) { - switch(map_cond->op) { + switch (map_cond->op) { case MAP_OP_NOT: return !eval_map_condition(map_cond->cond1, tags); case MAP_OP_AND: - return eval_map_condition(map_cond->cond1, tags) && - eval_map_condition(map_cond->cond2, tags); + return eval_map_condition(map_cond->cond1, tags) && eval_map_condition(map_cond->cond2, tags); case MAP_OP_OR: - return eval_map_condition(map_cond->cond1, tags) || - eval_map_condition(map_cond->cond2, tags); + return eval_map_condition(map_cond->cond1, tags) || eval_map_condition(map_cond->cond2, tags); default: return eval_comparison(map_cond, tags); @@ -136,28 +158,27 @@ eval_map_condition(const struct map_condition* map_cond, const struct tag_set *t } void -free_map_condition(struct map_condition* c) +free_map_condition(struct map_condition *c) { - switch (c->op) - { - case MAP_OP_EQ: - case MAP_OP_NE: - case MAP_OP_LE: - case MAP_OP_LT: - case MAP_OP_GE: - case MAP_OP_GT: - free(c->value); - /* FALLTHROUGH */ - case MAP_OP_SELF: - free(c->tag); - break; - case MAP_OP_AND: - case MAP_OP_OR: - free_map_condition(c->cond2); - /* FALLTHROUGH */ - case MAP_OP_NOT: - free_map_condition(c->cond1); - break; + switch (c->op) { + case MAP_OP_EQ: + case MAP_OP_NE: + case MAP_OP_LE: + case MAP_OP_LT: + case MAP_OP_GE: + case MAP_OP_GT: + free(c->value); + /* FALLTHROUGH */ + case MAP_OP_SELF: + free(c->tag); + break; + case MAP_OP_AND: + case MAP_OP_OR: + free_map_condition(c->cond2); + /* FALLTHROUGH */ + case MAP_OP_NOT: + free_map_condition(c->cond1); + break; } free(c); } @@ -167,7 +188,8 @@ struct particle_map { struct particle *particle; }; -struct private { +struct private +{ struct particle *default_particle; struct particle_map *map; size_t count; @@ -208,21 +230,16 @@ expose(const struct exposable *exposable, pixman_image_t *pix, int x, int y, int struct eprivate *e = exposable->private; exposable_render_deco(exposable, pix, x, y, height); - e->exposable->expose( - e->exposable, pix, x + exposable->particle->left_margin, y, height); + e->exposable->expose(e->exposable, pix, x + exposable->particle->left_margin, y, height); } static void -on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event, - enum mouse_button btn, int x, int y) +on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event, enum mouse_button btn, int x, int y) { const struct particle *p = exposable->particle; const struct eprivate *e = exposable->private; - if ((event == ON_MOUSE_MOTION && - exposable->particle->have_on_click_template) || - exposable->on_click[btn] != NULL) - { + if ((event == ON_MOUSE_MOTION && exposable->particle->have_on_click_template) || exposable->on_click[btn] != NULL) { /* We have our own handler */ exposable_default_on_mouse(exposable, bar, event, btn, x, y); return; @@ -295,8 +312,8 @@ particle_destroy(struct particle *particle) } static struct particle * -map_new(struct particle *common, const struct particle_map particle_map[], - size_t count, struct particle *default_particle) +map_new(struct particle *common, const struct particle_map particle_map[], size_t count, + struct particle *default_particle) { struct private *priv = calloc(1, sizeof(*priv)); priv->default_particle = default_particle; @@ -314,22 +331,16 @@ map_new(struct particle *common, const struct particle_map particle_map[], return common; } - static bool verify_map_conditions(keychain_t *chain, const struct yml_node *node) { if (!yml_is_dict(node)) { - LOG_ERR( - "%s: must be a dictionary of workspace-name: particle mappings", - conf_err_prefix(chain, node)); + LOG_ERR("%s: must be a dictionary of workspace-name: particle mappings", conf_err_prefix(chain, node)); return false; } bool result = true; - for (struct yml_dict_iter it = yml_dict_iter(node); - it.key != NULL; - yml_dict_next(&it)) - { + for (struct yml_dict_iter it = yml_dict_iter(node); it.key != NULL; yml_dict_next(&it)) { const char *key = yml_value_as_string(it.key); if (key == NULL) { LOG_ERR("%s: key must be a string", conf_err_prefix(chain, it.key)); @@ -363,17 +374,11 @@ from_conf(const struct yml_node *node, struct particle *common) struct particle_map particle_map[yml_dict_length(conditions)]; - struct conf_inherit inherited = { - .font = common->font, - .font_shaping = common->font_shaping, - .foreground = common->foreground - }; + struct conf_inherit inherited + = {.font = common->font, .font_shaping = common->font_shaping, .foreground = common->foreground}; size_t idx = 0; - for (struct yml_dict_iter it = yml_dict_iter(conditions); - it.key != NULL; - yml_dict_next(&it), idx++) - { + for (struct yml_dict_iter it = yml_dict_iter(conditions); it.key != NULL; yml_dict_next(&it), idx++) { /* Note we can skip the error checking here */ char *key_clone = strdup(yml_value_as_string(it.key)); YY_BUFFER_STATE buffer = yy_scan_string(key_clone); @@ -384,8 +389,7 @@ from_conf(const struct yml_node *node, struct particle *common) particle_map[idx].particle = conf_to_particle(it.value, inherited); } - struct particle *default_particle = def != NULL - ? conf_to_particle(def, inherited) : NULL; + struct particle *default_particle = def != NULL ? conf_to_particle(def, inherited) : NULL; return map_new(common, particle_map, yml_dict_length(conditions), default_particle); } diff --git a/particles/map.h b/particles/map.h index a6d35b4..23670a5 100644 --- a/particles/map.h +++ b/particles/map.h @@ -28,7 +28,7 @@ struct map_condition { void free_map_condition(struct map_condition *c); -typedef struct yy_buffer_state* YY_BUFFER_STATE; +typedef struct yy_buffer_state *YY_BUFFER_STATE; YY_BUFFER_STATE yy_scan_string(const char *str); int yyparse(); void yy_delete_buffer(YY_BUFFER_STATE buffer); diff --git a/particles/progress-bar.c b/particles/progress-bar.c index f9e3999..f0bacbf 100644 --- a/particles/progress-bar.c +++ b/particles/progress-bar.c @@ -1,16 +1,17 @@ +#include #include #include -#include #define LOG_MODULE "progress_bar" #define LOG_ENABLE_DBG 0 -#include "../log.h" -#include "../config.h" #include "../config-verify.h" +#include "../config.h" +#include "../log.h" #include "../particle.h" #include "../plugin.h" -struct private { +struct private +{ char *tag; int width; @@ -74,8 +75,7 @@ begin_expose(struct exposable *exposable) /* Margins */ if (have_at_least_one) { - exposable->width += exposable->particle->left_margin + - exposable->particle->right_margin; + exposable->width += exposable->particle->left_margin + exposable->particle->right_margin; } else assert(exposable->width == 0); @@ -97,8 +97,7 @@ expose(const struct exposable *exposable, pixman_image_t *pix, int x, int y, int } static void -on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event, - enum mouse_button btn, int x, int y) +on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event, enum mouse_button btn, int x, int y) { const struct particle *p = exposable->particle; const struct eprivate *e = exposable->private; @@ -161,18 +160,14 @@ on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event, original[i] = exposable->on_click[i]; if (event == ON_MOUSE_CLICK) { - long where = clickable_width > 0 - ? 100 * (x - x_offset) / clickable_width - : 0; + long where = clickable_width > 0 ? 100 * (x - x_offset) / clickable_width : 0; struct tag_set tags = { .tags = (struct tag *[]){tag_new_int(NULL, "where", where)}, .count = 1, }; - tags_expand_templates( - exposable->on_click, (const char **)exposable->on_click, - MOUSE_BTN_COUNT, &tags); + tags_expand_templates(exposable->on_click, (const char **)exposable->on_click, MOUSE_BTN_COUNT, &tags); tag_set_destroy(&tags); } @@ -198,19 +193,17 @@ instantiate(const struct particle *particle, const struct tag_set *tags) long min = tag != NULL ? tag->min(tag) : 0; long max = tag != NULL ? tag->max(tag) : 0; - LOG_DBG("%s: value=%ld, min=%ld, max=%ld", - tag != NULL ? tag->name(tag) : "", value, min, max); + LOG_DBG("%s: value=%ld, min=%ld, max=%ld", tag != NULL ? tag->name(tag) : "", value, min, max); long fill_count = max == min ? 0 : p->width * value / (max - min); long empty_count = p->width - fill_count; struct eprivate *epriv = calloc(1, sizeof(*epriv)); - epriv->count = ( - 1 + /* Start marker */ - fill_count + /* Before current position */ - 1 + /* Current position indicator */ - empty_count + /* After current position */ - 1); /* End marker */ + epriv->count = (1 + /* Start marker */ + fill_count + /* Before current position */ + 1 + /* Current position indicator */ + empty_count + /* After current position */ + 1); /* End marker */ epriv->exposables = malloc(epriv->count * sizeof(epriv->exposables[0])); @@ -259,8 +252,7 @@ instantiate(const struct particle *particle, const struct tag_set *tags) LOG_DBG("tag: %s, value: %ld, " "units-per-segment: %f, units-filled: %f, units-til-next: %f", - tag->name(tag), value, - units_per_segment, units_filled, units_til_next_segment); + tag->name(tag), value, units_per_segment, units_filled, units_til_next_segment); #endif @@ -271,10 +263,8 @@ instantiate(const struct particle *particle, const struct tag_set *tags) } static struct particle * -progress_bar_new(struct particle *common, const char *tag, int width, - struct particle *start_marker, struct particle *end_marker, - struct particle *fill, struct particle *empty, - struct particle *indicator) +progress_bar_new(struct particle *common, const char *tag, int width, struct particle *start_marker, + struct particle *end_marker, struct particle *fill, struct particle *empty, struct particle *indicator) { struct private *priv = calloc(1, sizeof(*priv)); priv->tag = strdup(tag); @@ -308,15 +298,10 @@ from_conf(const struct yml_node *node, struct particle *common) .foreground = common->foreground, }; - return progress_bar_new( - common, - yml_value_as_string(tag), - yml_value_as_int(length), - conf_to_particle(start, inherited), - conf_to_particle(end, inherited), - conf_to_particle(fill, inherited), - conf_to_particle(empty, inherited), - conf_to_particle(indicator, inherited)); + return progress_bar_new(common, yml_value_as_string(tag), yml_value_as_int(length), + conf_to_particle(start, inherited), conf_to_particle(end, inherited), + conf_to_particle(fill, inherited), conf_to_particle(empty, inherited), + conf_to_particle(indicator, inherited)); } static bool diff --git a/particles/ramp.c b/particles/ramp.c index 0127519..befe1d9 100644 --- a/particles/ramp.c +++ b/particles/ramp.c @@ -1,18 +1,19 @@ +#include #include #include -#include #include #define LOG_MODULE "ramp" #define LOG_ENABLE_DBG 0 -#include "../log.h" -#include "../config.h" #include "../config-verify.h" +#include "../config.h" +#include "../log.h" #include "../particle.h" #include "../plugin.h" -struct private { +struct private +{ char *tag; bool use_custom_min; long min; @@ -57,21 +58,16 @@ expose(const struct exposable *exposable, pixman_image_t *pix, int x, int y, int struct eprivate *e = exposable->private; exposable_render_deco(exposable, pix, x, y, height); - e->exposable->expose( - e->exposable, pix, x + exposable->particle->left_margin, y, height); + e->exposable->expose(e->exposable, pix, x + exposable->particle->left_margin, y, height); } static void -on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event, - enum mouse_button btn, int x, int y) +on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event, enum mouse_button btn, int x, int y) { const struct particle *p = exposable->particle; const struct eprivate *e = exposable->private; - if ((event == ON_MOUSE_MOTION && - exposable->particle->have_on_click_template) || - exposable->on_click[btn] != NULL) - { + if ((event == ON_MOUSE_MOTION && exposable->particle->have_on_click_template) || exposable->on_click[btn] != NULL) { /* We have our own handler */ exposable_default_on_mouse(exposable, bar, event, btn, x, y); return; @@ -118,22 +114,22 @@ instantiate(const struct particle *particle, const struct tag_set *tags) max = p->use_custom_max ? p->max : max; if (min > max) { - LOG_WARN( - "tag's minimum value is greater than its maximum: " - "tag=\"%s\", min=%ld, max=%ld", p->tag, min, max); + LOG_WARN("tag's minimum value is greater than its maximum: " + "tag=\"%s\", min=%ld, max=%ld", + p->tag, min, max); min = max; } if (value < min) { - LOG_WARN( - "tag's value is less than its minimum value: " - "tag=\"%s\", min=%ld, value=%ld", p->tag, min, value); + LOG_WARN("tag's value is less than its minimum value: " + "tag=\"%s\", min=%ld, value=%ld", + p->tag, min, value); value = min; } if (value > max) { - LOG_WARN( - "tag's value is greater than its maximum value: " - "tag=\"%s\", max=%ld, value=%ld", p->tag, max, value); + LOG_WARN("tag's value is greater than its maximum value: " + "tag=\"%s\", max=%ld, value=%ld", + p->tag, max, value); value = max; } @@ -168,10 +164,8 @@ instantiate(const struct particle *particle, const struct tag_set *tags) } static struct particle * -ramp_new(struct particle *common, const char *tag, - struct particle *particles[], size_t count, - bool use_custom_min, long min, - bool use_custom_max, long max) +ramp_new(struct particle *common, const char *tag, struct particle *particles[], size_t count, bool use_custom_min, + long min, bool use_custom_max, long max) { struct private *priv = calloc(1, sizeof(*priv)); @@ -204,19 +198,15 @@ from_conf(const struct yml_node *node, struct particle *common) struct particle *parts[count]; size_t idx = 0; - for (struct yml_list_iter it = yml_list_iter(items); - it.node != NULL; - yml_list_next(&it), idx++) - { - parts[idx] = conf_to_particle( - it.node, (struct conf_inherit){common->font, common->font_shaping, common->foreground}); + for (struct yml_list_iter it = yml_list_iter(items); it.node != NULL; yml_list_next(&it), idx++) { + parts[idx] + = conf_to_particle(it.node, (struct conf_inherit){common->font, common->font_shaping, common->foreground}); } long min_v = min != NULL ? yml_value_as_int(min) : 0; long max_v = max != NULL ? yml_value_as_int(max) : 0; - return ramp_new(common, yml_value_as_string(tag), parts, count, min != NULL, - min_v, max != NULL, max_v); + return ramp_new(common, yml_value_as_string(tag), parts, count, min != NULL, min_v, max != NULL, max_v); } static bool diff --git a/particles/string.c b/particles/string.c index 186d50e..4922d7d 100644 --- a/particles/string.c +++ b/particles/string.c @@ -1,13 +1,13 @@ +#include #include #include -#include #define LOG_MODULE "string" #define LOG_ENABLE_DBG 0 -#include "../log.h" #include "../char32.h" -#include "../config.h" #include "../config-verify.h" +#include "../config.h" +#include "../log.h" #include "../particle.h" #include "../plugin.h" @@ -18,7 +18,8 @@ struct text_run_cache { bool in_use; }; -struct private { +struct private +{ char *text; size_t max_len; @@ -51,9 +52,7 @@ begin_expose(struct exposable *exposable) struct eprivate *e = exposable->private; struct private *p = exposable->particle->private; - exposable->width = - exposable->particle->left_margin + - exposable->particle->right_margin; + exposable->width = exposable->particle->left_margin + exposable->particle->right_margin; if (e->cache_idx >= 0) { exposable->width += p->cache[e->cache_idx].width; @@ -97,9 +96,8 @@ expose(const struct exposable *exposable, pixman_image_t *pix, int x, int y, int * any real facts, but works very well with e.g. the "Awesome 6" * font family. */ - const double baseline = (double)y + - (double)(height + font->ascent + font->descent) / 2.0 - - (font->descent > 0 ? font->descent : 0); + const double baseline + = (double)y + (double)(height + font->ascent + font->descent) / 2.0 - (font->descent > 0 ? font->descent : 0); x += exposable->particle->left_margin; @@ -112,17 +110,13 @@ expose(const struct exposable *exposable, pixman_image_t *pix, int x, int y, int if (pixman_image_get_format(glyph->pix) == PIXMAN_a8r8g8b8) { /* Glyph surface is a pre-rendered image (typically a color emoji...) */ - pixman_image_composite32( - PIXMAN_OP_OVER, glyph->pix, NULL, pix, 0, 0, 0, 0, - x + glyph->x, baseline - glyph->y, - glyph->width, glyph->height); + pixman_image_composite32(PIXMAN_OP_OVER, glyph->pix, NULL, pix, 0, 0, 0, 0, x + glyph->x, + baseline - glyph->y, glyph->width, glyph->height); } else { /* Glyph surface is an alpha mask */ pixman_image_t *src = pixman_image_create_solid_fill(&exposable->particle->foreground); - pixman_image_composite32( - PIXMAN_OP_OVER, src, glyph->pix, pix, 0, 0, 0, 0, - x + glyph->x, baseline - glyph->y, - glyph->width, glyph->height); + pixman_image_composite32(PIXMAN_OP_OVER, src, glyph->pix, pix, 0, 0, 0, 0, x + glyph->x, + baseline - glyph->y, glyph->width, glyph->height); pixman_image_unref(src); } @@ -188,11 +182,8 @@ instantiate(const struct particle *particle, const struct tag_set *tags) e->kern_x = calloc(chars, sizeof(e->kern_x[0])); - if (particle->font_shaping == FONT_SHAPE_FULL && - fcft_capabilities() & FCFT_CAPABILITY_TEXT_RUN_SHAPING) - { - struct fcft_text_run *run = fcft_rasterize_text_run_utf32( - font, chars, wtext, FCFT_SUBPIXEL_NONE); + if (particle->font_shaping == FONT_SHAPE_FULL && fcft_capabilities() & FCFT_CAPABILITY_TEXT_RUN_SHAPING) { + struct fcft_text_run *run = fcft_rasterize_text_run_utf32(font, chars, wtext, FCFT_SUBPIXEL_NONE); if (run != NULL) { int w = 0; @@ -210,8 +201,7 @@ instantiate(const struct particle *particle, const struct tag_set *tags) if (cache_idx < 0) { size_t new_size = p->cache_size + 1; - struct text_run_cache *new_cache = realloc( - p->cache, new_size * sizeof(new_cache[0])); + struct text_run_cache *new_cache = realloc(p->cache, new_size * sizeof(new_cache[0])); p->cache_size = new_size; p->cache = new_cache; @@ -235,8 +225,7 @@ instantiate(const struct particle *particle, const struct tag_set *tags) /* Convert text to glyph masks/images. */ for (size_t i = 0; i < chars; i++) { - const struct fcft_glyph *glyph = fcft_rasterize_char_utf32( - font, wtext[i], FCFT_SUBPIXEL_NONE); + const struct fcft_glyph *glyph = fcft_rasterize_char_utf32(font, wtext[i], FCFT_SUBPIXEL_NONE); if (glyph == NULL) continue; @@ -297,10 +286,7 @@ from_conf(const struct yml_node *node, struct particle *common) const struct yml_node *text = yml_get_value(node, "text"); const struct yml_node *max = yml_get_value(node, "max"); - return string_new( - common, - yml_value_as_string(text), - max != NULL ? yml_value_as_int(max) : 0); + return string_new(common, yml_value_as_string(text), max != NULL ? yml_value_as_int(max) : 0); } static bool diff --git a/plugin.c b/plugin.c index 20dfbbf..8e75389 100644 --- a/plugin.c +++ b/plugin.c @@ -1,99 +1,94 @@ #include "plugin.h" -#include #include +#include #include #define LOG_MODULE "plugin" #define LOG_ENABLE_DBG 0 -#include "log.h" #include "config.h" +#include "log.h" #if !defined(CORE_PLUGINS_AS_SHARED_LIBRARIES) -#define EXTERN_MODULE(plug_name) \ - extern const struct module_iface module_##plug_name##_iface; \ - extern bool plug_name##_verify_conf( \ - keychain_t *chain, const struct yml_node *node); \ - extern struct module *plug_name##_from_conf( \ - const struct yml_node *node, struct conf_inherit inherited); +#define EXTERN_MODULE(plug_name) \ + extern const struct module_iface module_##plug_name##_iface; \ + extern bool plug_name##_verify_conf(keychain_t *chain, const struct yml_node *node); \ + extern struct module *plug_name##_from_conf(const struct yml_node *node, struct conf_inherit inherited); -#define EXTERN_PARTICLE(plug_name) \ - extern const struct particle_iface particle_##plug_name##_iface; \ - extern bool plug_name##_verify_conf( \ - keychain_t *chain, const struct yml_node *node); \ - extern struct particle *plug_name##_from_conf( \ - const struct yml_node *node, struct particle *common); +#define EXTERN_PARTICLE(plug_name) \ + extern const struct particle_iface particle_##plug_name##_iface; \ + extern bool plug_name##_verify_conf(keychain_t *chain, const struct yml_node *node); \ + extern struct particle *plug_name##_from_conf(const struct yml_node *node, struct particle *common); -#define EXTERN_DECORATION(plug_name) \ - extern const struct deco_iface deco_##plug_name##_iface; \ - extern bool plug_name##_verify_conf( \ - keychain_t *chain, const struct yml_node *node); \ +#define EXTERN_DECORATION(plug_name) \ + extern const struct deco_iface deco_##plug_name##_iface; \ + extern bool plug_name##_verify_conf(keychain_t *chain, const struct yml_node *node); \ extern struct deco *plug_name##_from_conf(const struct yml_node *node); #if defined(HAVE_PLUGIN_alsa) - EXTERN_MODULE(alsa); +EXTERN_MODULE(alsa); #endif #if defined(HAVE_PLUGIN_backlight) - EXTERN_MODULE(backlight); +EXTERN_MODULE(backlight); #endif #if defined(HAVE_PLUGIN_battery) - EXTERN_MODULE(battery); +EXTERN_MODULE(battery); #endif #if defined(HAVE_PLUGIN_clock) - EXTERN_MODULE(clock); +EXTERN_MODULE(clock); #endif #if defined(HAVE_PLUGIN_cpu) - EXTERN_MODULE(cpu); +EXTERN_MODULE(cpu); #endif #if defined(HAVE_PLUGIN_disk_io) - EXTERN_MODULE(disk_io); +EXTERN_MODULE(disk_io); #endif #if defined(HAVE_PLUGIN_dwl) - EXTERN_MODULE(dwl); +EXTERN_MODULE(dwl); #endif #if defined(HAVE_PLUGIN_foreign_toplevel) - EXTERN_MODULE(foreign_toplevel); +EXTERN_MODULE(foreign_toplevel); #endif #if defined(HAVE_PLUGIN_mem) - EXTERN_MODULE(mem); +EXTERN_MODULE(mem); #endif #if defined(HAVE_PLUGIN_mpd) - EXTERN_MODULE(mpd); +EXTERN_MODULE(mpd); #endif #if defined(HAVE_PLUGIN_i3) - EXTERN_MODULE(i3); +EXTERN_MODULE(i3); #endif #if defined(HAVE_PLUGIN_label) - EXTERN_MODULE(label); +EXTERN_MODULE(label); #endif #if defined(HAVE_PLUGIN_network) - EXTERN_MODULE(network); +EXTERN_MODULE(network); #endif #if defined(HAVE_PLUGIN_pipewire) - EXTERN_MODULE(pipewire); +EXTERN_MODULE(pipewire); #endif #if defined(HAVE_PLUGIN_pulse) - EXTERN_MODULE(pulse); +EXTERN_MODULE(pulse); #endif #if defined(HAVE_PLUGIN_removables) - EXTERN_MODULE(removables); +EXTERN_MODULE(removables); #endif #if defined(HAVE_PLUGIN_river) - EXTERN_MODULE(river); +EXTERN_MODULE(river); #endif #if defined(HAVE_PLUGIN_script) - EXTERN_MODULE(script); +EXTERN_MODULE(script); #endif #if defined(HAVE_PLUGIN_sway_xkb) - EXTERN_MODULE(sway_xkb); +EXTERN_MODULE(sway_xkb); #endif #if defined(HAVE_PLUGIN_xkb) - EXTERN_MODULE(xkb); +EXTERN_MODULE(xkb); #endif #if defined(HAVE_PLUGIN_xwindow) - EXTERN_MODULE(xwindow); +EXTERN_MODULE(xwindow); #endif EXTERN_PARTICLE(empty); @@ -121,42 +116,45 @@ static const char * type2str(enum plugin_type type) { switch (type) { - case PLUGIN_MODULE: return "module"; - case PLUGIN_PARTICLE: return "particle"; - case PLUGIN_DECORATION: return "decoration"; + case PLUGIN_MODULE: + return "module"; + case PLUGIN_PARTICLE: + return "particle"; + case PLUGIN_DECORATION: + return "decoration"; } assert(false && "invalid type"); return ""; } -static void __attribute__((constructor)) -init(void) +static void __attribute__((constructor)) init(void) { #if !defined(CORE_PLUGINS_AS_SHARED_LIBRARIES) -#define REGISTER_CORE_PLUGIN(plug_name, func_prefix, plug_type) \ - do { \ - tll_push_back( \ - plugins, \ - ((struct plugin){ \ - .name = strdup(#plug_name), \ - .type = (plug_type), \ - .lib = RTLD_DEFAULT, \ - })); \ +#define REGISTER_CORE_PLUGIN(plug_name, func_prefix, plug_type) \ + do { \ + tll_push_back(plugins, ((struct plugin){ \ + .name = strdup(#plug_name), \ + .type = (plug_type), \ + .lib = RTLD_DEFAULT, \ + })); \ } while (0) -#define REGISTER_CORE_MODULE(plug_name, func_prefix) do { \ - REGISTER_CORE_PLUGIN(plug_name, func_prefix, PLUGIN_MODULE); \ - tll_back(plugins).module = &module_##func_prefix##_iface; \ +#define REGISTER_CORE_MODULE(plug_name, func_prefix) \ + do { \ + REGISTER_CORE_PLUGIN(plug_name, func_prefix, PLUGIN_MODULE); \ + tll_back(plugins).module = &module_##func_prefix##_iface; \ } while (0) -#define REGISTER_CORE_PARTICLE(plug_name, func_prefix) do { \ - REGISTER_CORE_PLUGIN(plug_name, func_prefix, PLUGIN_PARTICLE); \ - tll_back(plugins).particle = &particle_##func_prefix##_iface; \ +#define REGISTER_CORE_PARTICLE(plug_name, func_prefix) \ + do { \ + REGISTER_CORE_PLUGIN(plug_name, func_prefix, PLUGIN_PARTICLE); \ + tll_back(plugins).particle = &particle_##func_prefix##_iface; \ } while (0) -#define REGISTER_CORE_DECORATION(plug_name, func_prefix) do { \ - REGISTER_CORE_PLUGIN(plug_name, func_prefix, PLUGIN_DECORATION); \ - tll_back(plugins).decoration = &deco_##func_prefix##_iface; \ +#define REGISTER_CORE_DECORATION(plug_name, func_prefix) \ + do { \ + REGISTER_CORE_PLUGIN(plug_name, func_prefix, PLUGIN_DECORATION); \ + tll_back(plugins).decoration = &deco_##func_prefix##_iface; \ } while (0) #if defined(HAVE_PLUGIN_alsa) @@ -258,16 +256,13 @@ free_plugin(struct plugin plug) free(plug.name); } -static void __attribute__((destructor)) -fini(void) -{ - tll_free_and_free(plugins, free_plugin); -} +static void __attribute__((destructor)) fini(void) { tll_free_and_free(plugins, free_plugin); } const struct plugin * plugin_load(const char *name, enum plugin_type type) { - tll_foreach(plugins, plug) { + tll_foreach(plugins, plug) + { if (plug->item.type == type && strcmp(plug->item.name, name) == 0) { LOG_DBG("%s: %s already loaded: %p", type2str(type), name, plug->item.lib); assert(plug->item.dummy != NULL); @@ -275,7 +270,6 @@ plugin_load(const char *name, enum plugin_type type) } } - char path[128]; snprintf(path, sizeof(path), "%s_%s.so", type2str(type), name); diff --git a/plugin.h b/plugin.h index 1b2da24..4c49caa 100644 --- a/plugin.h +++ b/plugin.h @@ -1,7 +1,7 @@ #pragma once -#include "config.h" #include "config-verify.h" +#include "config.h" #include "module.h" #include "particle.h" @@ -9,14 +9,12 @@ typedef bool (*verify_func_t)(keychain_t *chain, const struct yml_node *node); struct module_iface { verify_func_t verify_conf; - struct module *(*from_conf)( - const struct yml_node *node, struct conf_inherit inherited); + struct module *(*from_conf)(const struct yml_node *node, struct conf_inherit inherited); }; struct particle_iface { verify_func_t verify_conf; - struct particle *(*from_conf)( - const struct yml_node *node, struct particle *common); + struct particle *(*from_conf)(const struct yml_node *node, struct particle *common); }; struct deco_iface { diff --git a/tag.c b/tag.c index 0f44d7e..e95b1c7 100644 --- a/tag.c +++ b/tag.c @@ -1,19 +1,20 @@ #include "tag.h" -#include -#include -#include -#include -#include -#include #include -#include +#include +#include +#include +#include +#include +#include +#include #define LOG_MODULE "tag" #define LOG_ENABLE_DBG 1 #include "log.h" #include "module.h" -struct private { +struct private +{ char *name; union { struct { @@ -156,8 +157,8 @@ int_refresh_in(const struct tag *tag, long units) if (tag->owner == NULL || tag->owner->refresh_in == NULL) return false; - assert(priv->value_as_int.realtime_unit == TAG_REALTIME_SECS || - priv->value_as_int.realtime_unit == TAG_REALTIME_MSECS); + assert(priv->value_as_int.realtime_unit == TAG_REALTIME_SECS + || priv->value_as_int.realtime_unit == TAG_REALTIME_MSECS); long milli_seconds = units; if (priv->value_as_int.realtime_unit == TAG_REALTIME_SECS) @@ -269,15 +270,14 @@ tag_new_int(struct module *owner, const char *name, long value) } struct tag * -tag_new_int_range(struct module *owner, const char *name, long value, - long min, long max) +tag_new_int_range(struct module *owner, const char *name, long value, long min, long max) { return tag_new_int_realtime(owner, name, value, min, max, TAG_REALTIME_NONE); } struct tag * -tag_new_int_realtime(struct module *owner, const char *name, long value, - long min, long max, enum tag_realtime_unit unit) +tag_new_int_realtime(struct module *owner, const char *name, long value, long min, long max, + enum tag_realtime_unit unit) { struct private *priv = malloc(sizeof(*priv)); priv->name = strdup(name); @@ -414,7 +414,7 @@ sbuf_append_at_most(struct sbuf *s1, const char *s2, size_t n) s1->size = 2 * required_size; s1->s = realloc(s1->s, s1->size); - //s1->s[s1->len] = '\0'; + // s1->s[s1->len] = '\0'; } memcpy(&s1->s[s1->len], s2, n); @@ -516,14 +516,16 @@ tags_expand_template(const char *template, const struct tag_set *tags) FMT_KIBYTE, FMT_MIBYTE, FMT_GIBYTE, - } format = FMT_DEFAULT; + } format + = FMT_DEFAULT; enum { VALUE_VALUE, VALUE_MIN, VALUE_MAX, VALUE_UNIT, - } kind = VALUE_VALUE; + } kind + = VALUE_VALUE; int digits = 0; int decimals = 2; @@ -567,22 +569,17 @@ tags_expand_template(const char *template, const struct tag_set *tags) if (digits_str[0] != '\0') { // guards against i.e. "{tag:.3}" if (!is_number(digits_str, &digits)) { - LOG_WARN( - "tag `%s`: invalid field width formatter. Ignoring...", - tag_name); + LOG_WARN("tag `%s`: invalid field width formatter. Ignoring...", tag_name); } } if (decimals_str[0] != '\0') { // guards against i.e. "{tag:3.}" if (!is_number(decimals_str, &decimals)) { - LOG_WARN( - "tag `%s`: invalid decimals formatter. Ignoring...", - tag_name); + LOG_WARN("tag `%s`: invalid decimals formatter. Ignoring...", tag_name); } } zero_pad = digits_str[0] == '0'; - } - else + } else LOG_WARN("invalid tag formatter: %s", tag_args[i]); } @@ -593,7 +590,7 @@ tags_expand_template(const char *template, const struct tag_set *tags) case FMT_DEFAULT: { switch (tag->type(tag)) { case TAG_TYPE_FLOAT: { - const char* fmt = zero_pad ? "%0*.*f" : "%*.*f"; + const char *fmt = zero_pad ? "%0*.*f" : "%*.*f"; char str[24]; snprintf(str, sizeof(str), fmt, digits, decimals, tag->as_float(tag)); sbuf_append(&formatted, str); @@ -601,7 +598,7 @@ tags_expand_template(const char *template, const struct tag_set *tags) } case TAG_TYPE_INT: { - const char* fmt = zero_pad ? "%0*ld" : "%*ld"; + const char *fmt = zero_pad ? "%0*ld" : "%*ld"; char str[24]; snprintf(str, sizeof(str), fmt, digits, tag->as_int(tag)); sbuf_append(&formatted, str); @@ -618,9 +615,7 @@ tags_expand_template(const char *template, const struct tag_set *tags) case FMT_HEX: case FMT_OCT: { - const char* fmt = format == FMT_HEX ? - zero_pad ? "%0*lx" : "%*lx" : - zero_pad ? "%0*lo" : "%*lo"; + const char *fmt = format == FMT_HEX ? zero_pad ? "%0*lx" : "%*lx" : zero_pad ? "%0*lo" : "%*lo"; char str[24]; snprintf(str, sizeof(str), fmt, digits, tag->as_int(tag)); sbuf_append(&formatted, str); @@ -632,7 +627,7 @@ tags_expand_template(const char *template, const struct tag_set *tags) const long max = tag->max(tag); const long cur = tag->as_int(tag); - const char* fmt = zero_pad ? "%0*lu" : "%*lu"; + const char *fmt = zero_pad ? "%0*lu" : "%*lu"; char str[4]; snprintf(str, sizeof(str), fmt, digits, (cur - min) * 100 / (max - min)); sbuf_append(&formatted, str); @@ -645,21 +640,20 @@ tags_expand_template(const char *template, const struct tag_set *tags) case FMT_KIBYTE: case FMT_MIBYTE: case FMT_GIBYTE: { - const long divider = - format == FMT_KBYTE ? 1000 : - format == FMT_MBYTE ? 1000 * 1000 : - format == FMT_GBYTE ? 1000 * 1000 * 1000 : - format == FMT_KIBYTE ? 1024 : - format == FMT_MIBYTE ? 1024 * 1024 : - format == FMT_GIBYTE ? 1024 * 1024 * 1024 : - 1; + const long divider = format == FMT_KBYTE ? 1000 + : format == FMT_MBYTE ? 1000 * 1000 + : format == FMT_GBYTE ? 1000 * 1000 * 1000 + : format == FMT_KIBYTE ? 1024 + : format == FMT_MIBYTE ? 1024 * 1024 + : format == FMT_GIBYTE ? 1024 * 1024 * 1024 + : 1; char str[24]; if (tag->type(tag) == TAG_TYPE_FLOAT) { - const char* fmt = zero_pad ? "%0*.*f" : "%*.*f"; + const char *fmt = zero_pad ? "%0*.*f" : "%*.*f"; snprintf(str, sizeof(str), fmt, digits, decimals, tag->as_float(tag) / (double)divider); } else { - const char* fmt = zero_pad ? "%0*lu" : "%*lu"; + const char *fmt = zero_pad ? "%0*lu" : "%*lu"; snprintf(str, sizeof(str), fmt, digits, tag->as_int(tag) / divider); } sbuf_append(&formatted, str); @@ -676,9 +670,15 @@ tags_expand_template(const char *template, const struct tag_set *tags) const char *fmt = NULL; switch (format) { - case FMT_DEFAULT: fmt = zero_pad ? "%0*ld" : "%*ld"; break; - case FMT_HEX: fmt = zero_pad ? "%0*lx" : "%*lx"; break; - case FMT_OCT: fmt = zero_pad ? "%0*lo" : "%*lo"; break; + case FMT_DEFAULT: + fmt = zero_pad ? "%0*ld" : "%*ld"; + break; + case FMT_HEX: + fmt = zero_pad ? "%0*lx" : "%*lx"; + break; + case FMT_OCT: + fmt = zero_pad ? "%0*lo" : "%*lo"; + break; case FMT_PERCENT: value = (value - min) * 100 / (max - min); fmt = zero_pad ? "%0*lu" : "%*lu"; @@ -690,14 +690,13 @@ tags_expand_template(const char *template, const struct tag_set *tags) case FMT_KIBYTE: case FMT_MIBYTE: case FMT_GIBYTE: { - const long divider = - format == FMT_KBYTE ? 1024 : - format == FMT_MBYTE ? 1024 * 1024 : - format == FMT_GBYTE ? 1024 * 1024 * 1024 : - format == FMT_KIBYTE ? 1000 : - format == FMT_MIBYTE ? 1000 * 1000 : - format == FMT_GIBYTE ? 1000 * 1000 * 1000 : - 1; + const long divider = format == FMT_KBYTE ? 1024 + : format == FMT_MBYTE ? 1024 * 1024 + : format == FMT_GBYTE ? 1024 * 1024 * 1024 + : format == FMT_KIBYTE ? 1000 + : format == FMT_MIBYTE ? 1000 * 1000 + : format == FMT_GIBYTE ? 1000 * 1000 * 1000 + : 1; value /= divider; fmt = zero_pad ? "%0*lu" : "%*lu"; break; @@ -716,9 +715,15 @@ tags_expand_template(const char *template, const struct tag_set *tags) const char *value = NULL; switch (tag->realtime(tag)) { - case TAG_REALTIME_NONE: value = ""; break; - case TAG_REALTIME_SECS: value = "s"; break; - case TAG_REALTIME_MSECS: value = "ms"; break; + case TAG_REALTIME_NONE: + value = ""; + break; + case TAG_REALTIME_SECS: + value = "s"; + break; + case TAG_REALTIME_MSECS: + value = "ms"; + break; } sbuf_append(&formatted, value); @@ -734,8 +739,7 @@ tags_expand_template(const char *template, const struct tag_set *tags) } void -tags_expand_templates(char *expanded[], const char *template[], size_t nmemb, - const struct tag_set *tags) +tags_expand_templates(char *expanded[], const char *template[], size_t nmemb, const struct tag_set *tags) { for (size_t i = 0; i < nmemb; i++) expanded[i] = tags_expand_template(template[i], tags); diff --git a/tag.h b/tag.h index 1e9742a..6149b1e 100644 --- a/tag.h +++ b/tag.h @@ -1,7 +1,7 @@ #pragma once -#include #include +#include enum tag_type { TAG_TYPE_BOOL, @@ -43,21 +43,16 @@ struct tag_set { }; struct tag *tag_new_int(struct module *owner, const char *name, long value); -struct tag *tag_new_int_range( - struct module *owner, const char *name, long value, long min, long max); -struct tag *tag_new_int_realtime( - struct module *owner, const char *name, long value, long min, - long max, enum tag_realtime_unit unit); +struct tag *tag_new_int_range(struct module *owner, const char *name, long value, long min, long max); +struct tag *tag_new_int_realtime(struct module *owner, const char *name, long value, long min, long max, + enum tag_realtime_unit unit); struct tag *tag_new_bool(struct module *owner, const char *name, bool value); struct tag *tag_new_float(struct module *owner, const char *name, double value); -struct tag *tag_new_string( - struct module *owner, const char *name, const char *value); +struct tag *tag_new_string(struct module *owner, const char *name, const char *value); const struct tag *tag_for_name(const struct tag_set *set, const char *name); void tag_set_destroy(struct tag_set *set); /* Utility functions */ char *tags_expand_template(const char *template, const struct tag_set *tags); -void tags_expand_templates( - char *expanded[], const char *template[], size_t nmemb, - const struct tag_set *tags); +void tags_expand_templates(char *expanded[], const char *template[], size_t nmemb, const struct tag_set *tags); diff --git a/xcb.c b/xcb.c index a157c1a..d3d2e30 100644 --- a/xcb.c +++ b/xcb.c @@ -1,16 +1,16 @@ #include "xcb.h" +#include #include #include #include -#include -#include #include #include +#include #if defined(HAVE_XCB_ERRORS) - #include +#include #endif #define LOG_MODULE "xcb" @@ -36,8 +36,7 @@ xcb_atom_t _NET_WM_NAME; static xcb_errors_context_t *err_context; #endif -static void __attribute__((destructor)) -fini(void) +static void __attribute__((destructor)) fini(void) { #if defined(HAVE_XCB_ERRORS) xcb_errors_context_free(err_context); @@ -63,19 +62,17 @@ xcb_init(void) /* Vendor release number */ unsigned release = setup->release_number; - unsigned major = release / 10000000; release %= 10000000; - unsigned minor = release / 100000; release %= 100000; + unsigned major = release / 10000000; + release %= 10000000; + unsigned minor = release / 100000; + release %= 100000; unsigned patch = release / 1000; #endif - LOG_DBG("%.*s %u.%u.%u (protocol: %u.%u)", - xcb_setup_vendor_length(setup), xcb_setup_vendor(setup), - major, minor, patch, - setup->protocol_major_version, - setup->protocol_minor_version); + LOG_DBG("%.*s %u.%u.%u (protocol: %u.%u)", xcb_setup_vendor_length(setup), xcb_setup_vendor(setup), major, minor, + patch, setup->protocol_major_version, setup->protocol_minor_version); - const xcb_query_extension_reply_t *randr = - xcb_get_extension_data(conn, &xcb_randr_id); + const xcb_query_extension_reply_t *randr = xcb_get_extension_data(conn, &xcb_randr_id); if (randr == NULL || !randr->present) { LOG_ERR("RANDR extension not present"); @@ -83,8 +80,7 @@ xcb_init(void) return false; } - const xcb_query_extension_reply_t *render = - xcb_get_extension_data(conn, &xcb_render_id); + const xcb_query_extension_reply_t *render = xcb_get_extension_data(conn, &xcb_render_id); if (render == NULL || !render->present) { LOG_ERR("RENDER extension not present"); @@ -92,18 +88,15 @@ xcb_init(void) return false; } - xcb_randr_query_version_cookie_t randr_cookie = - xcb_randr_query_version(conn, XCB_RANDR_MAJOR_VERSION, - XCB_RANDR_MINOR_VERSION); - xcb_render_query_version_cookie_t render_cookie = - xcb_render_query_version(conn, XCB_RENDER_MAJOR_VERSION, - XCB_RENDER_MINOR_VERSION); + xcb_randr_query_version_cookie_t randr_cookie + = xcb_randr_query_version(conn, XCB_RANDR_MAJOR_VERSION, XCB_RANDR_MINOR_VERSION); + xcb_render_query_version_cookie_t render_cookie + = xcb_render_query_version(conn, XCB_RENDER_MAJOR_VERSION, XCB_RENDER_MINOR_VERSION); xcb_flush(conn); xcb_generic_error_t *e; - xcb_randr_query_version_reply_t *randr_version = - xcb_randr_query_version_reply(conn, randr_cookie, &e); + xcb_randr_query_version_reply_t *randr_version = xcb_randr_query_version_reply(conn, randr_cookie, &e); if (e != NULL) { LOG_ERR("failed to query RANDR version: %s", xcb_error(e)); free(e); @@ -111,8 +104,7 @@ xcb_init(void) return false; } - xcb_render_query_version_reply_t *render_version = - xcb_render_query_version_reply(conn, render_cookie, &e); + xcb_render_query_version_reply_t *render_version = xcb_render_query_version_reply(conn, render_cookie, &e); if (e != NULL) { LOG_ERR("failed to query RENDER version: %s", xcb_error(e)); free(e); @@ -120,10 +112,8 @@ xcb_init(void) return false; } - LOG_DBG("RANDR: %u.%u", - randr_version->major_version, randr_version->minor_version); - LOG_DBG("RENDER: %u.%u", - render_version->major_version, render_version->minor_version); + LOG_DBG("RANDR: %u.%u", randr_version->major_version, randr_version->minor_version); + LOG_DBG("RENDER: %u.%u", render_version->major_version, render_version->minor_version); free(randr_version); free(render_version); @@ -131,7 +121,7 @@ xcb_init(void) /* Cache atoms */ UTF8_STRING = get_atom(conn, "UTF8_STRING"); _NET_WM_PID = get_atom(conn, "_NET_WM_PID"); - _NET_WM_WINDOW_TYPE = get_atom(conn, "_NET_WM_WINDOW_TYPE"); + _NET_WM_WINDOW_TYPE = get_atom(conn, "_NET_WM_WINDOW_TYPE"); _NET_WM_WINDOW_TYPE_DOCK = get_atom(conn, "_NET_WM_WINDOW_TYPE_DOCK"); _NET_WM_STATE = get_atom(conn, "_NET_WM_STATE"); _NET_WM_STATE_ABOVE = get_atom(conn, "_NET_WM_STATE_ABOVE"); @@ -153,10 +143,7 @@ xcb_atom_t get_atom(xcb_connection_t *conn, const char *name) { xcb_generic_error_t *e; - xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply( - conn, - xcb_intern_atom(conn, 0, strlen(name), name), - &e); + xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(conn, xcb_intern_atom(conn, 0, strlen(name), name), &e); if (e != NULL) { LOG_ERR("%s: failed to get atom for %s", name, xcb_error(e)); @@ -182,8 +169,7 @@ char * get_atom_name(xcb_connection_t *conn, xcb_atom_t atom) { xcb_generic_error_t *e; - xcb_get_atom_name_reply_t *reply = xcb_get_atom_name_reply( - conn, xcb_get_atom_name(conn, atom), &e); + xcb_get_atom_name_reply_t *reply = xcb_get_atom_name_reply(conn, xcb_get_atom_name(conn, atom), &e); if (e != NULL) { LOG_ERR("failed to get atom name: %s", xcb_error(e)); @@ -192,8 +178,7 @@ get_atom_name(xcb_connection_t *conn, xcb_atom_t atom) return NULL; } - char *name = strndup( - xcb_get_atom_name_name(reply), xcb_get_atom_name_name_length(reply)); + char *name = strndup(xcb_get_atom_name_name(reply), xcb_get_atom_name_name_length(reply)); LOG_DBG("atom name: %s", name); @@ -207,22 +192,17 @@ xcb_error(const xcb_generic_error_t *error) static char msg[1024]; #if defined(HAVE_XCB_ERRORS) - const char *major = xcb_errors_get_name_for_major_code( - err_context, error->major_code); - const char *minor = xcb_errors_get_name_for_minor_code( - err_context, error->major_code, error->minor_code); + const char *major = xcb_errors_get_name_for_major_code(err_context, error->major_code); + const char *minor = xcb_errors_get_name_for_minor_code(err_context, error->major_code, error->minor_code); const char *extension; - const char *name = xcb_errors_get_name_for_error( - err_context, error->error_code, &extension); + const char *name = xcb_errors_get_name_for_error(err_context, error->error_code, &extension); - snprintf(msg, sizeof(msg), - "major=%s, minor=%s), code=%s, extension=%s, sequence=%u", - major, minor, name, extension, error->sequence); -#else - snprintf(msg, sizeof(msg), "op %hhu:%hu, code %hhu, sequence %hu", - error->major_code, error->minor_code, error->error_code, + snprintf(msg, sizeof(msg), "major=%s, minor=%s), code=%s, extension=%s, sequence=%u", major, minor, name, extension, error->sequence); +#else + snprintf(msg, sizeof(msg), "op %hhu:%hu, code %hhu, sequence %hu", error->major_code, error->minor_code, + error->error_code, error->sequence); #endif return msg; diff --git a/yml.c b/yml.c index a81bdad..ec08101 100644 --- a/yml.c +++ b/yml.c @@ -1,13 +1,13 @@ #include "yml.h" -#include -#include -#include -#include #include +#include +#include +#include +#include -#include #include +#include #define UNUSED __attribute__((unused)) @@ -78,7 +78,8 @@ clone_node(struct yml_node *parent, const struct yml_node *node) break; case DICT: - tll_foreach(node->dict.pairs, it) { + tll_foreach(node->dict.pairs, it) + { struct dict_pair p = { .key = clone_node(clone, it->item.key), .value = clone_node(clone, it->item.value), @@ -88,8 +89,7 @@ clone_node(struct yml_node *parent, const struct yml_node *node) break; case LIST: - tll_foreach(node->list.values, it) - tll_push_back(clone->list.values, clone_node(clone, it->item)); + tll_foreach(node->list.values, it) tll_push_back(clone->list.values, clone_node(clone, it->item)); break; case ROOT: @@ -119,7 +119,8 @@ dict_has_key(const struct yml_node *node, const struct yml_node *key) { assert(node->type == DICT); - tll_foreach(node->dict.pairs, pair) { + tll_foreach(node->dict.pairs, pair) + { if (node_equal(pair->item.key, key)) return true; } @@ -130,7 +131,7 @@ dict_has_key(const struct yml_node *node, const struct yml_node *key) static enum yml_error add_node(struct yml_node *parent, struct yml_node *new_node, yaml_mark_t loc) { - new_node->line = loc.line + 1; /* yaml uses 0-based line numbers */ + new_node->line = loc.line + 1; /* yaml uses 0-based line numbers */ new_node->column = loc.column; switch (parent->type) { @@ -168,8 +169,7 @@ add_node(struct yml_node *parent, struct yml_node *new_node, yaml_mark_t loc) } static void -add_anchor(struct yml_node *root, const char *anchor, - const struct yml_node *node) +add_anchor(struct yml_node *root, const char *anchor, const struct yml_node *node) { assert(root->type == ROOT); @@ -190,25 +190,23 @@ post_process(struct yml_node *node, char **error) break; case SCALAR: - //assert(strcmp(node->scalar.value, "<<") != 0); + // assert(strcmp(node->scalar.value, "<<") != 0); break; case LIST: - tll_foreach(node->list.values, it) - if (!post_process(it->item, error)) - return false; + tll_foreach(node->list.values, it) if (!post_process(it->item, error)) return false; break; case DICT: - tll_foreach(node->dict.pairs, it) { - if (!post_process(it->item.key, error) || - !post_process(it->item.value, error)) - { + tll_foreach(node->dict.pairs, it) + { + if (!post_process(it->item.key, error) || !post_process(it->item.value, error)) { return false; } } - tll_foreach(node->dict.pairs, it) { + tll_foreach(node->dict.pairs, it) + { if (it->item.key->type != SCALAR) continue; @@ -220,19 +218,19 @@ post_process(struct yml_node *node, char **error) * Merge value is a list (of dictionaries) * e.g. <<: [*foo, *bar] */ - tll_foreach(it->item.value->list.values, v_it) { + tll_foreach(it->item.value->list.values, v_it) + { if (v_it->item->type != DICT) { - int cnt = snprintf( - NULL, 0, "%zu:%zu: cannot merge non-dictionary anchor", - v_it->item->line, v_it->item->column); + int cnt = snprintf(NULL, 0, "%zu:%zu: cannot merge non-dictionary anchor", v_it->item->line, + v_it->item->column); *error = malloc(cnt + 1); - snprintf( - *error, cnt + 1, "%zu:%zu: cannot merge non-dictionary anchor", - v_it->item->line, v_it->item->column); + snprintf(*error, cnt + 1, "%zu:%zu: cannot merge non-dictionary anchor", v_it->item->line, + v_it->item->column); return false; } - tll_foreach(v_it->item->dict.pairs, vv_it) { + tll_foreach(v_it->item->dict.pairs, vv_it) + { struct dict_pair p = { .key = vv_it->item.key, .value = vv_it->item.value, @@ -261,17 +259,16 @@ post_process(struct yml_node *node, char **error) * e.g. <<: *foo */ if (it->item.value->type != DICT) { - int cnt = snprintf( - NULL, 0, "%zu:%zu: cannot merge non-dictionary anchor", - it->item.value->line, it->item.value->column); + int cnt = snprintf(NULL, 0, "%zu:%zu: cannot merge non-dictionary anchor", it->item.value->line, + it->item.value->column); *error = malloc(cnt + 1); - snprintf( - *error, cnt + 1, "%zu:%zu: cannot merge non-dictionary anchor", - it->item.value->line, it->item.value->column); + snprintf(*error, cnt + 1, "%zu:%zu: cannot merge non-dictionary anchor", it->item.value->line, + it->item.value->column); return false; } - tll_foreach(it->item.value->dict.pairs, v_it) { + tll_foreach(it->item.value->dict.pairs, v_it) + { struct dict_pair p = { .key = v_it->item.key, .value = v_it->item.value, @@ -307,10 +304,7 @@ post_process(struct yml_node *node, char **error) } static const char * -format_error(enum yml_error err, - const struct yml_node *parent, - const struct yml_node *node, - const char *anchor) +format_error(enum yml_error err, const struct yml_node *parent, const struct yml_node *node, const char *anchor) { static char err_str[512]; @@ -321,11 +315,9 @@ format_error(enum yml_error err, case YML_ERR_DUPLICATE_KEY: { /* Find parent's key (i.e its name) */ - if (parent->parent != NULL && - parent->parent->type == DICT && - node->type == SCALAR) - { - tll_foreach(parent->parent->dict.pairs, pair) { + if (parent->parent != NULL && parent->parent->type == DICT && node->type == SCALAR) { + tll_foreach(parent->parent->dict.pairs, pair) + { if (pair->item.value != parent) continue; @@ -335,17 +327,14 @@ format_error(enum yml_error err, assert(pair->item.key->type == SCALAR); assert(node->type == SCALAR); - snprintf(err_str, sizeof(err_str), - "%s: duplicate key: '%s'", - pair->item.key->scalar.value, + snprintf(err_str, sizeof(err_str), "%s: duplicate key: '%s'", pair->item.key->scalar.value, node->scalar.value); return err_str; } } if (node->type == SCALAR) { - snprintf(err_str, sizeof(err_str), - "duplicate key: %s", node->scalar.value); + snprintf(err_str, sizeof(err_str), "duplicate key: %s", node->scalar.value); } else snprintf(err_str, sizeof(err_str), "duplicate key"); break; @@ -353,22 +342,20 @@ format_error(enum yml_error err, case YML_ERR_INVALID_ANCHOR: if (parent->parent != NULL && parent->parent->type == DICT) { - tll_foreach(parent->parent->dict.pairs, pair) { + tll_foreach(parent->parent->dict.pairs, pair) + { if (pair->item.value != parent) continue; if (pair->item.key->type != SCALAR) break; - snprintf(err_str, sizeof(err_str), - "%s: invalid anchor: %s", - pair->item.key->scalar.value, + snprintf(err_str, sizeof(err_str), "%s: invalid anchor: %s", pair->item.key->scalar.value, anchor != NULL ? anchor : ""); return err_str; } } - snprintf(err_str, sizeof(err_str), "invalid anchor: %s", - anchor != NULL ? anchor : ""); + snprintf(err_str, sizeof(err_str), "invalid anchor: %s", anchor != NULL ? anchor : ""); break; case YML_ERR_UNKNOWN: @@ -403,19 +390,12 @@ yml_load(FILE *yml, char **error) yaml_event_t event; if (!yaml_parser_parse(&yaml, &event)) { if (error != NULL) { - int cnt = snprintf( - NULL, 0, "%zu:%zu: %s %s", - yaml.problem_mark.line + 1, - yaml.problem_mark.column, - yaml.problem, - yaml.context != NULL ? yaml.context : ""); + int cnt = snprintf(NULL, 0, "%zu:%zu: %s %s", yaml.problem_mark.line + 1, yaml.problem_mark.column, + yaml.problem, yaml.context != NULL ? yaml.context : ""); *error = malloc(cnt + 1); - snprintf(*error, cnt + 1, "%zu:%zu: %s %s", - yaml.problem_mark.line + 1, - yaml.problem_mark.column, - yaml.problem, - yaml.context != NULL ? yaml.context : ""); + snprintf(*error, cnt + 1, "%zu:%zu: %s %s", yaml.problem_mark.line + 1, yaml.problem_mark.column, + yaml.problem, yaml.context != NULL ? yaml.context : ""); } goto err_no_error_formatting; @@ -466,9 +446,7 @@ yml_load(FILE *yml, char **error) } if (!got_match) { - error_str = format_error( - YML_ERR_INVALID_ANCHOR, n, NULL, - (const char *)event.data.alias.anchor); + error_str = format_error(YML_ERR_INVALID_ANCHOR, n, NULL, (const char *)event.data.alias.anchor); yaml_event_delete(&event); goto err; } @@ -478,8 +456,7 @@ yml_load(FILE *yml, char **error) case YAML_SCALAR_EVENT: { struct yml_node *new_scalar = calloc(1, sizeof(*new_scalar)); new_scalar->type = SCALAR; - new_scalar->scalar.value = strndup( - (const char*)event.data.scalar.value, event.data.scalar.length); + new_scalar->scalar.value = strndup((const char *)event.data.scalar.value, event.data.scalar.length); enum yml_error err = add_node(n, new_scalar, event.start_mark); if (err != YML_ERR_NONE) { @@ -569,23 +546,13 @@ yml_load(FILE *yml, char **error) err: if (error_str != NULL) { - int cnt = snprintf( - NULL, 0, "%zu:%zu: %s", - yaml.mark.line + 1, - yaml.mark.column, - error_str); + int cnt = snprintf(NULL, 0, "%zu:%zu: %s", yaml.mark.line + 1, yaml.mark.column, error_str); *error = malloc(cnt + 1); - snprintf( - *error, cnt + 1, "%zu:%zu: %s", - yaml.mark.line + 1, - yaml.mark.column, - error_str); + snprintf(*error, cnt + 1, "%zu:%zu: %s", yaml.mark.line + 1, yaml.mark.column, error_str); } else { - int cnt = snprintf(NULL, 0, "%zu:%zu: unknown error", - yaml.mark.line + 1, yaml.mark.column); + int cnt = snprintf(NULL, 0, "%zu:%zu: unknown error", yaml.mark.line + 1, yaml.mark.column); *error = malloc(cnt + 1); - snprintf(*error, cnt + 1, "%zu:%zu: unknown error", - yaml.mark.line + 1, yaml.mark.column); + snprintf(*error, cnt + 1, "%zu:%zu: unknown error", yaml.mark.line + 1, yaml.mark.column); } err_no_error_formatting: @@ -617,7 +584,8 @@ yml_destroy(struct yml_node *node) break; case DICT: - tll_foreach(node->dict.pairs, it) { + tll_foreach(node->dict.pairs, it) + { yml_destroy(it->item.key); yml_destroy(it->item.value); } @@ -659,13 +627,12 @@ yml_get_(struct yml_node const *node, char const *_path, bool value) char *path = strdup(_path); - for (const char *part = strtok(path, "."), *next_part = strtok(NULL, "."); - part != NULL; - part = next_part, next_part = strtok(NULL, ".")) - { + for (const char *part = strtok(path, "."), *next_part = strtok(NULL, "."); part != NULL; + part = next_part, next_part = strtok(NULL, ".")) { assert(yml_is_dict(node)); - tll_foreach(node->dict.pairs, it) { + tll_foreach(node->dict.pairs, it) + { assert(yml_is_scalar(it->item.key)); if (strcmp(it->item.key->scalar.value, part) == 0) { if (next_part == NULL) { @@ -694,7 +661,8 @@ yml_get_value(const struct yml_node *node, const char *_path) } struct yml_node const * -yml_get_key(struct yml_node const *node, char const *_path) { +yml_get_key(struct yml_node const *node, char const *_path) +{ return yml_get_(node, _path, false); } @@ -702,7 +670,8 @@ struct yml_list_iter yml_list_iter(const struct yml_node *list) { assert(yml_is_list(list)); - tll_foreach(list->list.values, it) { + tll_foreach(list->list.values, it) + { return (struct yml_list_iter){ .node = it->item, .private = it, @@ -735,9 +704,7 @@ yml_list_length(const struct yml_node *list) assert(yml_is_list(list)); size_t length = 0; - for (struct yml_list_iter it = yml_list_iter(list); - it.node != NULL; - yml_list_next(&it), length++) + for (struct yml_list_iter it = yml_list_iter(list); it.node != NULL; yml_list_next(&it), length++) ; return length; @@ -748,7 +715,8 @@ yml_dict_iter(const struct yml_node *dict) { assert(yml_is_dict(dict)); - tll_foreach(dict->dict.pairs, it) { + tll_foreach(dict->dict.pairs, it) + { return (struct yml_dict_iter){ .key = it->item.key, .value = it->item.value, @@ -756,7 +724,7 @@ yml_dict_iter(const struct yml_node *dict) }; } - return (struct yml_dict_iter) { + return (struct yml_dict_iter){ .key = NULL, .value = NULL, .private1 = NULL, @@ -827,18 +795,12 @@ _as_bool(const struct yml_node *value, bool *ret) return false; const char *v = yml_value_as_string(value); - if (strcasecmp(v, "y") == 0 || - strcasecmp(v, "yes") == 0 || - strcasecmp(v, "true") == 0 || - strcasecmp(v, "on") == 0) - { + if (strcasecmp(v, "y") == 0 || strcasecmp(v, "yes") == 0 || strcasecmp(v, "true") == 0 + || strcasecmp(v, "on") == 0) { *ret = true; return true; - } else if (strcasecmp(v, "n") == 0 || - strcasecmp(v, "no") == 0 || - strcasecmp(v, "false") == 0 || - strcasecmp(v, "off") == 0) - { + } else if (strcasecmp(v, "n") == 0 || strcasecmp(v, "no") == 0 || strcasecmp(v, "false") == 0 + || strcasecmp(v, "off") == 0) { *ret = false; return true; } @@ -885,7 +847,8 @@ _print_node(const struct yml_node *n, int indent) break; case DICT: - tll_foreach(n->dict.pairs, it) { + tll_foreach(n->dict.pairs, it) + { _print_node(it->item.key, indent); printf(": "); @@ -900,7 +863,8 @@ _print_node(const struct yml_node *n, int indent) break; case LIST: - tll_foreach(n->list.values, it) { + tll_foreach(n->list.values, it) + { printf("%*s- ", indent, ""); if (it->item->type != SCALAR) { printf("\n"); diff --git a/yml.h b/yml.h index 0e5eca4..784252c 100644 --- a/yml.h +++ b/yml.h @@ -1,6 +1,6 @@ #pragma once -#include #include +#include struct yml_node; @@ -11,10 +11,8 @@ bool yml_is_scalar(const struct yml_node *node); bool yml_is_dict(const struct yml_node *node); bool yml_is_list(const struct yml_node *node); -const struct yml_node *yml_get_value( - const struct yml_node *node, const char *path); -const struct yml_node *yml_get_key( - struct yml_node const *node, char const *path); +const struct yml_node *yml_get_value(const struct yml_node *node, const char *path); +const struct yml_node *yml_get_key(struct yml_node const *node, char const *path); struct yml_list_iter { const struct yml_node *node; From 3c572c70c98f01cbfb892a1bb18da3f17d2db81c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 9 Apr 2024 16:34:53 +0200 Subject: [PATCH 542/611] wayland: silence compiler warning ... by ensuring 'layer' is always initialized, to _something_. --- bar/wayland.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bar/wayland.c b/bar/wayland.c index 50dc62d..3d8e4e0 100644 --- a/bar/wayland.c +++ b/bar/wayland.c @@ -765,7 +765,7 @@ create_surface(struct wayland_backend *backend) wl_surface_add_listener(backend->surface, &surface_listener, backend); - enum zwlr_layer_shell_v1_layer layer; + enum zwlr_layer_shell_v1_layer layer = ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM; switch (bar->layer) { case BAR_LAYER_BACKGROUND: From 13f46a314a433e899671212545c5468a37f3150f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 11 Apr 2024 15:47:38 +0200 Subject: [PATCH 543/611] examples: conf: laptop: repair network modules Closes #374 --- examples/configurations/laptop.conf | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/examples/configurations/laptop.conf b/examples/configurations/laptop.conf index cee2639..e57ebb3 100644 --- a/examples/configurations/laptop.conf +++ b/examples/configurations/laptop.conf @@ -174,13 +174,14 @@ bar: default: {empty: {}} conditions: name == enp1s0: - conditions: - ~carrier: {empty: {}} - carrier: - map: - default: {string: {text: , font: *awesome, foreground: ffffff66}} - conditions: - state == up && ipv4 != "": {string: {text: , font: *awesome}} + map: + conditions: + ~carrier: {empty: {}} + carrier: + map: + default: {string: {text: , font: *awesome, foreground: ffffff66}} + conditions: + state == up && ipv4 != "": {string: {text: , font: *awesome}} - network: poll-interval: 1000 content: @@ -198,10 +199,10 @@ bar: - string: {text: , font: *awesome} - string: {text: "{ssid} {dl-speed:mb}/{ul-speed:mb} Mb/s"} - conditions: - ipv4 == "": - - string: {text: , font: *awesome, foreground: ffffff66} - - string: {text: "{ssid} {dl-speed:mb}/{ul-speed:mb} Mb/s", foreground: ffffff66} + conditions: + ipv4 == "": + - string: {text: , font: *awesome, foreground: ffffff66} + - string: {text: "{ssid} {dl-speed:mb}/{ul-speed:mb} Mb/s", foreground: ffffff66} - alsa: card: hw:PCH mixer: Master From be01eeb1de4e143e6ee370891219fae368cbc484 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 17 Apr 2024 09:52:46 +0200 Subject: [PATCH 544/611] ci: set 'event' filters on all 'when'-statements --- .woodpecker.yaml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/.woodpecker.yaml b/.woodpecker.yaml index ca65b29..0f3a16b 100644 --- a/.woodpecker.yaml +++ b/.woodpecker.yaml @@ -1,9 +1,9 @@ steps: - name: codespell when: - branch: - - master - - releases/* + - event: [manual, pull_request] + - event: [push, tag] + branch: [master, releases/*] image: alpine:latest commands: - apk add python3 @@ -16,9 +16,9 @@ steps: - name: subprojects when: - branch: - - master - - releases/* + - event: [manual, pull_request] + - event: [push, tag] + branch: [master, releases/*] image: alpine:latest commands: - apk add git @@ -29,9 +29,9 @@ steps: - name: x64 when: - branch: - - master - - releases/* + - event: [manual, pull_request] + - event: [push, tag] + branch: [master, releases/*] depends_on: [subprojects] image: alpine:latest commands: @@ -88,9 +88,9 @@ steps: - name: x86 when: - branch: - - master - - releases/* + - event: [manual, pull_request] + - event: [push, tag] + branch: [master, releases/*] depends_on: [subprojects] image: i386/alpine:latest commands: From 3136310aded817a4e7e23c0441eb64882dd99d41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 17 Apr 2024 09:53:00 +0200 Subject: [PATCH 545/611] ci: install openssl explicitly, to fix missing SSL module in Python --- .woodpecker.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.woodpecker.yaml b/.woodpecker.yaml index 0f3a16b..8aee7ee 100644 --- a/.woodpecker.yaml +++ b/.woodpecker.yaml @@ -6,6 +6,7 @@ steps: branch: [master, releases/*] image: alpine:latest commands: + - apk add openssl - apk add python3 - apk add py3-pip - python3 -m venv codespell-venv From cae07a36ff79bc2b22bf757105a30bba9f975686 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 17 Apr 2024 10:30:29 +0200 Subject: [PATCH 546/611] changelog: prepare for 1.11.0 --- CHANGELOG.md | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 09f8c93..0e07a35 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -* [Unreleased](#unreleased) +* [1.11.0](#1-11-0) * [1.10.0](#1-10-0) * [1.9.0](#1-9-0) * [1.8.0](#1-8-0) @@ -11,7 +11,8 @@ * [1.5.0](#1-5-0) -## Unreleased +## 1.11.0 + ### Added * battery: current smoothing, for improved discharge estimates. @@ -35,8 +36,6 @@ [355]: https://codeberg.org/dnkl/yambar/pulls/355 -### Deprecated -### Removed ### Fixed * Compiler error _‘fmt’ may be used uninitialized_ ([#311][311]). @@ -63,9 +62,20 @@ [369]: https://codeberg.org/dnkl/yambar/issues/369 -### Security ### Contributors +* Delgan +* Haden Collins +* Jordan Isaacs +* kotyk +* Leonardo Hernández Hernández +* oob +* rdbo +* Sertonix +* steovd +* Väinö Mäkelä +* Yiyu Zhou + ## 1.10.0 From a2d30b96fbb026621ec11692272f0117f35a00fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 17 Apr 2024 10:30:54 +0200 Subject: [PATCH 547/611] meson/pkgbuild: bump version to 1.11.0 --- PKGBUILD | 2 +- PKGBUILD.wayland-only | 2 +- meson.build | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/PKGBUILD b/PKGBUILD index 5aaea5d..6fc2d69 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -1,5 +1,5 @@ pkgname=yambar -pkgver=1.10.0 +pkgver=1.11.0 pkgrel=1 pkgdesc="Simplistic and highly configurable status panel for X and Wayland" changelog=CHANGELOG.md diff --git a/PKGBUILD.wayland-only b/PKGBUILD.wayland-only index 4d11120..266fc06 100644 --- a/PKGBUILD.wayland-only +++ b/PKGBUILD.wayland-only @@ -1,5 +1,5 @@ pkgname=yambar-wayland -pkgver=1.10.0 +pkgver=1.11.0 pkgrel=1 pkgdesc="Simplistic and highly configurable status panel for Wayland" arch=('x86_64' 'aarch64') diff --git a/meson.build b/meson.build index f0369cd..d760e94 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('yambar', 'c', - version: '1.10.0', + version: '1.11.0', license: 'MIT', meson_version: '>=0.59.0', default_options: ['c_std=c18', From 6f3952819f23698fd4b7fe11bb3c62a5d84b49d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 17 Apr 2024 10:42:56 +0200 Subject: [PATCH 548/611] changelog: add new 'unreleased' section --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e07a35..5221a37 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # Changelog +* [Unreleased](#unreleased) * [1.11.0](#1-11-0) * [1.10.0](#1-10-0) * [1.9.0](#1-9-0) @@ -11,6 +12,16 @@ * [1.5.0](#1-5-0) +## Unreleased +### Added +### Changed +### Deprecated +### Removed +### Fixed +### Security +### Contributors + + ## 1.11.0 ### Added From 547cef5afbfbcbf9fe78705c7b5661059b706346 Mon Sep 17 00:00:00 2001 From: Sertonix Date: Mon, 22 Apr 2024 15:47:10 +0200 Subject: [PATCH 549/611] network: fix missing break in switch statement This can cause the first character of the string to be read as an iface state. Fixes https://codeberg.org/dnkl/yambar/issues/377 --- CHANGELOG.md | 6 ++++++ modules/network.c | 1 + 2 files changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5221a37..3152882 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,12 @@ ### Deprecated ### Removed ### Fixed + +* network: fix missing break in switch statement([#377][377]) + +[377]: https://codeberg.org/dnkl/yambar/issues/377 + + ### Security ### Contributors diff --git a/modules/network.c b/modules/network.c index 4bb0fb2..adb8f68 100644 --- a/modules/network.c +++ b/modules/network.c @@ -595,6 +595,7 @@ handle_link(struct module *mod, uint16_t type, const struct ifinfomsg *msg, size iface->name = strdup((const char *)RTA_DATA(attr)); LOG_DBG("%s: index=%d", iface->name, iface->index); mtx_unlock(&mod->lock); + break; case IFLA_OPERSTATE: { uint8_t operstate = *(const uint8_t *)RTA_DATA(attr); if (iface->state == operstate) From 3a7455913f85f4f8887a4fb3aecc4f8306a2010a Mon Sep 17 00:00:00 2001 From: Birger Schacht Date: Sat, 20 Apr 2024 10:13:05 +0000 Subject: [PATCH 550/611] fix: typo Probaly -> Probably --- modules/network.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/network.c b/modules/network.c index adb8f68..9ed9964 100644 --- a/modules/network.c +++ b/modules/network.c @@ -670,7 +670,7 @@ handle_address(struct module *mod, uint16_t type, const struct ifaddrmsg *msg, s } if (iface == NULL) { - LOG_ERR("failed to find network interface with index %d. Probaly a yambar bug", msg->ifa_index); + LOG_ERR("failed to find network interface with index %d. Probably a yambar bug", msg->ifa_index); return; } From 00234696fe5b09a1088254d2ee62955b3273e5a2 Mon Sep 17 00:00:00 2001 From: betazoid Date: Thu, 25 Apr 2024 21:02:03 +0000 Subject: [PATCH 551/611] Add examples/river-minimal.yml --- examples/river-minimal.yml | 69 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 examples/river-minimal.yml diff --git a/examples/river-minimal.yml b/examples/river-minimal.yml new file mode 100644 index 0000000..2ca225f --- /dev/null +++ b/examples/river-minimal.yml @@ -0,0 +1,69 @@ +bg_default: &bg_default {stack: [{background: {color: 81A1C1ff}}, {underline: {size: 4, color: D8DEE9ff}}]} +bar: + height: 32 + location: top + background: 000000ff + font: NotoSans:pixelsize=16 + + right: + - clock: + content: + - string: {text: , font: "Font Awesome 6 Free:style=solid:size=12"} + - string: {text: "{date}", right-margin: 5} + - string: {text: , font: "Font Awesome 6 Free:style=solid:size=12"} + - string: {text: "{time} "} + left: + - river: + anchors: + - base: &river_base + left-margin: 10 + right-margin: 13 + default: {string: {text: }} + conditions: + id == 1: {string: {text: 1}} + id == 2: {string: {text: 2}} + id == 3: {string: {text: 3}} + id == 4: {string: {text: 4}} + id == 5: {string: {text: 5}} + + content: + map: + on-click: + left: sh -c "riverctl set-focused-tags $((1 << ({id} - 1)))" + right: sh -c "riverctl toggle-focused-tags $((1 << ({id} -1)))" + middle: sh -c "riverctl toggle-view-tags $((1 << ({id} -1)))" + conditions: + state == urgent: + map: + <<: *river_base + deco: {background: {color: D08770ff}} + state == focused: + map: + <<: *river_base + deco: *bg_default + state == visible && ~occupied: + map: + <<: *river_base + state == visible && occupied: + map: + <<: *river_base + deco: *bg_default + state == unfocused: + map: + <<: *river_base + state == invisible && ~occupied: {empty: {}} + state == invisible && occupied: + map: + <<: *river_base + deco: {underline: {size: 3, color: ea6962ff}} + + + center: + - foreign-toplevel: + content: + map: + conditions: + ~activated: {empty: {}} + activated: + - string: {text: " {app-id}", foreground: ffa0a0ff} + - string: {text: ": {title}"} From b3313cefc67dfe6da61667b6b2ed9d8ebb0a829c Mon Sep 17 00:00:00 2001 From: Delgan Date: Thu, 2 May 2024 16:28:51 +0000 Subject: [PATCH 552/611] Fix remaining typos in the codebase (and update CI checks) --- .woodpecker.yaml | 2 +- bar/bar.c | 2 +- examples/scripts/dwl-tags.sh | 2 +- examples/scripts/pacman.sh | 2 +- modules/disk-io.c | 2 +- modules/dwl.c | 2 +- modules/pipewire.c | 12 ++++++------ 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.woodpecker.yaml b/.woodpecker.yaml index 8aee7ee..e7c9151 100644 --- a/.woodpecker.yaml +++ b/.woodpecker.yaml @@ -12,7 +12,7 @@ steps: - python3 -m venv codespell-venv - source codespell-venv/bin/activate - pip install codespell - - codespell README.md CHANGELOG.md *.c *.h doc/*.scd + - codespell README.md CHANGELOG.md *.c *.h doc/*.scd bar decorations modules particles examples - deactivate - name: subprojects diff --git a/bar/bar.c b/bar/bar.c index 3890bc0..109f210 100644 --- a/bar/bar.c +++ b/bar/bar.c @@ -28,7 +28,7 @@ #define max(x, y) ((x) > (y) ? (x) : (y)) /* - * Calculate total width of left/center/rigth groups. + * Calculate total width of left/center/right groups. * Note: begin_expose() must have been called */ static void diff --git a/examples/scripts/dwl-tags.sh b/examples/scripts/dwl-tags.sh index b1dbe4c..4999548 100755 --- a/examples/scripts/dwl-tags.sh +++ b/examples/scripts/dwl-tags.sh @@ -19,7 +19,7 @@ # # Now the fun part # -# Exemple configuration: +# Example configuration: # # - script: # path: /absolute/path/to/dwl-tags.sh diff --git a/examples/scripts/pacman.sh b/examples/scripts/pacman.sh index 83e2a3f..5026b5a 100755 --- a/examples/scripts/pacman.sh +++ b/examples/scripts/pacman.sh @@ -12,7 +12,7 @@ # {aur} int number of aur packages # {pkg} int sum of both # -# Exemples configuration: +# Examples configuration: # - script: # path: /absolute/path/to/pacman.sh # args: [] diff --git a/modules/disk-io.c b/modules/disk-io.c index 90abdba..015715f 100644 --- a/modules/disk-io.c +++ b/modules/disk-io.c @@ -129,7 +129,7 @@ refresh_device_stats(struct private *m) while ((read = getline(&line, &len, fp)) != -1) { /* - * For an explanation of the fields bellow, see + * For an explanation of the fields below, see * https://www.kernel.org/doc/Documentation/ABI/testing/procfs-diskstats */ uint8_t major_number = 0; diff --git a/modules/dwl.c b/modules/dwl.c index 4d9e8fa..a0d5797 100644 --- a/modules/dwl.c +++ b/modules/dwl.c @@ -231,7 +231,7 @@ process_line(char *line, struct module *module) /* No need to check error IMHO */ *target = strtoul(string, NULL, 10); - /* Populate informations */ + /* Populate information */ if (index == 6) { for (size_t id = 1; id <= private->number_of_tags; ++id) { uint32_t mask = 1 << (id - 1); diff --git a/modules/pipewire.c b/modules/pipewire.c index 1a6ab1f..e614a0a 100644 --- a/modules/pipewire.c +++ b/modules/pipewire.c @@ -33,7 +33,7 @@ struct output_informations { uint32_t device_id; uint32_t card_profile_device_id; - /* informations */ + /* information */ bool muted; uint16_t linear_volume; /* classic volume */ uint16_t cubic_volume; /* volume a la pulseaudio */ @@ -333,7 +333,7 @@ device_events_param(void *userdata, int seq, uint32_t id, uint32_t index, uint32 X_FREE_SET(route->icon_name, X_STRDUP(data.icon_name)); route->direction = data.direction; - /* set missing informations if possible */ + /* set missing information if possible */ struct private *private = device->data->module->private; struct node *binded_node = NULL; struct output_informations *output_informations = NULL; @@ -358,7 +358,7 @@ device_events_param(void *userdata, int seq, uint32_t id, uint32_t index, uint32 if (output_informations->card_profile_device_id != route->profile_device_id) return; - /* Update missing informations */ + /* Update missing information */ X_FREE_SET(output_informations->form_factor, X_STRDUP(route->form_factor)); X_FREE_SET(output_informations->icon, X_STRDUP(route->icon_name)); @@ -384,7 +384,7 @@ node_events_info(void *userdata, struct pw_node_info const *info) for (size_t i = 0; i < info->n_params; ++i) { if (info->params[i].id == SPA_PARAM_Props) { void *target_node = (node_data->is_sink ? data->node_sink : data->node_source); - /* Found it, will emit a param event, the parem will then be handled + /* Found it, will emit a param event, the param will then be handled * in node_events_param */ pw_node_enum_params(target_node, 0, info->params[i].id, 0, -1, NULL); break; @@ -419,7 +419,7 @@ node_events_info(void *userdata, struct pw_node_info const *info) output_informations->card_profile_device_id = value; } - /* Device's informations has an more important priority than node's informations */ + /* Device's information has an more important priority than node's information */ /* icon_name */ struct route *route = node_find_route(data, node_data->is_sink); if (route != NULL && route->icon_name != NULL) @@ -659,7 +659,7 @@ static void try_to_bind_node(struct node_data *node_data, char const *target_name, struct node **target_node, void **target_proxy, struct spa_hook *target_listener) { - /* profile deactived */ + /* profile deactivated */ if (target_name == NULL) return; From a467f5667769a323dea2f64391969f8d979087df Mon Sep 17 00:00:00 2001 From: QuincePie Date: Sat, 18 May 2024 18:17:10 -0500 Subject: [PATCH 553/611] i3: Handle FALLBACK output for workspaces. sway moves the workspace to fallback_output when there is no output. For example: when all the screens are off. This commit adds an ignore for the fallback output. --- modules/i3.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/modules/i3.c b/modules/i3.c index 73bd9d6..5cb6e01 100644 --- a/modules/i3.c +++ b/modules/i3.c @@ -514,7 +514,6 @@ handle_workspace_event(int sock, int type, const struct json_object *json, void else if (is_move) { struct workspace *w = workspace_lookup(m, current_id); - assert(w != NULL); struct json_object *_current_output; if (!json_object_object_get_ex(current, "output", &_current_output)) { @@ -522,16 +521,22 @@ handle_workspace_event(int sock, int type, const struct json_object *json, void mtx_unlock(&mod->lock); return false; } + const char *current_output_string = json_object_get_string(_current_output); - free(w->output); - w->output = strdup(json_object_get_string(_current_output)); + /* Ignore fallback_output ("For when there's no connected outputs") */ + if (strcmp(current_output_string, "FALLBACK") != 0) { - /* - * If the moved workspace was focused, schedule a full update because - * visibility for other workspaces may have changed. - */ - if (w->focused) { - i3_send_pkg(sock, I3_IPC_MESSAGE_TYPE_GET_WORKSPACES, NULL); + assert(w != NULL); + free(w->output); + w->output = strdup(current_output_string); + + /* + * If the moved workspace was focused, schedule a full update because + * visibility for other workspaces may have changed. + */ + if (w->focused) { + i3_send_pkg(sock, I3_IPC_MESSAGE_TYPE_GET_WORKSPACES, NULL); + } } } From b8a93a26736c076d071bfc402fd1de6b136fe5ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 20 May 2024 07:45:49 +0200 Subject: [PATCH 554/611] changelog: i3/sway crash fix for output being turned on/off --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3152882..de5c52a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,9 +19,11 @@ ### Removed ### Fixed -* network: fix missing break in switch statement([#377][377]) +* network: fix missing break in switch statement ([#377][377]). +* i3/sway: crash when output is turned off an on ([#300][300]). [377]: https://codeberg.org/dnkl/yambar/issues/377 +[300]: https://codeberg.org/dnkl/yambar/issues/300 ### Security From 70efd7d15c7752754bab0c518321cdef5289126e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 20 May 2024 09:21:29 +0200 Subject: [PATCH 555/611] doc: yambar-particles: document the hard-coded spacing of short-form lists Closes #385 --- doc/yambar-particles.5.scd | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/yambar-particles.5.scd b/doc/yambar-particles.5.scd index 5dc858b..d9b0e56 100644 --- a/doc/yambar-particles.5.scd +++ b/doc/yambar-particles.5.scd @@ -214,6 +214,11 @@ content: - string: ... ``` +Note that the short form has a hard-coded *right-spacing* of 2. This +cannot be changed. If you want a different spacing, you must use an +explicit list particle (i.e. the long form). + + # MAP This particle maps the values of a specific tag to different From 0bea49b75e2cf3fe347bce3447e9dfbaaaaf2c8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 20 May 2024 09:33:45 +0200 Subject: [PATCH 556/611] module/river: return empty particle list when river is not running Closes #384 --- CHANGELOG.md | 7 +++++++ modules/river.c | 8 ++++++++ 2 files changed, 15 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index de5c52a..eedc5f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,13 @@ ## Unreleased ### Added ### Changed + +* `river`: expand to an empty list of particles when river is not + running ([#384][384]). + +[384]: https://codeberg.org/dnkl/yambar/issues/384 + + ### Deprecated ### Removed ### Fixed diff --git a/modules/river.c b/modules/river.c index 2619c62..ec25f9f 100644 --- a/modules/river.c +++ b/modules/river.c @@ -52,6 +52,7 @@ struct seat { struct private { struct module *mod; + bool is_running; struct zxdg_output_manager_v1 *xdg_output_manager; struct zriver_status_manager_v1 *status_manager; struct particle *template; @@ -88,6 +89,11 @@ content(struct module *mod) mtx_lock(&m->mod->lock); + if (!m->is_running) { + mtx_unlock(&m->mod->lock); + return dynlist_exposable_new(NULL, 0, 0, 0); + } + uint32_t urgent = 0; uint32_t occupied = 0; uint32_t output_focused = 0; @@ -685,6 +691,8 @@ run(struct module *mod) goto out; } + m->is_running = true; + wl_display_roundtrip(display); while (true) { From 20659d3350ab0044c41b781fa4df64e1164d2051 Mon Sep 17 00:00:00 2001 From: Tomas Slusny Date: Thu, 9 May 2024 03:55:25 +0200 Subject: [PATCH 557/611] Add support for environment variable references The format is key: ${env_variable} Closes #96 Signed-off-by: Tomas Slusny --- CHANGELOG.md | 5 +++++ doc/yambar.5.scd | 5 +++++ yml.c | 40 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 49 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eedc5f6..3c13106 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,11 @@ ## Unreleased ### Added + +* environment variable substitution in config files ([#388][388]). + +[388]: https://codeberg.org/dnkl/yambar/issues/388 + ### Changed * `river`: expand to an empty list of particles when river is not diff --git a/doc/yambar.5.scd b/doc/yambar.5.scd index 1b06939..cc03f8c 100644 --- a/doc/yambar.5.scd +++ b/doc/yambar.5.scd @@ -23,6 +23,11 @@ types that are frequently used: - 000000ff: black, no transparency - 00ff00ff: green, no transparency - ff000099: red, semi-transparent +- *environment reference*: a string that contains format ${VAR}. This will be + replaced by the value of the environment variable VAR. Example: + - ${HOME} + - ${HOME}/.config/yambar + - ENV is ${ENV}, ENV2 is ${ENV2} # FORMAT [[ *Name* diff --git a/yml.c b/yml.c index ec08101..73dba66 100644 --- a/yml.c +++ b/yml.c @@ -366,6 +366,44 @@ format_error(enum yml_error err, const struct yml_node *parent, const struct yml return err_str; } +static char * +replace_env_variables(const char *str, size_t len) +{ + char *result = strndup(str, len); + char *start, *end, *key, *env_value; + char* prefix = "${"; + char* suffix = "}"; + size_t pref_len = 2; + size_t suff_len = 1; + size_t key_len; + + while ((start = strstr(result, prefix)) && (end = strstr(start, suffix))) { + key_len = end - start - pref_len; + key = strndup(start + pref_len, key_len); + env_value = getenv(key); + + if (env_value) { + size_t result_len = strlen(result); + size_t new_len = result_len - key_len - pref_len - suff_len + strlen(env_value); + char *new_result = malloc(new_len + 1); + + strncpy(new_result, result, start - result); + new_result[start - result] = '\0'; + strcat(new_result, env_value); + strcat(new_result, end + 1); + + free(result); + result = new_result; + } else { + memmove(start, end + 1, strlen(end + 1) + 1); + } + + free(key); + } + + return result; +} + struct yml_node * yml_load(FILE *yml, char **error) { @@ -456,7 +494,7 @@ yml_load(FILE *yml, char **error) case YAML_SCALAR_EVENT: { struct yml_node *new_scalar = calloc(1, sizeof(*new_scalar)); new_scalar->type = SCALAR; - new_scalar->scalar.value = strndup((const char *)event.data.scalar.value, event.data.scalar.length); + new_scalar->scalar.value = replace_env_variables((const char *)event.data.scalar.value, event.data.scalar.length); enum yml_error err = add_node(n, new_scalar, event.start_mark); if (err != YML_ERR_NONE) { From 3431d5fc7526a7c160bb12a158773b39927845f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 9 Jun 2024 10:05:21 +0200 Subject: [PATCH 558/611] yml: replace_env_variables(): const:ify function variables --- yml.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/yml.c b/yml.c index 73dba66..4769d38 100644 --- a/yml.c +++ b/yml.c @@ -370,14 +370,17 @@ static char * replace_env_variables(const char *str, size_t len) { char *result = strndup(str, len); - char *start, *end, *key, *env_value; - char* prefix = "${"; - char* suffix = "}"; - size_t pref_len = 2; - size_t suff_len = 1; + char *start, *key; + const char *end, *env_value; + const char* prefix = "${"; + const char* suffix = "}"; + const size_t pref_len = 2; + const size_t suff_len = 1; size_t key_len; - while ((start = strstr(result, prefix)) && (end = strstr(start, suffix))) { + while ((start = strstr(result, prefix)) != NULL && + (end = strstr(start, suffix)) != NULL) + { key_len = end - start - pref_len; key = strndup(start + pref_len, key_len); env_value = getenv(key); From 9cc5e0f7a7398f88f80a3520d44feecb645a0e3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 9 Jun 2024 10:08:38 +0200 Subject: [PATCH 559/611] module/network: plug memory leak Free the 'ifaces' list, not just its contents. --- modules/network.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/network.c b/modules/network.c index 9ed9964..c2f6ec3 100644 --- a/modules/network.c +++ b/modules/network.c @@ -119,7 +119,10 @@ destroy(struct module *mod) if (m->urandom_fd >= 0) close(m->urandom_fd); - tll_foreach(m->ifaces, it) free_iface(it->item); + tll_foreach(m->ifaces, it) { + free_iface(it->item); + tll_remove(m->ifaces, it); + } free(m); module_default_destroy(mod); From 8422e7e0b1baebb9addf9d18244bdbacb3e539f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 9 Jun 2024 10:12:23 +0200 Subject: [PATCH 560/611] doc: yambar(5): remove trailing whitespace --- doc/yambar.5.scd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/yambar.5.scd b/doc/yambar.5.scd index cc03f8c..38c521d 100644 --- a/doc/yambar.5.scd +++ b/doc/yambar.5.scd @@ -24,7 +24,7 @@ types that are frequently used: - 00ff00ff: green, no transparency - ff000099: red, semi-transparent - *environment reference*: a string that contains format ${VAR}. This will be - replaced by the value of the environment variable VAR. Example: + replaced by the value of the environment variable VAR. Example: - ${HOME} - ${HOME}/.config/yambar - ENV is ${ENV}, ENV2 is ${ENV2} From 739dc30323adbb93cfad1918eca4bb0f18b41359 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 9 Jun 2024 10:12:32 +0200 Subject: [PATCH 561/611] changelog: env var substitution: fix issue reference Reference the issue, not the PR. --- CHANGELOG.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c13106..9c0a0f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,9 +15,10 @@ ## Unreleased ### Added -* environment variable substitution in config files ([#388][388]). +* environment variable substitution in config files ([#96][96]). + +[96]: https://codeberg.org/dnkl/yambar/issues/96 -[388]: https://codeberg.org/dnkl/yambar/issues/388 ### Changed From 1a323c6d21d337e710534559682e2459a5db591b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 18 Jul 2024 08:31:46 +0200 Subject: [PATCH 562/611] log: respect the NO_COLOR environment variable http://no-color.org/ --- CHANGELOG.md | 2 ++ log.c | 12 +++++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c0a0f4..c6bb179 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,8 @@ ### Added * environment variable substitution in config files ([#96][96]). +* Log output now respects the [`NO_COLOR`](http://no-color.org/) + environment variable. [96]: https://codeberg.org/dnkl/yambar/issues/96 diff --git a/log.c b/log.c index 7ba4193..ba4ebd9 100644 --- a/log.c +++ b/log.c @@ -39,9 +39,15 @@ log_init(enum log_colorize _colorize, bool _do_syslog, enum log_facility syslog_ [LOG_FACILITY_DAEMON] = LOG_DAEMON, }; - colorize = _colorize == LOG_COLORIZE_NEVER ? false - : _colorize == LOG_COLORIZE_ALWAYS ? true - : isatty(STDERR_FILENO); + /* Don't use colors if NO_COLOR is defined and not empty */ + const char *no_color_str = getenv("NO_COLOR"); + const bool no_color = no_color_str != NULL && no_color_str[0] != '\0'; + + colorize = _colorize == LOG_COLORIZE_NEVER + ? false + : _colorize == LOG_COLORIZE_ALWAYS + ? true + : !no_color && isatty(STDERR_FILENO); do_syslog = _do_syslog; log_level = _log_level; From 3e0a65f1852eb6a17ed2e8e5e381eb54b96bb8f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 7 Aug 2024 17:27:26 +0200 Subject: [PATCH 563/611] meson: fix misdetection of memfd_create() --- meson.build | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/meson.build b/meson.build index d760e94..d9b1364 100644 --- a/meson.build +++ b/meson.build @@ -12,7 +12,9 @@ plugs_as_libs = get_option('core-plugins-as-shared-libraries') cc = meson.get_compiler('c') -if cc.has_function('memfd_create') +if cc.has_function('memfd_create', + args: ['-D_GNU_SOURCE=200809L'], + prefix: '#include ') add_project_arguments('-DMEMFD_CREATE', language: 'c') endif From 568eb1140fe2296bbb45368902acf98868a15fe0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 20 Aug 2024 07:32:51 +0200 Subject: [PATCH 564/611] modules/mpd: fix reconnect when we're not using inotify When we're not able to use inotify, we rely on polling. However, we never detected poll() timeouts, which meant we never re-attempted to reconnect to MPD. Maybe #394 --- CHANGELOG.md | 2 ++ modules/mpd.c | 10 ++++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c6bb179..f7c2ec8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,8 @@ * network: fix missing break in switch statement ([#377][377]). * i3/sway: crash when output is turned off an on ([#300][300]). +* mpd: yambar never attempting to reconnect after MPD closed the + connection (for example, when MPD is restarted). [377]: https://codeberg.org/dnkl/yambar/issues/377 [300]: https://codeberg.org/dnkl/yambar/issues/300 diff --git a/modules/mpd.c b/modules/mpd.c index 22c0e6b..63da818 100644 --- a/modules/mpd.c +++ b/modules/mpd.c @@ -437,7 +437,7 @@ run(struct module *mod) */ while (!aborted) { struct pollfd fds[] = {{.fd = mod->abort_fd, .events = POLLIN}}; - int res = poll(fds, sizeof(fds) / sizeof(fds[0]), 10 * 1000); + int res = poll(fds, sizeof(fds) / sizeof(fds[0]), 2 * 1000); if (res < 0) { if (errno == EINTR) @@ -448,10 +448,16 @@ run(struct module *mod) break; } - if (res == 1) { + if (res == 0) { + ret = 0; + break; + } + + else if (res == 1) { assert(fds[0].revents & POLLIN); aborted = true; } + } } From a5ae61b5df3b3e04b79741927f3e70a809257a49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 30 Apr 2024 10:00:46 +0200 Subject: [PATCH 565/611] module: network: add 'type` tag This tag maps to the ifinfomsg->ifi_type member, which is set to one of the ARPHRD_xyz values, defined in linux/if_arp.h. There's a *ton* of them, and we can't possibly add a string mapping for _all_ of them, so for now, set to one of: * loopback * ether * wlan * ARPHRD_NNN, where N is a number --- CHANGELOG.md | 2 ++ doc/yambar-modules-network.5.scd | 4 ++++ modules/network.c | 34 +++++++++++++++++++++++++++++--- 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f7c2ec8..5dff40c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,8 +18,10 @@ * environment variable substitution in config files ([#96][96]). * Log output now respects the [`NO_COLOR`](http://no-color.org/) environment variable. +* network: `type` tag ([#380][380]). [96]: https://codeberg.org/dnkl/yambar/issues/96 +[380]: https://codeberg.org/dnkl/yambar/issues/380 ### Changed diff --git a/doc/yambar-modules-network.5.scd b/doc/yambar-modules-network.5.scd index cdc5530..eda6fd5 100644 --- a/doc/yambar-modules-network.5.scd +++ b/doc/yambar-modules-network.5.scd @@ -21,6 +21,10 @@ address per network interface. | name : string : Network interface name +| type +: string +: Interface type (*ether*, *wlan*, *loopback*, or *ARPHRD_NNN*, where + *N* is a number). | index : int : Network interface index diff --git a/modules/network.c b/modules/network.c index c2f6ec3..8a05dd7 100644 --- a/modules/network.c +++ b/modules/network.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -24,7 +25,7 @@ #include #define LOG_MODULE "network" -#define LOG_ENABLE_DBG 0 +#define LOG_ENABLE_DBG 1 #include "../bar/bar.h" #include "../config-verify.h" #include "../config.h" @@ -52,6 +53,7 @@ struct af_addr { struct iface { char *name; + char *type; uint32_t get_stats_seq_nr; @@ -104,6 +106,7 @@ free_iface(struct iface iface) { tll_free(iface.addrs); free(iface.ssid); + free(iface.type); free(iface.name); } @@ -207,6 +210,7 @@ content(struct module *mod) struct tag_set tags = { .tags = (struct tag *[]){ tag_new_string(mod, "name", iface->name), + tag_new_string(mod, "type", iface->type), tag_new_int(mod, "index", iface->index), tag_new_bool(mod, "carrier", iface->carrier), tag_new_string(mod, "state", state), @@ -221,7 +225,7 @@ content(struct module *mod) tag_new_float(mod, "dl-speed", iface->dl_speed), tag_new_float(mod, "ul-speed", iface->ul_speed), }, - .count = 14, + .count = 15, }; exposables[idx++] = m->label->instantiate(m->label, &tags); tag_set_destroy(&tags); @@ -549,6 +553,7 @@ send_nl80211_get_scan(struct private *m) return true; } + static void handle_link(struct module *mod, uint16_t type, const struct ifinfomsg *msg, size_t len) { @@ -581,9 +586,31 @@ handle_link(struct module *mod, uint16_t type, const struct ifinfomsg *msg, size } if (iface == NULL) { + char *type = NULL; + + switch (msg->ifi_type) { + case ARPHRD_ETHER: + type = strdup("ether"); + break; + + case ARPHRD_LOOPBACK: + type = strdup("loopback"); + break; + + case ARPHRD_IEEE80211: + type = strdup("wlan"); + break; + + default: + if (asprintf(&type, "ARPHRD_%hu", msg->ifi_type) < 0) + type = strdup("unknown"); + break; + } + mtx_lock(&mod->lock); tll_push_back(m->ifaces, ((struct iface){ .index = msg->ifi_index, + .type = type, .state = IF_OPER_DOWN, .addrs = tll_init(), })); @@ -596,9 +623,10 @@ handle_link(struct module *mod, uint16_t type, const struct ifinfomsg *msg, size case IFLA_IFNAME: mtx_lock(&mod->lock); iface->name = strdup((const char *)RTA_DATA(attr)); - LOG_DBG("%s: index=%d", iface->name, iface->index); + LOG_DBG("%s: index=%d, type=%s", iface->name, iface->index, iface->type); mtx_unlock(&mod->lock); break; + case IFLA_OPERSTATE: { uint8_t operstate = *(const uint8_t *)RTA_DATA(attr); if (iface->state == operstate) From 699c5630511641c931e1d287c606da14d09ea2dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 30 Apr 2024 10:22:04 +0200 Subject: [PATCH 566/611] module: network: add 'kind' tag The tag maps to the IFLA_INFO_KIND (part of the IFLA_LINKINFO) netlink attribute. This attribute is only available on virtual interfaces. Examples of valid values are: * bond * bridge * gre * tun * veth --- CHANGELOG.md | 1 + doc/yambar-modules-network.5.scd | 6 ++ modules/network.c | 134 ++++++++++++++++++++----------- 3 files changed, 93 insertions(+), 48 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5dff40c..461e6db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ * Log output now respects the [`NO_COLOR`](http://no-color.org/) environment variable. * network: `type` tag ([#380][380]). +* network: `type` and `kind` tags ([#380][380]). [96]: https://codeberg.org/dnkl/yambar/issues/96 [380]: https://codeberg.org/dnkl/yambar/issues/380 diff --git a/doc/yambar-modules-network.5.scd b/doc/yambar-modules-network.5.scd index eda6fd5..7e500f0 100644 --- a/doc/yambar-modules-network.5.scd +++ b/doc/yambar-modules-network.5.scd @@ -25,6 +25,12 @@ address per network interface. : string : Interface type (*ether*, *wlan*, *loopback*, or *ARPHRD_NNN*, where *N* is a number). +| kind +: string +: Interface kind. Empty for non-virtual interfaces. For virtual + interfaces, this value is taken from the _IFLA\_INFO\_KIND_ netlink + attribute. Examples of valid values are *bond*, *bridge*, *gre*, *tun* + and *veth*. | index : int : Network interface index diff --git a/modules/network.c b/modules/network.c index 8a05dd7..dfbd5da 100644 --- a/modules/network.c +++ b/modules/network.c @@ -53,7 +53,8 @@ struct af_addr { struct iface { char *name; - char *type; + char *type; /* ARPHRD_NNN */ + char *kind; /* IFLA_LINKINFO::IFLA_INFO_KIND */ uint32_t get_stats_seq_nr; @@ -106,6 +107,7 @@ free_iface(struct iface iface) { tll_free(iface.addrs); free(iface.ssid); + free(iface.kind); free(iface.type); free(iface.name); } @@ -211,6 +213,7 @@ content(struct module *mod) .tags = (struct tag *[]){ tag_new_string(mod, "name", iface->name), tag_new_string(mod, "type", iface->type), + tag_new_string(mod, "kind", iface->kind), tag_new_int(mod, "index", iface->index), tag_new_bool(mod, "carrier", iface->carrier), tag_new_string(mod, "state", state), @@ -225,7 +228,7 @@ content(struct module *mod) tag_new_float(mod, "dl-speed", iface->dl_speed), tag_new_float(mod, "ul-speed", iface->ul_speed), }, - .count = 15, + .count = 16, }; exposables[idx++] = m->label->instantiate(m->label, &tags); tag_set_destroy(&tags); @@ -553,6 +556,78 @@ send_nl80211_get_scan(struct private *m) return true; } +static bool +foreach_nlattr(struct module *mod, struct iface *iface, const struct genlmsghdr *genl, size_t len, + bool (*cb)(struct module *mod, struct iface *iface, uint16_t type, bool nested, const void *payload, + size_t len, void *ctx), + void *ctx) +{ + const uint8_t *raw = (const uint8_t *)genl + GENL_HDRLEN; + const uint8_t *end = (const uint8_t *)genl + len; + + for (const struct nlattr *attr = (const struct nlattr *)raw; raw < end; + raw += NLA_ALIGN(attr->nla_len), attr = (const struct nlattr *)raw) { + uint16_t type = attr->nla_type & NLA_TYPE_MASK; + bool nested = (attr->nla_type & NLA_F_NESTED) != 0; + ; + const void *payload = raw + NLA_HDRLEN; + + if (!cb(mod, iface, type, nested, payload, attr->nla_len - NLA_HDRLEN, ctx)) + return false; + } + + return true; +} + +static bool +foreach_nlattr_nested(struct module *mod, struct iface *iface, const void *parent_payload, size_t len, + bool (*cb)(struct module *mod, struct iface *iface, uint16_t type, bool nested, + const void *payload, size_t len, void *ctx), + void *ctx) +{ + const uint8_t *raw = parent_payload; + const uint8_t *end = parent_payload + len; + + for (const struct nlattr *attr = (const struct nlattr *)raw; raw < end; + raw += NLA_ALIGN(attr->nla_len), attr = (const struct nlattr *)raw) { + uint16_t type = attr->nla_type & NLA_TYPE_MASK; + bool nested = (attr->nla_type & NLA_F_NESTED) != 0; + const void *payload = raw + NLA_HDRLEN; + + if (!cb(mod, iface, type, nested, payload, attr->nla_len - NLA_HDRLEN, ctx)) + return false; + } + + return true; +} + +static bool +parse_linkinfo(struct module *mod, struct iface *iface, uint16_t type, + bool nested, const void *payload, size_t len, void *_void) +{ + switch (type) { + case IFLA_INFO_KIND: { + const char *kind = payload; + free(iface->kind); + iface->kind = strndup(kind, len); + + LOG_DBG("%s: IFLA_INFO_KIND: %s", iface->name, iface->kind); + break; + } + + case IFLA_INFO_DATA: + //LOG_DBG("%s: IFLA_INFO_DATA", iface->name); + break; + + default: + LOG_WARN("unrecognized IFLA_LINKINFO attribute: " + "type=%hu, nested=%d, len=%zu", + type, nested, len); + break; + } + + return true; +} static void handle_link(struct module *mod, uint16_t type, const struct ifinfomsg *msg, size_t len) @@ -661,7 +736,8 @@ handle_link(struct module *mod, uint16_t type, const struct ifinfomsg *msg, size if (memcmp(iface->mac, mac, sizeof(iface->mac)) == 0) break; - LOG_DBG("%s: IFLA_ADDRESS: %02x:%02x:%02x:%02x:%02x:%02x", iface->name, mac[0], mac[1], mac[2], mac[3], + LOG_DBG("%s: IFLA_ADDRESS: %02x:%02x:%02x:%02x:%02x:%02x", + iface->name, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); mtx_lock(&mod->lock); @@ -669,6 +745,13 @@ handle_link(struct module *mod, uint16_t type, const struct ifinfomsg *msg, size mtx_unlock(&mod->lock); break; } + + case IFLA_LINKINFO: { + foreach_nlattr_nested( + mod, iface, RTA_DATA(attr), RTA_PAYLOAD(attr), + &parse_linkinfo, NULL); + break; + } } } @@ -751,51 +834,6 @@ handle_address(struct module *mod, uint16_t type, const struct ifaddrmsg *msg, s mod->bar->refresh(mod->bar); } -static bool -foreach_nlattr(struct module *mod, struct iface *iface, const struct genlmsghdr *genl, size_t len, - bool (*cb)(struct module *mod, struct iface *iface, uint16_t type, bool nested, const void *payload, - size_t len, void *ctx), - void *ctx) -{ - const uint8_t *raw = (const uint8_t *)genl + GENL_HDRLEN; - const uint8_t *end = (const uint8_t *)genl + len; - - for (const struct nlattr *attr = (const struct nlattr *)raw; raw < end; - raw += NLA_ALIGN(attr->nla_len), attr = (const struct nlattr *)raw) { - uint16_t type = attr->nla_type & NLA_TYPE_MASK; - bool nested = (attr->nla_type & NLA_F_NESTED) != 0; - ; - const void *payload = raw + NLA_HDRLEN; - - if (!cb(mod, iface, type, nested, payload, attr->nla_len - NLA_HDRLEN, ctx)) - return false; - } - - return true; -} - -static bool -foreach_nlattr_nested(struct module *mod, struct iface *iface, const void *parent_payload, size_t len, - bool (*cb)(struct module *mod, struct iface *iface, uint16_t type, bool nested, - const void *payload, size_t len, void *ctx), - void *ctx) -{ - const uint8_t *raw = parent_payload; - const uint8_t *end = parent_payload + len; - - for (const struct nlattr *attr = (const struct nlattr *)raw; raw < end; - raw += NLA_ALIGN(attr->nla_len), attr = (const struct nlattr *)raw) { - uint16_t type = attr->nla_type & NLA_TYPE_MASK; - bool nested = (attr->nla_type & NLA_F_NESTED) != 0; - const void *payload = raw + NLA_HDRLEN; - - if (!cb(mod, iface, type, nested, payload, attr->nla_len - NLA_HDRLEN, ctx)) - return false; - } - - return true; -} - struct mcast_group { uint32_t id; const char *name; From 54902f46ab3cfa5c72dbf3bd8e08886fd8fa0021 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 30 Apr 2024 10:40:14 +0200 Subject: [PATCH 567/611] module: network: hardcode type to "wlan" when we see NL80211_CMD_NEW_INTERFACE Wlan interfaces apparently report themselves as ARPHRD_ETHER in their ifinfomsg struct (despite there being a ARPHRD_IEEE80211 type...). "Fix" by hardcoding the type to "wlan" when we receive a NL80211_CMD_NEW_INTERFACE message. --- modules/network.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/network.c b/modules/network.c index dfbd5da..1b2ceba 100644 --- a/modules/network.c +++ b/modules/network.c @@ -1370,6 +1370,8 @@ parse_genl_reply(struct module *mod, const struct nlmsghdr *hdr, size_t len) continue; LOG_DBG("%s: got interface information", iface->name); + free(iface->type); + iface->type = strdup("wlan"); foreach_nlattr(mod, iface, genl, msg_size, &handle_nl80211_new_interface, NULL); break; From 887e770202e2839027bfa138bfc5d5b4bbec688a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 30 Apr 2024 12:03:33 +0200 Subject: [PATCH 568/611] doc: network: update example Only display Ethernet and WLAN devices (not loopback, bridges etc). --- doc/yambar-modules-network.5.scd | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/doc/yambar-modules-network.5.scd b/doc/yambar-modules-network.5.scd index 7e500f0..afbbae3 100644 --- a/doc/yambar-modules-network.5.scd +++ b/doc/yambar-modules-network.5.scd @@ -101,17 +101,23 @@ address per network interface. # EXAMPLES +Display all Ethernet (including WLAN) devices. This excludes loopback, +bridges etc. + ``` bar: left: - network: content: map: - default: - string: {text: "{name}: {state} ({ipv4})"} conditions: - ipv4 == "": - string: {text: "{name}: {state}"} + type == ether || type == wlan: + map: + default: + string: {text: "{name}: {state} ({ipv4})"} + conditions: + ipv4 == "": + string: {text: "{name}: {state}"} ``` # SEE ALSO From f8ba887dcd5d063c9a069e6bd61b00d7c9f55439 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 20 Aug 2024 09:11:17 +0200 Subject: [PATCH 569/611] readme: repology: use four columns --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2887f53..48566dc 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ # Yambar -[![Packaging status](https://repology.org/badge/vertical-allrepos/yambar.svg)](https://repology.org/project/yambar/versions) +[![Packaging status](https://repology.org/badge/vertical-allrepos/yambar.svg?columns=4)](https://repology.org/project/yambar/versions) ## Index From 700bf5b28c3f060ed629ac9bf782e0ff2ec76636 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 20 Aug 2024 14:34:45 +0200 Subject: [PATCH 570/611] tag: add 'b' formatter Divides the tag's decimal value by 8. Closes #392 --- CHANGELOG.md | 3 +++ doc/yambar-tags.5.scd | 7 +++++- tag.c | 51 +++++++++++++++++++++++++++++++------------ 3 files changed, 46 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 461e6db..33e12b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,9 +20,12 @@ environment variable. * network: `type` tag ([#380][380]). * network: `type` and `kind` tags ([#380][380]). +* tags: `b` tag formatter; divides the tag's decimal value with `8` + ([#392][392]). [96]: https://codeberg.org/dnkl/yambar/issues/96 [380]: https://codeberg.org/dnkl/yambar/issues/380 +[392]: https://codeberg.org/dnkl/yambar/issues/392 ### Changed diff --git a/doc/yambar-tags.5.scd b/doc/yambar-tags.5.scd index b778154..adda208 100644 --- a/doc/yambar-tags.5.scd +++ b/doc/yambar-tags.5.scd @@ -86,11 +86,16 @@ be used. : format : Range tags : Renders a range tag's value as a percentage value +| b +: format +: All tag types +: Renders a tag's value (in decimal) divided by 8. Note: no unit + suffix is appended | kb, mb, gb : format : All tag types : Renders a tag's value (in decimal) divided by 1000, 1000^2 or - 1000^3. Note: no unit suffix is appended) + 1000^3. Note: no unit suffix is appended | kib, mib, gib : format : All tag types diff --git a/tag.c b/tag.c index e95b1c7..438af64 100644 --- a/tag.c +++ b/tag.c @@ -510,6 +510,7 @@ tags_expand_template(const char *template, const struct tag_set *tags) FMT_HEX, FMT_OCT, FMT_PERCENT, + FMT_BYTE, FMT_KBYTE, FMT_MBYTE, FMT_GBYTE, @@ -541,6 +542,8 @@ tags_expand_template(const char *template, const struct tag_set *tags) format = FMT_OCT; else if (strcmp(tag_args[i], "%") == 0) format = FMT_PERCENT; + else if (strcmp(tag_args[i], "b") == 0) + format = FMT_BYTE; else if (strcmp(tag_args[i], "kb") == 0) format = FMT_KBYTE; else if (strcmp(tag_args[i], "mb") == 0) @@ -634,19 +637,29 @@ tags_expand_template(const char *template, const struct tag_set *tags) break; } + case FMT_BYTE: case FMT_KBYTE: case FMT_MBYTE: case FMT_GBYTE: case FMT_KIBYTE: case FMT_MIBYTE: case FMT_GIBYTE: { - const long divider = format == FMT_KBYTE ? 1000 - : format == FMT_MBYTE ? 1000 * 1000 - : format == FMT_GBYTE ? 1000 * 1000 * 1000 - : format == FMT_KIBYTE ? 1024 - : format == FMT_MIBYTE ? 1024 * 1024 - : format == FMT_GIBYTE ? 1024 * 1024 * 1024 - : 1; + const long divider = + format == FMT_BYTE + ? 8 + : format == FMT_KBYTE + ? 1000 + : format == FMT_MBYTE + ? 1000 * 1000 + : format == FMT_GBYTE + ? 1000 * 1000 * 1000 + : format == FMT_KIBYTE + ? 1024 + : format == FMT_MIBYTE + ? 1024 * 1024 + : format == FMT_GIBYTE + ? 1024 * 1024 * 1024 + : 1; char str[24]; if (tag->type(tag) == TAG_TYPE_FLOAT) { @@ -684,19 +697,29 @@ tags_expand_template(const char *template, const struct tag_set *tags) fmt = zero_pad ? "%0*lu" : "%*lu"; break; + case FMT_BYTE: case FMT_KBYTE: case FMT_MBYTE: case FMT_GBYTE: case FMT_KIBYTE: case FMT_MIBYTE: case FMT_GIBYTE: { - const long divider = format == FMT_KBYTE ? 1024 - : format == FMT_MBYTE ? 1024 * 1024 - : format == FMT_GBYTE ? 1024 * 1024 * 1024 - : format == FMT_KIBYTE ? 1000 - : format == FMT_MIBYTE ? 1000 * 1000 - : format == FMT_GIBYTE ? 1000 * 1000 * 1000 - : 1; + const long divider = + format == FMT_BYTE + ? 8 + : format == FMT_KBYTE + ? 1024 + : format == FMT_MBYTE + ? 1024 * 1024 + : format == FMT_GBYTE + ? 1024 * 1024 * 1024 + : format == FMT_KIBYTE + ? 1000 + : format == FMT_MIBYTE + ? 1000 * 1000 + : format == FMT_GIBYTE + ? 1000 * 1000 * 1000 + : 1; value /= divider; fmt = zero_pad ? "%0*lu" : "%*lu"; break; From 1b2dee55efa246d1a020fff4345988ded02123f0 Mon Sep 17 00:00:00 2001 From: fraktal Date: Wed, 4 Sep 2024 15:33:25 +0200 Subject: [PATCH 571/611] fix bar Y position in case of multi-monitor setups with mixed resolutions --- bar/xcb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bar/xcb.c b/bar/xcb.c index 2552fe6..f3167a5 100644 --- a/bar/xcb.c +++ b/bar/xcb.c @@ -101,7 +101,7 @@ setup(struct bar *_bar) backend->x = mon->x; backend->y = mon->y; bar->width = mon->width; - backend->y += bar->location == BAR_TOP ? 0 : screen->height_in_pixels - bar->height_with_border; + backend->y += bar->location == BAR_TOP ? 0 : mon->height - bar->height_with_border; found_monitor = true; From 2d651d1c0e5b74ac3db643fc0aabffe561387a1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 5 Sep 2024 08:16:25 +0200 Subject: [PATCH 572/611] changelog: bar position in multi-monitor setups, with location=bottom --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 33e12b1..962c268 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,6 +44,7 @@ * i3/sway: crash when output is turned off an on ([#300][300]). * mpd: yambar never attempting to reconnect after MPD closed the connection (for example, when MPD is restarted). +* Bar positioning on multi-monitor setups, when `location=bottom`. [377]: https://codeberg.org/dnkl/yambar/issues/377 [300]: https://codeberg.org/dnkl/yambar/issues/300 From 9498d7e445cd444d2840bf89a3e20dd71fc45045 Mon Sep 17 00:00:00 2001 From: Zhong Jianxin Date: Sun, 1 Sep 2024 21:16:49 +0800 Subject: [PATCH 573/611] tag: combine FMT_*BYTE into one FMT_DIVIDE --- tag.c | 100 +++++++++++++++++++--------------------------------------- 1 file changed, 32 insertions(+), 68 deletions(-) diff --git a/tag.c b/tag.c index 438af64..ce4e0e8 100644 --- a/tag.c +++ b/tag.c @@ -510,13 +510,7 @@ tags_expand_template(const char *template, const struct tag_set *tags) FMT_HEX, FMT_OCT, FMT_PERCENT, - FMT_BYTE, - FMT_KBYTE, - FMT_MBYTE, - FMT_GBYTE, - FMT_KIBYTE, - FMT_MIBYTE, - FMT_GIBYTE, + FMT_DIVIDE, } format = FMT_DEFAULT; @@ -530,6 +524,7 @@ tags_expand_template(const char *template, const struct tag_set *tags) int digits = 0; int decimals = 2; + long divider = 1; bool zero_pad = false; char *point = NULL; @@ -542,20 +537,34 @@ tags_expand_template(const char *template, const struct tag_set *tags) format = FMT_OCT; else if (strcmp(tag_args[i], "%") == 0) format = FMT_PERCENT; - else if (strcmp(tag_args[i], "b") == 0) - format = FMT_BYTE; - else if (strcmp(tag_args[i], "kb") == 0) - format = FMT_KBYTE; - else if (strcmp(tag_args[i], "mb") == 0) - format = FMT_MBYTE; - else if (strcmp(tag_args[i], "gb") == 0) - format = FMT_GBYTE; - else if (strcmp(tag_args[i], "kib") == 0) - format = FMT_KIBYTE; - else if (strcmp(tag_args[i], "mib") == 0) - format = FMT_MIBYTE; - else if (strcmp(tag_args[i], "gib") == 0) - format = FMT_GIBYTE; + else if (strcmp(tag_args[i], "b") == 0) { + format = FMT_DIVIDE; + divider = 8; + } + else if (strcmp(tag_args[i], "kb") == 0) { + format = FMT_DIVIDE; + divider = 1000; + } + else if (strcmp(tag_args[i], "mb") == 0) { + format = FMT_DIVIDE; + divider = 1000 * 1000; + } + else if (strcmp(tag_args[i], "gb") == 0) { + format = FMT_DIVIDE; + divider = 1000 * 1000 * 1000; + } + else if (strcmp(tag_args[i], "kib") == 0) { + format = FMT_DIVIDE; + divider = 1024; + } + else if (strcmp(tag_args[i], "mib") == 0) { + format = FMT_DIVIDE; + divider = 1024 * 1024; + } + else if (strcmp(tag_args[i], "gib") == 0) { + format = FMT_DIVIDE; + divider = 1024 * 1024 * 1024; + } else if (strcmp(tag_args[i], "min") == 0) kind = VALUE_MIN; else if (strcmp(tag_args[i], "max") == 0) @@ -637,30 +646,7 @@ tags_expand_template(const char *template, const struct tag_set *tags) break; } - case FMT_BYTE: - case FMT_KBYTE: - case FMT_MBYTE: - case FMT_GBYTE: - case FMT_KIBYTE: - case FMT_MIBYTE: - case FMT_GIBYTE: { - const long divider = - format == FMT_BYTE - ? 8 - : format == FMT_KBYTE - ? 1000 - : format == FMT_MBYTE - ? 1000 * 1000 - : format == FMT_GBYTE - ? 1000 * 1000 * 1000 - : format == FMT_KIBYTE - ? 1024 - : format == FMT_MIBYTE - ? 1024 * 1024 - : format == FMT_GIBYTE - ? 1024 * 1024 * 1024 - : 1; - + case FMT_DIVIDE: { char str[24]; if (tag->type(tag) == TAG_TYPE_FLOAT) { const char *fmt = zero_pad ? "%0*.*f" : "%*.*f"; @@ -697,29 +683,7 @@ tags_expand_template(const char *template, const struct tag_set *tags) fmt = zero_pad ? "%0*lu" : "%*lu"; break; - case FMT_BYTE: - case FMT_KBYTE: - case FMT_MBYTE: - case FMT_GBYTE: - case FMT_KIBYTE: - case FMT_MIBYTE: - case FMT_GIBYTE: { - const long divider = - format == FMT_BYTE - ? 8 - : format == FMT_KBYTE - ? 1024 - : format == FMT_MBYTE - ? 1024 * 1024 - : format == FMT_GBYTE - ? 1024 * 1024 * 1024 - : format == FMT_KIBYTE - ? 1000 - : format == FMT_MIBYTE - ? 1000 * 1000 - : format == FMT_GIBYTE - ? 1000 * 1000 * 1000 - : 1; + case FMT_DIVIDE: { value /= divider; fmt = zero_pad ? "%0*lu" : "%*lu"; break; From 311c481bfe0b22516859584bf79fb95716927e99 Mon Sep 17 00:00:00 2001 From: Zhong Jianxin Date: Sun, 1 Sep 2024 21:21:26 +0800 Subject: [PATCH 574/611] tag: add '/N' formatter --- doc/yambar-tags.5.scd | 4 ++++ tag.c | 16 ++++++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/doc/yambar-tags.5.scd b/doc/yambar-tags.5.scd index adda208..f58678c 100644 --- a/doc/yambar-tags.5.scd +++ b/doc/yambar-tags.5.scd @@ -86,6 +86,10 @@ be used. : format : Range tags : Renders a range tag's value as a percentage value +| /N +: format +: All tag types +: Renders a tag's value (in decimal) divided by N | b : format : All tag types diff --git a/tag.c b/tag.c index ce4e0e8..48155b5 100644 --- a/tag.c +++ b/tag.c @@ -430,12 +430,12 @@ sbuf_append(struct sbuf *s1, const char *s2) // stores the number in "*value" on success static bool -is_number(const char *str, int *value) +is_number(const char *str, long *value) { errno = 0; char *end; - int v = strtol(str, &end, 10); + long v = strtol(str, &end, 10); if (errno != 0 || *end != '\0') return false; @@ -522,8 +522,8 @@ tags_expand_template(const char *template, const struct tag_set *tags) } kind = VALUE_VALUE; - int digits = 0; - int decimals = 2; + long digits = 0; + long decimals = 2; long divider = 1; bool zero_pad = false; char *point = NULL; @@ -537,6 +537,14 @@ tags_expand_template(const char *template, const struct tag_set *tags) format = FMT_OCT; else if (strcmp(tag_args[i], "%") == 0) format = FMT_PERCENT; + else if (*tag_args[i] == '/') { + format = FMT_DIVIDE; + const char *divider_str = tag_args[i] + 1; + if (!is_number(divider_str, ÷r) || divider == 0) { + divider = 1; + LOG_WARN("tag `%s`: invalid divider %s, reset to 1", tag_name, divider_str); + } + } else if (strcmp(tag_args[i], "b") == 0) { format = FMT_DIVIDE; divider = 8; From c80bae7604b18f54ee25e4073e9a95b081da1541 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 5 Sep 2024 08:20:59 +0200 Subject: [PATCH 575/611] changelog: /N tag formatter --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 962c268..1bbcae1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,8 @@ * network: `type` and `kind` tags ([#380][380]). * tags: `b` tag formatter; divides the tag's decimal value with `8` ([#392][392]). +* tags: `/` tag formatter: divides the tag's decimal value with `N` + ([#392][392]). [96]: https://codeberg.org/dnkl/yambar/issues/96 [380]: https://codeberg.org/dnkl/yambar/issues/380 From 060586dbbeb9134ee0a8810d611fc08310da490a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 5 Sep 2024 08:23:47 +0200 Subject: [PATCH 576/611] tag: remove the :b formatter Superseded by /N. Removing since a) it's no longer needed, and b) its name is not consistent with the other kb/mb/gb formatters. --- CHANGELOG.md | 2 -- doc/yambar-tags.5.scd | 5 ----- tag.c | 4 ---- 3 files changed, 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1bbcae1..0e2e456 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,8 +20,6 @@ environment variable. * network: `type` tag ([#380][380]). * network: `type` and `kind` tags ([#380][380]). -* tags: `b` tag formatter; divides the tag's decimal value with `8` - ([#392][392]). * tags: `/` tag formatter: divides the tag's decimal value with `N` ([#392][392]). diff --git a/doc/yambar-tags.5.scd b/doc/yambar-tags.5.scd index f58678c..b6b8b56 100644 --- a/doc/yambar-tags.5.scd +++ b/doc/yambar-tags.5.scd @@ -90,11 +90,6 @@ be used. : format : All tag types : Renders a tag's value (in decimal) divided by N -| b -: format -: All tag types -: Renders a tag's value (in decimal) divided by 8. Note: no unit - suffix is appended | kb, mb, gb : format : All tag types diff --git a/tag.c b/tag.c index 48155b5..d6609af 100644 --- a/tag.c +++ b/tag.c @@ -545,10 +545,6 @@ tags_expand_template(const char *template, const struct tag_set *tags) LOG_WARN("tag `%s`: invalid divider %s, reset to 1", tag_name, divider_str); } } - else if (strcmp(tag_args[i], "b") == 0) { - format = FMT_DIVIDE; - divider = 8; - } else if (strcmp(tag_args[i], "kb") == 0) { format = FMT_DIVIDE; divider = 1000; From b81e41c3c4567c78fe45c8b380c271ab111fcd89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 5 Sep 2024 11:56:10 +0200 Subject: [PATCH 577/611] module/i3: add 'output' tag This allows bars to render workspaces differently, depending on which output the workspace is on: - map: default: ... conditions: output == DP-1: ... --- modules/i3.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/i3.c b/modules/i3.c index 5cb6e01..b1d1ca8 100644 --- a/modules/i3.c +++ b/modules/i3.c @@ -876,6 +876,7 @@ content(struct module *mod) struct tag_set tags = { .tags = (struct tag *[]){ tag_new_string(mod, "name", name), + tag_new_string(mod, "output", ws->output), tag_new_bool(mod, "visible", ws->visible), tag_new_bool(mod, "focused", ws->focused), tag_new_bool(mod, "urgent", ws->urgent), @@ -887,7 +888,7 @@ content(struct module *mod) tag_new_string(mod, "mode", m->mode), }, - .count = 9, + .count = 10, }; if (ws->focused) { From c3f7fe013daba11ba32880687e4a7102e402b096 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 7 Sep 2024 08:35:39 +0200 Subject: [PATCH 578/611] doc: i3/sway: add 'output' to tag list --- doc/yambar-modules-i3.5.scd | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/yambar-modules-i3.5.scd b/doc/yambar-modules-i3.5.scd index 296a2da..2014a3c 100644 --- a/doc/yambar-modules-i3.5.scd +++ b/doc/yambar-modules-i3.5.scd @@ -26,6 +26,9 @@ with the _application_ and _title_ tags to replace the X11-only | name : string : The workspace name +| output +: string +: The output (monitor) the workspace is on | visible : bool : True if the workspace is currently visible (on any output) From 0f47cbb889716dcd0824b5501cab6f5f1cc85eac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 7 Sep 2024 08:35:58 +0200 Subject: [PATCH 579/611] changelog: i3/sway: output tag --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e2e456..763873f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,8 @@ * network: `type` and `kind` tags ([#380][380]). * tags: `/` tag formatter: divides the tag's decimal value with `N` ([#392][392]). +* i3/sway: `output` tag, reflecting the output (monitor) a workspace + is on. [96]: https://codeberg.org/dnkl/yambar/issues/96 [380]: https://codeberg.org/dnkl/yambar/issues/380 From 4826a52306ffc9881269d6ecf12c0f5ede123a11 Mon Sep 17 00:00:00 2001 From: bagnaram Date: Fri, 26 Jul 2024 14:40:10 -0600 Subject: [PATCH 580/611] string like operation --- CHANGELOG.md | 3 ++ doc/yambar-particles.5.scd | 20 ++++++++++++++ particles/map.c | 56 ++++++++++++++++++++++++++++++++++++++ particles/map.h | 1 + particles/map.l | 1 + particles/map.y | 14 +++++----- 6 files changed, 88 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 763873f..7b65b3b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,10 +24,13 @@ ([#392][392]). * i3/sway: `output` tag, reflecting the output (monitor) a workspace is on. +* Added "string like" `~~` operator to Map particle. Allows + glob-style matching on strings using `*` and `?` characters. ([#400][400]) [96]: https://codeberg.org/dnkl/yambar/issues/96 [380]: https://codeberg.org/dnkl/yambar/issues/380 [392]: https://codeberg.org/dnkl/yambar/issues/392 +[400]: https://codeberg.org/dnkl/yambar/pulls/400 ### Changed diff --git a/doc/yambar-particles.5.scd b/doc/yambar-particles.5.scd index d9b0e56..c86a93b 100644 --- a/doc/yambar-particles.5.scd +++ b/doc/yambar-particles.5.scd @@ -265,6 +265,26 @@ To match for empty strings, use ' "" ': == "" ``` +String glob matching + +To perform string matching using globbing with "\*" & "?" characters: +\* Match any zero or more characters. +? Match exactly any one character. + +``` + ~~ "hello*" +``` + +Will match any string starting with "hello", including "hello", +"hello1", "hello123", etc. + +``` + ~~ "hello?" +``` + +Will match any string starting with "hello" followed by any single +character, including "hello1", "hello-", but not "hello". + Furthermore, you may use the boolean operators: [- && diff --git a/particles/map.c b/particles/map.c index 51fc744..348e966 100644 --- a/particles/map.c +++ b/particles/map.c @@ -13,6 +13,59 @@ #include "map.h" +// String globbing match. +// Note: Uses "non-greedy" implementation for "*" wildcard matching +static bool +string_like(const char* name, const char* pattern) +{ + LOG_DBG("pattern:%s name:%s", pattern, name); + int px = 0, nx = 0; + int nextpx = 0, nextnx = 0; + while(px < strlen(pattern) || nx < strlen(name)) + { + if(px < strlen(pattern)) + { + char c = pattern[px]; + switch (c) { + case '?': { + // single character + px++; + nx++; + continue; + } + case '*': { + // zero or more glob + nextpx=px; + nextnx=nx+1; + px++; + continue; + } + default: { + // normal character + if (nx < strlen(name) && name[nx] == c) + { + px++; + nx++; + continue; + } + } + } + + } + // mismatch + if (0 < nextnx && nextnx <= strlen(name)) { + px = nextpx; + nx = nextnx; + continue; + } + return false; + + } + LOG_DBG("map: name %s matched all the pattern %s", name, pattern); + // Matched all of pattern to all of name. Success. + return true; +} + static bool int_condition(const long tag_value, const long cond_value, enum map_op op) { @@ -75,6 +128,8 @@ str_condition(const char *tag_value, const char *cond_value, enum map_op op) return strcmp(tag_value, cond_value) >= 0; case MAP_OP_GT: return strcmp(tag_value, cond_value) > 0; + case MAP_OP_LIKE: + return string_like(tag_value, cond_value) != 0; case MAP_OP_SELF: LOG_WARN("using String tag as bool"); default: @@ -166,6 +221,7 @@ free_map_condition(struct map_condition *c) case MAP_OP_LE: case MAP_OP_LT: case MAP_OP_GE: + case MAP_OP_LIKE: case MAP_OP_GT: free(c->value); /* FALLTHROUGH */ diff --git a/particles/map.h b/particles/map.h index 23670a5..1256744 100644 --- a/particles/map.h +++ b/particles/map.h @@ -9,6 +9,7 @@ enum map_op { MAP_OP_GT, MAP_OP_SELF, MAP_OP_NOT, + MAP_OP_LIKE, MAP_OP_AND, MAP_OP_OR, diff --git a/particles/map.l b/particles/map.l index d34f086..034353c 100644 --- a/particles/map.l +++ b/particles/map.l @@ -69,6 +69,7 @@ void yyerror(const char *s); \< yylval.op = MAP_OP_LT; return CMP_OP; >= yylval.op = MAP_OP_GE; return CMP_OP; > yylval.op = MAP_OP_GT; return CMP_OP; +~~ yylval.op = MAP_OP_LIKE; return CMP_OP; && yylval.op = MAP_OP_AND; return BOOL_OP; \|\| yylval.op = MAP_OP_OR; return BOOL_OP; ~ return NOT; diff --git a/particles/map.y b/particles/map.y index ee426da..8f3f46b 100644 --- a/particles/map.y +++ b/particles/map.y @@ -35,27 +35,27 @@ result: condition { MAP_CONDITION_PARSE_RESULT = $1; }; condition: WORD { $$ = malloc(sizeof(struct map_condition)); - $$->tag = $1; + $$->tag = $1; $$->op = MAP_OP_SELF; } | WORD CMP_OP WORD { $$ = malloc(sizeof(struct map_condition)); - $$->tag = $1; + $$->tag = $1; $$->op = $2; - $$->value = $3; + $$->value = $3; } | WORD CMP_OP STRING { $$ = malloc(sizeof(struct map_condition)); - $$->tag = $1; + $$->tag = $1; $$->op = $2; - $$->value = $3; + $$->value = $3; } | L_PAR condition R_PAR { $$ = $2; } | - NOT condition { + NOT condition { $$ = malloc(sizeof(struct map_condition)); $$->cond1 = $2; $$->op = MAP_OP_NOT; @@ -79,7 +79,7 @@ static char const* token_to_str(yysymbol_kind_t tkn) { switch (tkn) { - case YYSYMBOL_CMP_OP: return "==, !=, <=, <, >=, >"; + case YYSYMBOL_CMP_OP: return "==, !=, <=, <, >=, >, ~~"; case YYSYMBOL_BOOL_OP: return "||, &&"; case YYSYMBOL_L_PAR: return "("; case YYSYMBOL_R_PAR: return ")"; From 37ecc251a43ad14b49948c5eb7d119980ad82eac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 2 Oct 2024 08:10:30 +0200 Subject: [PATCH 581/611] changelog: line-wrap --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b65b3b..09db85b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,8 +24,8 @@ ([#392][392]). * i3/sway: `output` tag, reflecting the output (monitor) a workspace is on. -* Added "string like" `~~` operator to Map particle. Allows - glob-style matching on strings using `*` and `?` characters. ([#400][400]) +* Added "string like" `~~` operator to Map particle. Allows glob-style + matching on strings using `*` and `?` characters ([#400][400]). [96]: https://codeberg.org/dnkl/yambar/issues/96 [380]: https://codeberg.org/dnkl/yambar/issues/380 From 20d48a753b4faf1c9cad92359caad234296f34a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 2 Oct 2024 08:10:53 +0200 Subject: [PATCH 582/611] particle/map: code style --- particles/map.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/particles/map.c b/particles/map.c index 348e966..c5510ff 100644 --- a/particles/map.c +++ b/particles/map.c @@ -21,10 +21,9 @@ string_like(const char* name, const char* pattern) LOG_DBG("pattern:%s name:%s", pattern, name); int px = 0, nx = 0; int nextpx = 0, nextnx = 0; - while(px < strlen(pattern) || nx < strlen(name)) - { - if(px < strlen(pattern)) - { + + while (px < strlen(pattern) || nx < strlen(name)) { + if (px < strlen(pattern)) { char c = pattern[px]; switch (c) { case '?': { @@ -52,18 +51,21 @@ string_like(const char* name, const char* pattern) } } + // mismatch - if (0 < nextnx && nextnx <= strlen(name)) { - px = nextpx; - nx = nextnx; - continue; - } + if (0 < nextnx && nextnx <= strlen(name)) { + px = nextpx; + nx = nextnx; + continue; + } + return false; } + LOG_DBG("map: name %s matched all the pattern %s", name, pattern); // Matched all of pattern to all of name. Success. - return true; + return true; } static bool From e1b6a78f227eec7abf5b6d5dc59d5d913cb8e68a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 2 Oct 2024 08:11:02 +0200 Subject: [PATCH 583/611] doc: particles: remove trailing spaces --- doc/yambar-particles.5.scd | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/yambar-particles.5.scd b/doc/yambar-particles.5.scd index c86a93b..70a5375 100644 --- a/doc/yambar-particles.5.scd +++ b/doc/yambar-particles.5.scd @@ -268,21 +268,21 @@ To match for empty strings, use ' "" ': String glob matching To perform string matching using globbing with "\*" & "?" characters: -\* Match any zero or more characters. -? Match exactly any one character. +\* Match any zero or more characters. ? Match exactly any one +character. ``` ~~ "hello*" ``` -Will match any string starting with "hello", including "hello", +Will match any string starting with "hello", including "hello", "hello1", "hello123", etc. ``` ~~ "hello?" ``` -Will match any string starting with "hello" followed by any single +Will match any string starting with "hello" followed by any single character, including "hello1", "hello-", but not "hello". Furthermore, you may use the boolean operators: From 650d1f13f9f718dbbec33f4ebe6494aefe276ed2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathias=20St=C3=A6rk?= Date: Tue, 8 Oct 2024 15:42:03 +0200 Subject: [PATCH 584/611] docs: fix typo in example --- doc/yambar-particles.5.scd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/yambar-particles.5.scd b/doc/yambar-particles.5.scd index 70a5375..231b419 100644 --- a/doc/yambar-particles.5.scd +++ b/doc/yambar-particles.5.scd @@ -476,7 +476,7 @@ itself when needed. ``` content: - progres-bar: + progress-bar: tag: tag_name length: 20 start: {string: {text: ├}} From a367895dc63f822ab4063449009bc6755adbeec3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 23 Oct 2024 09:36:59 +0200 Subject: [PATCH 585/611] Open sockets, files etc with FD_CLOEXEC --- main.c | 2 +- modules/backlight.c | 8 ++++---- modules/battery.c | 36 ++++++++++++++++++------------------ modules/cpu.c | 2 +- modules/disk-io.c | 2 +- modules/dwl.c | 2 +- modules/i3.c | 2 +- modules/mem.c | 2 +- modules/network.c | 2 +- modules/pulse.c | 2 +- modules/removables.c | 2 +- modules/xwindow.c | 2 +- 12 files changed, 32 insertions(+), 32 deletions(-) diff --git a/main.c b/main.c index a9c6932..c355843 100644 --- a/main.c +++ b/main.c @@ -87,7 +87,7 @@ get_config_path(void) static struct bar * load_bar(const char *config_path, enum bar_backend backend) { - FILE *conf_file = fopen(config_path, "r"); + FILE *conf_file = fopen(config_path, "re"); if (conf_file == NULL) { LOG_ERRNO("%s: failed to open", config_path); return NULL; diff --git a/modules/backlight.c b/modules/backlight.c index 0fa1787..1495c5c 100644 --- a/modules/backlight.c +++ b/modules/backlight.c @@ -112,13 +112,13 @@ readint_from_fd(int fd) static int initialize(struct private *m) { - int backlight_fd = open("/sys/class/backlight", O_RDONLY); + int backlight_fd = open("/sys/class/backlight", O_RDONLY | O_CLOEXEC); if (backlight_fd == -1) { LOG_ERRNO("/sys/class/backlight"); return -1; } - int base_dir_fd = openat(backlight_fd, m->device, O_RDONLY); + int base_dir_fd = openat(backlight_fd, m->device, O_RDONLY | O_CLOEXEC); close(backlight_fd); if (base_dir_fd == -1) { @@ -126,7 +126,7 @@ initialize(struct private *m) return -1; } - int max_fd = openat(base_dir_fd, "max_brightness", O_RDONLY); + int max_fd = openat(base_dir_fd, "max_brightness", O_RDONLY | O_CLOEXEC); if (max_fd == -1) { LOG_ERRNO("/sys/class/backlight/%s/max_brightness", m->device); close(base_dir_fd); @@ -136,7 +136,7 @@ initialize(struct private *m) m->max_brightness = readint_from_fd(max_fd); close(max_fd); - int current_fd = openat(base_dir_fd, "brightness", O_RDONLY); + int current_fd = openat(base_dir_fd, "brightness", O_RDONLY | O_CLOEXEC); close(base_dir_fd); if (current_fd == -1) { diff --git a/modules/battery.c b/modules/battery.c index c3507d7..34b98c8 100644 --- a/modules/battery.c +++ b/modules/battery.c @@ -259,13 +259,13 @@ initialize(struct private *m) { char line_buf[512]; - int pw_fd = open("/sys/class/power_supply", O_RDONLY); + int pw_fd = open("/sys/class/power_supply", O_RDONLY | O_CLOEXEC); if (pw_fd < 0) { LOG_ERRNO("/sys/class/power_supply"); return false; } - int base_dir_fd = openat(pw_fd, m->battery, O_RDONLY); + int base_dir_fd = openat(pw_fd, m->battery, O_RDONLY | O_CLOEXEC); close(pw_fd); if (base_dir_fd < 0) { @@ -274,7 +274,7 @@ initialize(struct private *m) } { - int fd = openat(base_dir_fd, "manufacturer", O_RDONLY); + int fd = openat(base_dir_fd, "manufacturer", O_RDONLY | O_CLOEXEC); if (fd == -1) { LOG_WARN("/sys/class/power_supply/%s/manufacturer: %s", m->battery, strerror(errno)); m->manufacturer = NULL; @@ -285,7 +285,7 @@ initialize(struct private *m) } { - int fd = openat(base_dir_fd, "model_name", O_RDONLY); + int fd = openat(base_dir_fd, "model_name", O_RDONLY | O_CLOEXEC); if (fd == -1) { LOG_WARN("/sys/class/power_supply/%s/model_name: %s", m->battery, strerror(errno)); m->model = NULL; @@ -298,7 +298,7 @@ initialize(struct private *m) if (faccessat(base_dir_fd, "energy_full_design", O_RDONLY, 0) == 0 && faccessat(base_dir_fd, "energy_full", O_RDONLY, 0) == 0) { { - int fd = openat(base_dir_fd, "energy_full_design", O_RDONLY); + int fd = openat(base_dir_fd, "energy_full_design", O_RDONLY | O_CLOEXEC); if (fd == -1) { LOG_ERRNO("/sys/class/power_supply/%s/energy_full_design", m->battery); goto err; @@ -309,7 +309,7 @@ initialize(struct private *m) } { - int fd = openat(base_dir_fd, "energy_full", O_RDONLY); + int fd = openat(base_dir_fd, "energy_full", O_RDONLY | O_CLOEXEC); if (fd == -1) { LOG_ERRNO("/sys/class/power_supply/%s/energy_full", m->battery); goto err; @@ -325,7 +325,7 @@ initialize(struct private *m) if (faccessat(base_dir_fd, "charge_full_design", O_RDONLY, 0) == 0 && faccessat(base_dir_fd, "charge_full", O_RDONLY, 0) == 0) { { - int fd = openat(base_dir_fd, "charge_full_design", O_RDONLY); + int fd = openat(base_dir_fd, "charge_full_design", O_RDONLY | O_CLOEXEC); if (fd == -1) { LOG_ERRNO("/sys/class/power_supply/%s/charge_full_design", m->battery); goto err; @@ -336,7 +336,7 @@ initialize(struct private *m) } { - int fd = openat(base_dir_fd, "charge_full", O_RDONLY); + int fd = openat(base_dir_fd, "charge_full", O_RDONLY | O_CLOEXEC); if (fd == -1) { LOG_ERRNO("/sys/class/power_supply/%s/charge_full", m->battery); goto err; @@ -362,13 +362,13 @@ update_status(struct module *mod) { struct private *m = mod->private; - int pw_fd = open("/sys/class/power_supply", O_RDONLY); + int pw_fd = open("/sys/class/power_supply", O_RDONLY | O_CLOEXEC); if (pw_fd < 0) { LOG_ERRNO("/sys/class/power_supply"); return false; } - int base_dir_fd = openat(pw_fd, m->battery, O_RDONLY); + int base_dir_fd = openat(pw_fd, m->battery, O_RDONLY | O_CLOEXEC); close(pw_fd); if (base_dir_fd < 0) { @@ -376,14 +376,14 @@ update_status(struct module *mod) return false; } - int status_fd = openat(base_dir_fd, "status", O_RDONLY); + int status_fd = openat(base_dir_fd, "status", O_RDONLY | O_CLOEXEC); if (status_fd < 0) { LOG_ERRNO("/sys/class/power_supply/%s/status", m->battery); close(base_dir_fd); return false; } - int capacity_fd = openat(base_dir_fd, "capacity", O_RDONLY); + int capacity_fd = openat(base_dir_fd, "capacity", O_RDONLY | O_CLOEXEC); if (capacity_fd < 0) { LOG_ERRNO("/sys/class/power_supply/%s/capacity", m->battery); close(status_fd); @@ -391,12 +391,12 @@ update_status(struct module *mod) return false; } - int energy_fd = openat(base_dir_fd, "energy_now", O_RDONLY); - int power_fd = openat(base_dir_fd, "power_now", O_RDONLY); - int charge_fd = openat(base_dir_fd, "charge_now", O_RDONLY); - int current_fd = openat(base_dir_fd, "current_now", O_RDONLY); - int time_to_empty_fd = openat(base_dir_fd, "time_to_empty_now", O_RDONLY); - int time_to_full_fd = openat(base_dir_fd, "time_to_full_now", O_RDONLY); + int energy_fd = openat(base_dir_fd, "energy_now", O_RDONLY | O_CLOEXEC); + int power_fd = openat(base_dir_fd, "power_now", O_RDONLY | O_CLOEXEC); + int charge_fd = openat(base_dir_fd, "charge_now", O_RDONLY | O_CLOEXEC); + int current_fd = openat(base_dir_fd, "current_now", O_RDONLY | O_CLOEXEC); + int time_to_empty_fd = openat(base_dir_fd, "time_to_empty_now", O_RDONLY | O_CLOEXEC); + int time_to_full_fd = openat(base_dir_fd, "time_to_full_now", O_RDONLY | O_CLOEXEC); long capacity = readint_from_fd(capacity_fd); long energy = energy_fd >= 0 ? readint_from_fd(energy_fd) : -1; diff --git a/modules/cpu.c b/modules/cpu.c index 833c188..118361e 100644 --- a/modules/cpu.c +++ b/modules/cpu.c @@ -124,7 +124,7 @@ refresh_cpu_stats(struct cpu_stats *cpu_stats, size_t core_count) size_t len = 0; ssize_t read; - fp = fopen("/proc/stat", "r"); + fp = fopen("/proc/stat", "re"); if (NULL == fp) { LOG_ERRNO("unable to open /proc/stat"); return; diff --git a/modules/disk-io.c b/modules/disk-io.c index 015715f..c33cbef 100644 --- a/modules/disk-io.c +++ b/modules/disk-io.c @@ -105,7 +105,7 @@ refresh_device_stats(struct private *m) size_t len = 0; ssize_t read; - fp = fopen("/proc/diskstats", "r"); + fp = fopen("/proc/diskstats", "re"); if (NULL == fp) { LOG_ERRNO("unable to open /proc/diskstats"); return; diff --git a/modules/dwl.c b/modules/dwl.c index a0d5797..3b1bdcc 100644 --- a/modules/dwl.c +++ b/modules/dwl.c @@ -330,7 +330,7 @@ run_init(int *inotify_fd, int *inotify_wd, FILE **file, char *dwl_info_filename) return 1; } - *file = fopen(dwl_info_filename, "r"); + *file = fopen(dwl_info_filename, "re"); if (*file == NULL) { inotify_rm_watch(*inotify_fd, *inotify_wd); close(*inotify_fd); diff --git a/modules/i3.c b/modules/i3.c index b1d1ca8..47f6d99 100644 --- a/modules/i3.c +++ b/modules/i3.c @@ -664,7 +664,7 @@ handle_window_event(int sock, int type, const struct json_object *json, void *_m char path[64]; snprintf(path, sizeof(path), "/proc/%u/comm", ws->window.pid); - int fd = open(path, O_RDONLY); + int fd = open(path, O_RDONLY | O_CLOEXEC); if (fd == -1) { /* Application may simply have terminated */ free(ws->window.application); diff --git a/modules/mem.c b/modules/mem.c index dc9bcf8..de4e133 100644 --- a/modules/mem.c +++ b/modules/mem.c @@ -54,7 +54,7 @@ get_mem_stats(uint64_t *mem_free, uint64_t *mem_total) size_t len = 0; ssize_t read = 0; - fp = fopen("/proc/meminfo", "r"); + fp = fopen("/proc/meminfo", "re"); if (NULL == fp) { LOG_ERRNO("unable to open /proc/meminfo"); return false; diff --git a/modules/network.c b/modules/network.c index 1b2ceba..46a3148 100644 --- a/modules/network.c +++ b/modules/network.c @@ -1576,7 +1576,7 @@ out: static struct module * network_new(struct particle *label, int poll_interval, int left_spacing, int right_spacing) { - int urandom_fd = open("/dev/urandom", O_RDONLY); + int urandom_fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC); if (urandom_fd < 0) { LOG_ERRNO("failed to open /dev/urandom"); return NULL; diff --git a/modules/pulse.c b/modules/pulse.c index e605dea..f6c7f69 100644 --- a/modules/pulse.c +++ b/modules/pulse.c @@ -438,7 +438,7 @@ run(struct module *mod) } // Create refresh timer. - priv->refresh_timer_fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK); + priv->refresh_timer_fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC); if (priv->refresh_timer_fd < 0) { LOG_ERRNO("failed to create timerfd"); pa_mainloop_free(priv->mainloop); diff --git a/modules/removables.c b/modules/removables.c index e4ef98e..a4fb4ad 100644 --- a/modules/removables.c +++ b/modules/removables.c @@ -162,7 +162,7 @@ static void find_mount_points(const char *dev_path, mount_point_list_t *mount_points) { int fd = open("/proc/self/mountinfo", O_RDONLY | O_CLOEXEC); - FILE *f = fd >= 0 ? fdopen(fd, "r") : NULL; + FILE *f = fd >= 0 ? fdopen(fd, "re") : NULL; if (fd < 0 || f == NULL) { LOG_ERRNO("failed to open /proc/self/mountinfo"); diff --git a/modules/xwindow.c b/modules/xwindow.c index ffae527..c730128 100644 --- a/modules/xwindow.c +++ b/modules/xwindow.c @@ -130,7 +130,7 @@ update_application(struct module *mod) char path[1024]; snprintf(path, sizeof(path), "/proc/%d/cmdline", pid); - int fd = open(path, O_RDONLY); + int fd = open(path, O_RDONLY | O_CLOEXEC); if (fd == -1) return; From 3e0083c9f21a276840e3487a0a6a85c71e185b4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 23 Oct 2024 09:40:06 +0200 Subject: [PATCH 586/611] module/removables: no need to open+fdopen, just do fopen() --- modules/removables.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/modules/removables.c b/modules/removables.c index a4fb4ad..df4ade4 100644 --- a/modules/removables.c +++ b/modules/removables.c @@ -161,13 +161,10 @@ content(struct module *mod) static void find_mount_points(const char *dev_path, mount_point_list_t *mount_points) { - int fd = open("/proc/self/mountinfo", O_RDONLY | O_CLOEXEC); - FILE *f = fd >= 0 ? fdopen(fd, "re") : NULL; + FILE *f = fopen("/proc/self/mountinfo", "re"); - if (fd < 0 || f == NULL) { + if (f == NULL) { LOG_ERRNO("failed to open /proc/self/mountinfo"); - if (fd >= 0) - close(fd); return; } From b15714b38a1ed58196046d4365c45e85f552a8ce Mon Sep 17 00:00:00 2001 From: Alexey Yerin Date: Sat, 23 Nov 2024 20:10:14 +0300 Subject: [PATCH 587/611] pipewire: Improve handling of node switching When switching to a node that has a missing property, yambar didn't reset its internal state to the default value, causing outdated information to be displayed. --- CHANGELOG.md | 2 ++ modules/pipewire.c | 55 ++++++++++++++++++++++++++-------------------- 2 files changed, 33 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 09db85b..3192cec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,9 +50,11 @@ * mpd: yambar never attempting to reconnect after MPD closed the connection (for example, when MPD is restarted). * Bar positioning on multi-monitor setups, when `location=bottom`. +* pipewire: Improve handling of node switching ([#424][424]). [377]: https://codeberg.org/dnkl/yambar/issues/377 [300]: https://codeberg.org/dnkl/yambar/issues/300 +[424]: https://codeberg.org/dnkl/yambar/pulls/424 ### Security diff --git a/modules/pipewire.c b/modules/pipewire.c index e614a0a..98b96d8 100644 --- a/modules/pipewire.c +++ b/modules/pipewire.c @@ -45,6 +45,16 @@ struct output_informations { }; static struct output_informations const output_informations_null; +static void +output_informations_destroy(struct output_informations *output_informations) +{ + free(output_informations->name); + free(output_informations->description); + free(output_informations->icon); + free(output_informations->form_factor); + free(output_informations->bus); +} + struct data; struct private { @@ -213,18 +223,23 @@ node_find_route(struct data *data, bool is_sink) static void node_unhook_binded_node(struct data *data, bool is_sink) { + struct private *private = data->module->private; + struct node **target_node = NULL; struct spa_hook *target_listener = NULL; void **target_proxy = NULL; + struct output_informations *output_informations = NULL; if (is_sink) { target_node = &data->binded_sink; target_listener = &data->node_sink_listener; target_proxy = &data->node_sink; + output_informations = &private->sink_informations; } else { target_node = &data->binded_source; target_listener = &data->node_source_listener; target_proxy = &data->node_source; + output_informations = &private->source_informations; } if (*target_node == NULL) @@ -235,6 +250,9 @@ node_unhook_binded_node(struct data *data, bool is_sink) *target_node = NULL; *target_proxy = NULL; + + output_informations_destroy(output_informations); + *output_informations = output_informations_null; } static void @@ -398,18 +416,18 @@ node_events_info(void *userdata, struct pw_node_info const *info) struct spa_dict_item const *item = NULL; item = spa_dict_lookup_item(info->props, "node.name"); - if (item != NULL) - X_FREE_SET(output_informations->name, X_STRDUP(item->value)); + X_FREE_SET(output_informations->name, item != NULL ? X_STRDUP(item->value) : NULL); item = spa_dict_lookup_item(info->props, "node.description"); - if (item != NULL) - X_FREE_SET(output_informations->description, X_STRDUP(item->value)); + X_FREE_SET(output_informations->description, item != NULL ? X_STRDUP(item->value) : NULL); item = spa_dict_lookup_item(info->props, "device.id"); if (item != NULL) { uint32_t value = 0; spa_atou32(item->value, &value, 10); output_informations->device_id = value; + } else { + output_informations->device_id = 0; } item = spa_dict_lookup_item(info->props, "card.profile.device"); @@ -417,30 +435,29 @@ node_events_info(void *userdata, struct pw_node_info const *info) uint32_t value = 0; spa_atou32(item->value, &value, 10); output_informations->card_profile_device_id = value; + } else { + output_informations->card_profile_device_id = 0; } /* Device's information has an more important priority than node's information */ /* icon_name */ struct route *route = node_find_route(data, node_data->is_sink); if (route != NULL && route->icon_name != NULL) - output_informations->icon = X_STRDUP(route->icon_name); + X_FREE_SET(output_informations->icon, X_STRDUP(route->icon_name)); else { item = spa_dict_lookup_item(info->props, "device.icon-name"); - if (item != NULL) - X_FREE_SET(output_informations->icon, X_STRDUP(item->value)); + X_FREE_SET(output_informations->icon, item != NULL ? X_STRDUP(item->value) : NULL); } /* form_factor */ if (route != NULL && route->form_factor != NULL) - output_informations->form_factor = X_STRDUP(route->form_factor); + X_FREE_SET(output_informations->form_factor, X_STRDUP(route->form_factor)); else { item = spa_dict_lookup_item(info->props, "device.form-factor"); - if (item != NULL) - X_FREE_SET(output_informations->form_factor, X_STRDUP(item->value)); + X_FREE_SET(output_informations->form_factor, item != NULL ? X_STRDUP(item->value) : NULL); } item = spa_dict_lookup_item(info->props, "device.bus"); - if (item != NULL) - X_FREE_SET(output_informations->bus, X_STRDUP(item->value)); + X_FREE_SET(output_informations->bus, item != NULL ? X_STRDUP(item->value) : NULL); data->module->bar->refresh(data->module->bar); } @@ -827,18 +844,8 @@ destroy(struct module *module) pipewire_deinit(private->data); private->label->destroy(private->label); - /* sink */ - free(private->sink_informations.name); - free(private->sink_informations.description); - free(private->sink_informations.icon); - free(private->sink_informations.form_factor); - free(private->sink_informations.bus); - /* source */ - free(private->source_informations.name); - free(private->source_informations.description); - free(private->source_informations.icon); - free(private->source_informations.form_factor); - free(private->source_informations.bus); + output_informations_destroy(&private->sink_informations); + output_informations_destroy(&private->source_informations); free(private); module_default_destroy(module); From 57711f0dbe84f9c9b7f5e019813d0a9f52698d8d Mon Sep 17 00:00:00 2001 From: Ben Boeckel Date: Tue, 24 Dec 2024 23:52:13 +0100 Subject: [PATCH 588/611] mpd: support the `single` flag This flag indicates that `mpd` will automatically stop after the current song is played. --- CHANGELOG.md | 2 ++ doc/yambar-modules-mpd.5.scd | 3 +++ modules/mpd.c | 5 ++++- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3192cec..d16b477 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,11 +26,13 @@ is on. * Added "string like" `~~` operator to Map particle. Allows glob-style matching on strings using `*` and `?` characters ([#400][400]). +* Added "single" mode flag to the `mpd` module ([#428][428]). [96]: https://codeberg.org/dnkl/yambar/issues/96 [380]: https://codeberg.org/dnkl/yambar/issues/380 [392]: https://codeberg.org/dnkl/yambar/issues/392 [400]: https://codeberg.org/dnkl/yambar/pulls/400 +[428]: https://codeberg.org/dnkl/yambar/pulls/428 ### Changed diff --git a/doc/yambar-modules-mpd.5.scd b/doc/yambar-modules-mpd.5.scd index aff6227..d89407a 100644 --- a/doc/yambar-modules-mpd.5.scd +++ b/doc/yambar-modules-mpd.5.scd @@ -20,6 +20,9 @@ mpd - This module provides MPD status such as currently playing artist/album/son | consume : bool : True if the *consume* flag is set +| single +: bool +: True if the *single* flag is set | volume : range : Volume of MPD in percentage diff --git a/modules/mpd.c b/modules/mpd.c index 63da818..e70e41f 100644 --- a/modules/mpd.c +++ b/modules/mpd.c @@ -39,6 +39,7 @@ struct private bool repeat; bool random; bool consume; + bool single; int volume; char *album; char *artist; @@ -176,6 +177,7 @@ content(struct module *mod) tag_new_bool(mod, "repeat", m->repeat), tag_new_bool(mod, "random", m->random), tag_new_bool(mod, "consume", m->consume), + tag_new_bool(mod, "single", m->single), tag_new_int_range(mod, "volume", m->volume, 0, 100), tag_new_string(mod, "album", m->album), tag_new_string(mod, "artist", m->artist), @@ -187,7 +189,7 @@ content(struct module *mod) tag_new_int_realtime( mod, "elapsed", elapsed, 0, m->duration, realtime), }, - .count = 13, + .count = 14, }; mtx_unlock(&mod->lock); @@ -336,6 +338,7 @@ update_status(struct module *mod) m->repeat = mpd_status_get_repeat(status); m->random = mpd_status_get_random(status); m->consume = mpd_status_get_consume(status); + m->single = mpd_status_get_single_state(status) == MPD_SINGLE_ONESHOT; m->volume = mpd_status_get_volume(status); m->duration = mpd_status_get_total_time(status) * 1000; m->elapsed.value = mpd_status_get_elapsed_ms(status); From 61d082c802c46438be0c1b165367536290328c1c Mon Sep 17 00:00:00 2001 From: Ben Boeckel Date: Tue, 24 Dec 2024 23:48:34 +0100 Subject: [PATCH 589/611] typos: fix some typos --- bar/xcb.c | 2 +- doc/yambar-decorations.5.scd | 2 +- doc/yambar-modules-disk-io.5.scd | 4 ++-- doc/yambar-modules-pipewire.5.scd | 4 ++-- doc/yambar-modules-script.5.scd | 2 +- doc/yambar-particles.5.scd | 2 +- doc/yambar.1.scd | 2 +- modules/pipewire.c | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/bar/xcb.c b/bar/xcb.c index f3167a5..ae52bf3 100644 --- a/bar/xcb.c +++ b/bar/xcb.c @@ -369,7 +369,7 @@ refresh(const struct bar *_bar) /* Send an event to handle refresh from main thread */ - /* Note: docs say that all X11 events are 32 bytes, reglardless of + /* Note: docs say that all X11 events are 32 bytes, regardless of * the size of the event structure */ xcb_expose_event_t *evt = calloc(32, 1); diff --git a/doc/yambar-decorations.5.scd b/doc/yambar-decorations.5.scd index 9dd21b8..3d7c379 100644 --- a/doc/yambar-decorations.5.scd +++ b/doc/yambar-decorations.5.scd @@ -137,7 +137,7 @@ content: # STACK -This particles combines multiple decorations. +This particle combines multiple decorations. ## CONFIGURATION diff --git a/doc/yambar-modules-disk-io.5.scd b/doc/yambar-modules-disk-io.5.scd index 5203316..3f51e79 100644 --- a/doc/yambar-modules-disk-io.5.scd +++ b/doc/yambar-modules-disk-io.5.scd @@ -17,8 +17,8 @@ currently present in the machine. for the machine | is_disk : boolean -: whether or not the device is a disk (e.g. sda, sdb) or a partition - (e.g. sda1, sda2, ...). "Total" is advertised as a disk. +: whether or not the device is a disk (e.g., sda, sdb) or a partition + (e.g., sda1, sda2, ...). "Total" is advertised as a disk. | read_speed : int : bytes read, in bytes/s diff --git a/doc/yambar-modules-pipewire.5.scd b/doc/yambar-modules-pipewire.5.scd index be94489..ba79aaf 100644 --- a/doc/yambar-modules-pipewire.5.scd +++ b/doc/yambar-modules-pipewire.5.scd @@ -19,10 +19,10 @@ pipewire - Monitors pipewire for volume, mute/unmute, device change : Current device description | form_factor : string -: Current device form factor (headset, speaker, mic, etc) +: Current device form factor (headset, speaker, mic, etc.) | bus : string -: Current device bus (bluetooth, alsa, etc) +: Current device bus (bluetooth, alsa, etc.) | icon : string : Current device icon name diff --git a/doc/yambar-modules-script.5.scd b/doc/yambar-modules-script.5.scd index d27a006..48722cf 100644 --- a/doc/yambar-modules-script.5.scd +++ b/doc/yambar-modules-script.5.scd @@ -16,7 +16,7 @@ configurable amount of time. In continuous mode, the script is executed once. It will typically run in a loop, sending an updated tag set whenever it needs, or wants to. The last tag set is used (displayed) by yambar until a new tag set -is received. This mode is intended to be used by scripts that depends +is received. This mode is intended to be used by scripts that depend on non-polling methods to update their state. Tag sets, or _transactions_, are separated by an empty line diff --git a/doc/yambar-particles.5.scd b/doc/yambar-particles.5.scd index 231b419..325ef89 100644 --- a/doc/yambar-particles.5.scd +++ b/doc/yambar-particles.5.scd @@ -155,7 +155,7 @@ content: This particle is a list (or sequence, if you like) of other particles. It can be used to render e.g. _string_ particles with -different font and/or color formatting. Or ay other particle +different font and/or color formatting. Or any other particle combinations. But note that this means you *cannot* set any attributes on the _list_ diff --git a/doc/yambar.1.scd b/doc/yambar.1.scd index 549b980..2aaa46f 100644 --- a/doc/yambar.1.scd +++ b/doc/yambar.1.scd @@ -25,7 +25,7 @@ yambar - modular status panel for X11 and Wayland *-p*,*--print-pid*=_FILE_|_FD_ Print PID to this file, or FD, when successfully started. The file (or FD) is closed immediately after writing the PID. When a _FILE_ - as been specified, the file is unlinked exit. + as been specified, the file is unlinked upon exiting. *-d*,*--log-level*={*info*,*warning*,*error*,*none*} Log level, used both for log output on stderr as well as diff --git a/modules/pipewire.c b/modules/pipewire.c index 98b96d8..a2fdcae 100644 --- a/modules/pipewire.c +++ b/modules/pipewire.c @@ -368,7 +368,7 @@ device_events_param(void *userdata, int seq, uint32_t id, uint32_t index, uint32 if (binded_node == NULL) return; - /* Node's device is the the same as route's device */ + /* Node's device is the same as route's device */ if (output_informations->device_id != route->device->id) return; From d746d12f6a36a93f8a2205ea03ccb4e4e06876af Mon Sep 17 00:00:00 2001 From: vova Date: Tue, 3 Sep 2024 21:18:05 +0200 Subject: [PATCH 590/611] add niri-workspaces and niri-language modules --- CHANGELOG.md | 2 + doc/meson.build | 6 + doc/yambar-modules-niri-language.5.scd | 34 ++ doc/yambar-modules-niri-workspaces.5.scd | 60 ++++ doc/yambar-modules.5.scd | 4 + meson.build | 2 + meson_options.txt | 4 + modules/meson.build | 14 + modules/niri-common.c | 377 +++++++++++++++++++++++ modules/niri-common.h | 45 +++ modules/niri-language.c | 160 ++++++++++ modules/niri-workspaces.c | 163 ++++++++++ plugin.c | 12 + 13 files changed, 883 insertions(+) create mode 100644 doc/yambar-modules-niri-language.5.scd create mode 100644 doc/yambar-modules-niri-workspaces.5.scd create mode 100644 modules/niri-common.c create mode 100644 modules/niri-common.h create mode 100644 modules/niri-language.c create mode 100644 modules/niri-workspaces.c diff --git a/CHANGELOG.md b/CHANGELOG.md index d16b477..aec47c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,12 +27,14 @@ * Added "string like" `~~` operator to Map particle. Allows glob-style matching on strings using `*` and `?` characters ([#400][400]). * Added "single" mode flag to the `mpd` module ([#428][428]). +* niri: add a new module for niri-workspaces and niri-language ([#405][405]). [96]: https://codeberg.org/dnkl/yambar/issues/96 [380]: https://codeberg.org/dnkl/yambar/issues/380 [392]: https://codeberg.org/dnkl/yambar/issues/392 [400]: https://codeberg.org/dnkl/yambar/pulls/400 [428]: https://codeberg.org/dnkl/yambar/pulls/428 +[405]: https://codeberg.org/dnkl/yambar/issues/405 ### Changed diff --git a/doc/meson.build b/doc/meson.build index e5728ab..90a83ec 100644 --- a/doc/meson.build +++ b/doc/meson.build @@ -44,6 +44,12 @@ endif if plugin_network_enabled plugin_pages += ['yambar-modules-network.5.scd'] endif +if plugin_niri_language_enabled + plugin_pages += ['yambar-modules-niri-language.5.scd'] +endif +if plugin_niri_workspaces_enabled + plugin_pages += ['yambar-modules-niri-workspaces.5.scd'] +endif if plugin_pipewire_enabled plugin_pages += ['yambar-modules-pipewire.5.scd'] endif diff --git a/doc/yambar-modules-niri-language.5.scd b/doc/yambar-modules-niri-language.5.scd new file mode 100644 index 0000000..befa41e --- /dev/null +++ b/doc/yambar-modules-niri-language.5.scd @@ -0,0 +1,34 @@ +yambar-modules-niri-language(5) + +# NAME +niri-language - This module provides information about niri's currently +selected language. + +# TAGS + +[[ *Name* +:[ *Type* +:< *Description* +| language +: string +: The currently selected language. + +# CONFIGURATION + +No additional attributes supported, only the generic ones (see +*GENERIC CONFIGURATION* in *yambar-modules*(5)) + +# EXAMPLES + +``` +bar: + left: + - niri-language: + content: + string: {text: "{language}"} +``` + +# SEE ALSO + +*yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5) + diff --git a/doc/yambar-modules-niri-workspaces.5.scd b/doc/yambar-modules-niri-workspaces.5.scd new file mode 100644 index 0000000..812bade --- /dev/null +++ b/doc/yambar-modules-niri-workspaces.5.scd @@ -0,0 +1,60 @@ +yambar-modules-niri-workspaces(5) + +# NAME +niri-workspaces - This module provides information about niri workspaces. + +# DESCRIPTION + +This module provides a map of each workspace present in niri. + +Each workspace has its _id_, _name_, and its status (_focused_, +_active_, _empty_). The workspaces are sorted by their ids. + +This module will *only* track the monitor where yambar was launched. +If you have a multi monitor setup, please launch yambar on each +individual monitor to track its workspaces. + +# TAGS + +[[ *Name* +:[ *Type* +:< *Description* +| id +: int +: The workspace id. +| name +: string +: The name of the workspace. +| active +: bool +: True if the workspace is currently visible on the current output. +| focused +: bool +: True if the workspace is currently focused. +| empty +: bool +: True if the workspace contains no window. + +# CONFIGURATION + +No additional attributes supported, only the generic ones (see +*GENERIC CONFIGURATION* in *yambar-modules*(5)) + +# EXAMPLES + +``` +bar: + left: + - niri-workspaces: + content: + map: + default: {string: {text: "| {id}"}} + conditions: + active: {string: {text: "-> {id}"}} + ~empty: {string: {text: "@ {id}"}} +``` + +# SEE ALSO + +*yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5) + diff --git a/doc/yambar-modules.5.scd b/doc/yambar-modules.5.scd index 765d06f..1ec4871 100644 --- a/doc/yambar-modules.5.scd +++ b/doc/yambar-modules.5.scd @@ -174,6 +174,10 @@ Available modules have their own pages: *yambar-modules-sway*(5) +*yambar-modules-niri-language*(5) + +*yambar-modules-niri-workspaces*(5) + *yambar-modules-xkb*(5) *yambar-modules-xwindow*(5) diff --git a/meson.build b/meson.build index d9b1364..81af577 100644 --- a/meson.build +++ b/meson.build @@ -189,6 +189,8 @@ summary( 'River': plugin_river_enabled, 'Script': plugin_script_enabled, 'Sway XKB keyboard': plugin_sway_xkb_enabled, + 'Niri language': plugin_niri_language_enabled, + 'Niri workspaces': plugin_niri_workspaces_enabled, 'XKB keyboard (for X11)': plugin_xkb_enabled, 'XWindow (window tracking for X11)': plugin_xwindow_enabled, }, diff --git a/meson_options.txt b/meson_options.txt index a9aac05..9fd0dd5 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -44,6 +44,10 @@ option('plugin-script', type: 'feature', value: 'auto', description: 'Script support') option('plugin-sway-xkb', type: 'feature', value: 'auto', description: 'keyboard support for Sway') +option('plugin-niri-language', type: 'feature', value: 'auto', + description: 'language support for Niri') +option('plugin-niri-workspaces', type: 'feature', value: 'auto', + description: 'workspaces support for Niri') option('plugin-xkb', type: 'feature', value: 'auto', description: 'keyboard support for X11') option('plugin-xwindow', type: 'feature', value: 'auto', diff --git a/modules/meson.build b/modules/meson.build index e2ed56e..b54e9d7 100644 --- a/modules/meson.build +++ b/modules/meson.build @@ -45,6 +45,12 @@ plugin_script_enabled = get_option('plugin-script').allowed() json_sway_xkb = dependency('json-c', required: get_option('plugin-sway-xkb')) plugin_sway_xkb_enabled = json_sway_xkb.found() +json_niri_language = dependency('json-c', required: get_option('plugin-niri-language')) +plugin_niri_language_enabled = json_niri_language.found() + +json_niri_workspaces = dependency('json-c', required: get_option('plugin-niri-workspaces')) +plugin_niri_workspaces_enabled = json_niri_workspaces.found() + xcb_xkb = dependency('xcb-xkb', required: get_option('plugin-xkb')) plugin_xkb_enabled = backend_x11 and xcb_xkb.found() @@ -121,6 +127,14 @@ if plugin_sway_xkb_enabled mod_data += {'sway-xkb': [['i3-common.c', 'i3-common.h'], [dynlist, json_sway_xkb]]} endif +if plugin_niri_language_enabled + mod_data += {'niri-language': [['niri-common.c', 'niri-common.h'], [dynlist, json_niri_language]]} +endif + +if plugin_niri_workspaces_enabled + mod_data += {'niri-workspaces': [['niri-common.c', 'niri-common.h'], [dynlist, json_niri_workspaces]]} +endif + if plugin_xkb_enabled mod_data += {'xkb': [[], [xcb_stuff, xcb_xkb]]} endif diff --git a/modules/niri-common.c b/modules/niri-common.c new file mode 100644 index 0000000..ac53921 --- /dev/null +++ b/modules/niri-common.c @@ -0,0 +1,377 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../log.h" +#include "niri-common.h" + +#define LOG_MODULE "niri:common" +#define LOG_ENABLE_DBG 0 + +static struct niri_socket instance = { + .fd = -1, + .abort_fd = -1, +}; + +static void +workspace_free(struct niri_workspace *workspace) +{ + free(workspace->name); + free(workspace); +} + +static void +parser(char *response) +{ + enum json_tokener_error error = json_tokener_success; + struct json_object *json = json_tokener_parse_verbose(response, &error); + if (error != json_tokener_success) { + LOG_WARN("failed to parse niri socket's response"); + return; + } + + enum niri_event events = 0; + struct json_object_iterator it = json_object_iter_begin(json); + struct json_object_iterator end = json_object_iter_end(json); + while (!json_object_iter_equal(&it, &end)) { + char const *key = json_object_iter_peek_name(&it); + + // "WorkspacesChanged": { + // "workspaces": [ + // { + // "id": 3, + // "idx": 1, + // "name": null, + // "output": "DP-4", + // "is_active": true, + // "is_focused": true, + // "active_window_id": 24 + // }, + // ... + // ] + // } + if (strcmp(key, "WorkspacesChanged") == 0) { + mtx_lock(&instance.mtx); + tll_foreach(instance.workspaces, it) { tll_remove_and_free(instance.workspaces, it, workspace_free); } + mtx_unlock(&instance.mtx); + + json_object *obj = json_object_iter_peek_value(&it); + json_object *workspaces = json_object_object_get(obj, "workspaces"); + + size_t length = json_object_array_length(workspaces); + for (size_t i = 0; i < length; ++i) { + json_object *ws_obj = json_object_array_get_idx(workspaces, i); + + // only add workspaces on the current yambar's monitor + struct json_object *output = json_object_object_get(ws_obj, "output"); + if (strcmp(instance.monitor, json_object_get_string(output)) != 0) + continue; + + struct niri_workspace *ws = calloc(1, sizeof(*ws)); + ws->idx = json_object_get_int(json_object_object_get(ws_obj, "idx")); + ws->id = json_object_get_int(json_object_object_get(ws_obj, "id")); + ws->active = json_object_get_boolean(json_object_object_get(ws_obj, "is_active")); + ws->focused = json_object_get_boolean(json_object_object_get(ws_obj, "is_focused")); + ws->empty = json_object_get_int(json_object_object_get(ws_obj, "active_window_id")) == 0; + + char const *name = json_object_get_string(json_object_object_get(ws_obj, "name")); + if (name) + ws->name = strdup(name); + + mtx_lock(&instance.mtx); + bool inserted = false; + tll_foreach(instance.workspaces, it) + { + if (it->item->idx > ws->idx) { + tll_insert_before(instance.workspaces, it, ws); + inserted = true; + break; + } + } + if (!inserted) + tll_push_back(instance.workspaces, ws); + mtx_unlock(&instance.mtx); + + events |= workspaces_changed; + } + } + + // "WorkspaceActivated": { + // "id": 7, + // "focused":true + // } + else if (strcmp(key, "WorkspaceActivated") == 0) { + json_object *obj = json_object_iter_peek_value(&it); + int id = json_object_get_int(json_object_object_get(obj, "id")); + + mtx_lock(&instance.mtx); + tll_foreach(instance.workspaces, it) + { + bool b = it->item->id == id; + it->item->focused = b; + it->item->active = b; + } + mtx_unlock(&instance.mtx); + + events |= workspace_activated; + } + + // "WorkspaceActiveWindowChanged": { + // "workspace_id": 3, + // "active_window_id": 8 + // } + else if (strcmp(key, "WorkspaceActiveWindowChanged") == 0) { + json_object *obj = json_object_iter_peek_value(&it); + int id = json_object_get_int(json_object_object_get(obj, "id")); + bool empty = json_object_get_int(json_object_object_get(obj, "active_window_id")) == 0; + + mtx_lock(&instance.mtx); + tll_foreach(instance.workspaces, it) + { + if (it->item->id == id) { + it->item->empty = empty; + break; + } + } + mtx_unlock(&instance.mtx); + + events |= workspace_active_window_changed; + } + + // + // "KeyboardLayoutsChanged": { + // "keyboard_layouts": { + // "names": [ + // "English (US)", + // "Russian" + // ], + // "current_idx": 0 + // } + // } + else if (strcmp(key, "KeyboardLayoutsChanged") == 0) { + tll_foreach(instance.keyboard_layouts, it) { tll_remove_and_free(instance.keyboard_layouts, it, free); } + + json_object *obj = json_object_iter_peek_value(&it); + json_object *kb_layouts = json_object_object_get(obj, "keyboard_layouts"); + + instance.keyboard_layout_index = json_object_get_int(json_object_object_get(kb_layouts, "current_idx")); + + json_object *names = json_object_object_get(kb_layouts, "names"); + size_t names_length = json_object_array_length(names); + for (size_t i = 0; i < names_length; ++i) { + char const *name = json_object_get_string(json_object_array_get_idx(names, i)); + tll_push_back(instance.keyboard_layouts, strdup(name)); + } + + events |= keyboard_layouts_changed; + } + + // "KeyboardLayoutSwitched": { + // "idx": 1 + // } + else if (strcmp(key, "KeyboardLayoutSwitched") == 0) { + json_object *obj = json_object_iter_peek_value(&it); + instance.keyboard_layout_index = json_object_get_int(json_object_object_get(obj, "idx")); + + events |= keyboard_layouts_switched; + } + + json_object_iter_next(&it); + } + + json_object_put(json); + + mtx_lock(&instance.mtx); + tll_foreach(instance.subscribers, it) + { + if (it->item->events & events) + if (write(it->item->fd, &(uint64_t){1}, sizeof(uint64_t)) == -1) + LOG_ERRNO("failed to write"); + } + mtx_unlock(&instance.mtx); +} + +static int +run(void *userdata) +{ + static char msg[] = "\"EventStream\"\n"; + static char expected[] = "{\"Ok\":\"Handled\"}"; + + if (write(instance.fd, msg, sizeof(msg) / sizeof(msg[0])) == -1) { + LOG_ERRNO("failed to sent message to niri socket"); + return thrd_error; + } + + static char buffer[8192]; + if (read(instance.fd, buffer, sizeof(buffer) / sizeof(buffer[0]) - 1) == -1) { + LOG_ERRNO("failed to read response of niri socket"); + return thrd_error; + } + + char *saveptr; + char *response = strtok_r(buffer, "\n", &saveptr); + if (response == NULL || strcmp(expected, response) != 0) { + // unexpected first response, something went wrong + LOG_ERR("unexpected response of niri socket"); + return thrd_error; + } + + while ((response = strtok_r(NULL, "\n", &saveptr)) != NULL) + parser(response); + + while (true) { + struct pollfd fds[] = { + (struct pollfd){.fd = instance.abort_fd, .events = POLLIN}, + (struct pollfd){.fd = instance.fd, .events = POLLIN}, + }; + + if (poll(fds, sizeof(fds) / sizeof(fds[0]), -1) == -1) { + if (errno == EINTR) + continue; + + LOG_ERRNO("failed to poll"); + break; + } + + if (fds[0].revents & POLLIN) + break; + + static char buffer[8192]; + ssize_t length = read(fds[1].fd, buffer, sizeof(buffer) / sizeof(buffer[0])); + + if (length == 0) + break; + + if (length == -1) { + if (errno == EAGAIN || errno == EINTR) + continue; + + LOG_ERRNO("unable to read niri socket"); + break; + } + + buffer[length] = '\0'; + saveptr = NULL; + response = strtok_r(buffer, "\n", &saveptr); + do { + parser(response); + } while ((response = strtok_r(NULL, "\n", &saveptr)) != NULL); + } + + return thrd_success; +} + +struct niri_socket * +niri_socket_open(char const *monitor) +{ + if (instance.fd >= 0) + return &instance; + + char const *path = getenv("NIRI_SOCKET"); + if (path == NULL) { + LOG_ERR("NIRI_SOCKET is empty. Is niri running?"); + return NULL; + } + + if ((instance.fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)) == -1) { + LOG_ERRNO("failed to create socket"); + goto error; + } + + struct sockaddr_un addr; + addr.sun_family = AF_UNIX; + addr.sun_path[sizeof(addr.sun_path) - 1] = '\0'; + strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1); + + if (connect(instance.fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) { + LOG_ERRNO("failed to connect to niri socket"); + goto error; + } + + if ((instance.abort_fd = eventfd(0, EFD_CLOEXEC)) == -1) { + LOG_ERRNO("failed to create abort_fd"); + goto error; + } + + if (mtx_init(&instance.mtx, mtx_plain) != thrd_success) { + LOG_ERR("failed to initialize mutex"); + goto error; + } + + if (thrd_create(&instance.thrd, run, NULL) != thrd_success) { + LOG_ERR("failed to create thread"); + mtx_destroy(&instance.mtx); + goto error; + } + + instance.monitor = monitor; + + return &instance; + +error: + if (instance.fd >= 0) + close(instance.fd); + if (instance.abort_fd >= 0) + close(instance.abort_fd); + instance.fd = -1; + instance.abort_fd = -1; + instance.monitor = NULL; + + return NULL; +} + +static void +socket_close(void) +{ + if (write(instance.abort_fd, &(uint64_t){1}, sizeof(uint64_t)) != sizeof(uint64_t)) + LOG_ERRNO("failed to write to abort_fd"); + + thrd_join(instance.thrd, NULL); + + close(instance.abort_fd); + close(instance.fd); + instance.abort_fd = -1; + instance.fd = -1; + + mtx_destroy(&instance.mtx); + + tll_free_and_free(instance.subscribers, free); + tll_free_and_free(instance.workspaces, workspace_free); + tll_free_and_free(instance.keyboard_layouts, free); +} + +void +niri_socket_close(void) +{ + static once_flag flag = ONCE_FLAG_INIT; + call_once(&flag, socket_close); +} + +int +niri_socket_subscribe(enum niri_event events) +{ + int fd = eventfd(0, EFD_CLOEXEC); + if (fd == -1) { + LOG_ERRNO("failed to create eventfd"); + return -1; + } + + struct niri_subscriber *subscriber = calloc(1, sizeof(*subscriber)); + subscriber->events = events; + subscriber->fd = fd; + + mtx_lock(&instance.mtx); + tll_push_back(instance.subscribers, subscriber); + mtx_unlock(&instance.mtx); + + return subscriber->fd; +} diff --git a/modules/niri-common.h b/modules/niri-common.h new file mode 100644 index 0000000..18afe38 --- /dev/null +++ b/modules/niri-common.h @@ -0,0 +1,45 @@ +#pragma once + +#include +#include +#include + +enum niri_event { + workspaces_changed = (1 << 0), + workspace_activated = (1 << 1), + workspace_active_window_changed = (1 << 2), + keyboard_layouts_changed = (1 << 3), + keyboard_layouts_switched = (1 << 4), +}; + +struct niri_subscriber { + int events; + int fd; +}; + +struct niri_workspace { + int id; + int idx; + char *name; + bool active; + bool focused; + bool empty; +}; + +struct niri_socket { + char const *monitor; + int abort_fd; + int fd; + + tll(struct niri_subscriber *) subscribers; + tll(struct niri_workspace *) workspaces; + tll(char *) keyboard_layouts; + size_t keyboard_layout_index; + + thrd_t thrd; + mtx_t mtx; +}; + +struct niri_socket *niri_socket_open(char const *monitor); +void niri_socket_close(void); +int niri_socket_subscribe(enum niri_event events); diff --git a/modules/niri-language.c b/modules/niri-language.c new file mode 100644 index 0000000..f8138ee --- /dev/null +++ b/modules/niri-language.c @@ -0,0 +1,160 @@ +#include +#include +#include +#include +#include + +#define LOG_MODULE "niri-language" +#define LOG_ENABLE_DBG 0 +#include "niri-common.h" + +#include "../log.h" +#include "../particles/dynlist.h" +#include "../plugin.h" + +struct private +{ + struct particle *label; + struct niri_socket *niri; +}; + +static void +destroy(struct module *module) +{ + struct private *private = module->private; + private->label->destroy(private->label); + + free(private); + + module_default_destroy(module); +} + +static const char * +description(const struct module *module) +{ + return "niri-lang"; +} + +static struct exposable * +content(struct module *module) +{ + const struct private *private = module->private; + + if (private->niri == NULL) + return dynlist_exposable_new(&((struct exposable *){0}), 0, 0, 0); + + mtx_lock(&module->lock); + mtx_lock(&private->niri->mtx); + + char *name = "???"; + size_t i = 0; + tll_foreach(private->niri->keyboard_layouts, it) + { + if (i++ == private->niri->keyboard_layout_index) + name = it->item; + } + + struct tag_set tags = { + .tags = (struct tag *[]){tag_new_string(module, "language", name)}, + .count = 1, + }; + + struct exposable *exposable = private->label->instantiate(private->label, &tags); + tag_set_destroy(&tags); + mtx_unlock(&private->niri->mtx); + mtx_unlock(&module->lock); + return exposable; +} + +static int +run(struct module *module) +{ + struct private *private = module->private; + + /* Ugly, but I didn't find better way for waiting + * the monitor's name to be set */ + char const *monitor; + do { + monitor = module->bar->output_name(module->bar); + usleep(50); + } while (monitor == NULL); + + private->niri = niri_socket_open(monitor); + if (private->niri == NULL) + return 1; + + int fd = niri_socket_subscribe(keyboard_layouts_changed | keyboard_layouts_switched); + if (fd == -1) { + niri_socket_close(); + return 1; + } + + module->bar->refresh(module->bar); + + while (true) { + struct pollfd fds[] = { + (struct pollfd){.fd = module->abort_fd, .events = POLLIN}, + (struct pollfd){.fd = fd, .events = POLLIN}, + }; + + if (poll(fds, sizeof(fds) / sizeof(fds[0]), -1) == -1) { + if (errno == EINTR) + continue; + + LOG_ERRNO("failed to poll"); + break; + } + + if (fds[0].revents & POLLIN) + break; + + if (read(fds[1].fd, &(uint64_t){0}, sizeof(uint64_t)) == -1) + LOG_ERRNO("failed to read from eventfd"); + + module->bar->refresh(module->bar); + } + + niri_socket_close(); + return 0; +} + +static struct module * +niri_language_new(struct particle *label) +{ + struct private *private = calloc(1, sizeof(struct private)); + private->label = label; + + struct module *module = module_common_new(); + module->private = private; + module->run = &run; + module->destroy = &destroy; + module->content = &content; + module->description = &description; + + return module; +} + +static struct module * +from_conf(struct yml_node const *node, struct conf_inherit inherited) +{ + struct yml_node const *content = yml_get_value(node, "content"); + return niri_language_new(conf_to_particle(content, inherited)); +} + +static bool +verify_conf(keychain_t *chain, const struct yml_node *node) +{ + static struct attr_info const attrs[] = { + MODULE_COMMON_ATTRS, + }; + return conf_verify_dict(chain, node, attrs); +} + +const struct module_iface module_niri_language_iface = { + .verify_conf = &verify_conf, + .from_conf = &from_conf, +}; + +#if defined(CORE_PLUGINS_AS_SHARED_LIBRARIES) +extern const struct module_iface iface __attribute__((weak, alias("module_niri_language_iface"))); +#endif diff --git a/modules/niri-workspaces.c b/modules/niri-workspaces.c new file mode 100644 index 0000000..bca0150 --- /dev/null +++ b/modules/niri-workspaces.c @@ -0,0 +1,163 @@ +#include +#include +#include +#include + +#define LOG_MODULE "niri-workspaces" +#define LOG_ENABLE_DBG 0 +#include "niri-common.h" + +#include "../log.h" +#include "../particles/dynlist.h" +#include "../plugin.h" + +struct private +{ + struct particle *label; + struct niri_socket *niri; +}; + +static void +destroy(struct module *module) +{ + struct private *private = module->private; + private->label->destroy(private->label); + + free(private); + + module_default_destroy(module); +} + +static const char * +description(const struct module *module) +{ + return "niri-ws"; +} + +static struct exposable * +content(struct module *module) +{ + struct private const *private = module->private; + + if (private->niri == NULL) + return dynlist_exposable_new(&((struct exposable *){0}), 0, 0, 0); + + mtx_lock(&module->lock); + mtx_lock(&private->niri->mtx); + + size_t i = 0; + struct exposable *exposable[tll_length(private->niri->workspaces)]; + tll_foreach(private->niri->workspaces, it) + { + struct tag_set tags = { + .tags = (struct tag*[]){ + tag_new_int(module, "id", it->item->idx), + tag_new_string(module, "name", it->item->name), + tag_new_bool(module, "active", it->item->active), + tag_new_bool(module, "focused", it->item->focused), + tag_new_bool(module, "empty", it->item->empty), + }, + .count = 5, + }; + + exposable[i++] = private->label->instantiate(private->label, &tags); + tag_set_destroy(&tags); + } + + mtx_unlock(&private->niri->mtx); + mtx_unlock(&module->lock); + return dynlist_exposable_new(exposable, i, 0, 0); +} + +static int +run(struct module *module) +{ + struct private *private = module->private; + + /* Ugly, but I didn't find better way for waiting + * the monitor's name to be set */ + char const *monitor; + do { + monitor = module->bar->output_name(module->bar); + usleep(50); + } while (monitor == NULL); + + private->niri = niri_socket_open(monitor); + if (private->niri == NULL) + return 1; + + int fd = niri_socket_subscribe(workspaces_changed | workspace_activated | workspace_active_window_changed); + if (fd == -1) { + niri_socket_close(); + return 1; + } + + module->bar->refresh(module->bar); + + while (true) { + struct pollfd fds[] = { + (struct pollfd){.fd = module->abort_fd, .events = POLLIN}, + (struct pollfd){.fd = fd, .events = POLLIN}, + }; + + if (poll(fds, sizeof(fds) / sizeof(fds[0]), -1) == -1) { + if (errno == EINTR) + continue; + + LOG_ERRNO("failed to poll"); + break; + } + + if (fds[0].revents & POLLIN) + break; + + if (read(fds[1].fd, &(uint64_t){0}, sizeof(uint64_t)) == -1) + LOG_ERRNO("failed to read from eventfd"); + + module->bar->refresh(module->bar); + } + + niri_socket_close(); + return 0; +} + +static struct module * +niri_workspaces_new(struct particle *label) +{ + struct private *private = calloc(1, sizeof(struct private)); + private->label = label; + + struct module *module = module_common_new(); + module->private = private; + module->run = &run; + module->destroy = &destroy; + module->content = &content; + module->description = &description; + + return module; +} + +static struct module * +from_conf(struct yml_node const *node, struct conf_inherit inherited) +{ + struct yml_node const *content = yml_get_value(node, "content"); + return niri_workspaces_new(conf_to_particle(content, inherited)); +} + +static bool +verify_conf(keychain_t *chain, const struct yml_node *node) +{ + static struct attr_info const attrs[] = { + MODULE_COMMON_ATTRS, + }; + return conf_verify_dict(chain, node, attrs); +} + +const struct module_iface module_niri_workspaces_iface = { + .verify_conf = &verify_conf, + .from_conf = &from_conf, +}; + +#if defined(CORE_PLUGINS_AS_SHARED_LIBRARIES) +extern const struct module_iface iface __attribute__((weak, alias("module_niri_workspaces_iface"))); +#endif diff --git a/plugin.c b/plugin.c index 8e75389..b1e268b 100644 --- a/plugin.c +++ b/plugin.c @@ -84,6 +84,12 @@ EXTERN_MODULE(script); #if defined(HAVE_PLUGIN_sway_xkb) EXTERN_MODULE(sway_xkb); #endif +#if defined(HAVE_PLUGIN_niri_language) +EXTERN_MODULE(niri_language); +#endif +#if defined(HAVE_PLUGIN_niri_workspaces) +EXTERN_MODULE(niri_workspaces); +#endif #if defined(HAVE_PLUGIN_xkb) EXTERN_MODULE(xkb); #endif @@ -214,6 +220,12 @@ static void __attribute__((constructor)) init(void) #if defined(HAVE_PLUGIN_sway_xkb) REGISTER_CORE_MODULE(sway-xkb, sway_xkb); #endif +#if defined(HAVE_PLUGIN_niri_language) + REGISTER_CORE_MODULE(niri-language, niri_language); +#endif +#if defined(HAVE_PLUGIN_niri_workspaces) + REGISTER_CORE_MODULE(niri-workspaces, niri_workspaces); +#endif #if defined(HAVE_PLUGIN_xkb) REGISTER_CORE_MODULE(xkb, xkb); #endif From e1f7c0292fadfcd369cf26da82f4c3504cc7f628 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 1 Jan 2025 13:52:52 +0100 Subject: [PATCH 591/611] changelog: fix ref for #405 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aec47c4..74181de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,7 +34,7 @@ [392]: https://codeberg.org/dnkl/yambar/issues/392 [400]: https://codeberg.org/dnkl/yambar/pulls/400 [428]: https://codeberg.org/dnkl/yambar/pulls/428 -[405]: https://codeberg.org/dnkl/yambar/issues/405 +[405]: https://codeberg.org/dnkl/yambar/pulls/405 ### Changed From fc24ea225d0cb66c1e6949cecd584c21ad1af9d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 1 Jan 2025 13:57:32 +0100 Subject: [PATCH 592/611] changelog: fix ref (again) for #405 - the issue number is #404 --- CHANGELOG.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 74181de..802c82b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,14 +27,15 @@ * Added "string like" `~~` operator to Map particle. Allows glob-style matching on strings using `*` and `?` characters ([#400][400]). * Added "single" mode flag to the `mpd` module ([#428][428]). -* niri: add a new module for niri-workspaces and niri-language ([#405][405]). +* niri: add a new module for niri-workspaces and niri-language + ([#404][404]). [96]: https://codeberg.org/dnkl/yambar/issues/96 [380]: https://codeberg.org/dnkl/yambar/issues/380 [392]: https://codeberg.org/dnkl/yambar/issues/392 [400]: https://codeberg.org/dnkl/yambar/pulls/400 [428]: https://codeberg.org/dnkl/yambar/pulls/428 -[405]: https://codeberg.org/dnkl/yambar/pulls/405 +[404]: https://codeberg.org/dnkl/yambar/issues/404 ### Changed From 21f374d2eb6d7db915bda4ca3551169454b19531 Mon Sep 17 00:00:00 2001 From: Ralph Torres Date: Mon, 24 Feb 2025 04:59:37 +0000 Subject: [PATCH 593/611] module/pipewire: add spacing config --- doc/yambar-modules-pipewire.5.scd | 12 ++++++++++++ modules/pipewire.c | 22 +++++++++++++++++++--- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/doc/yambar-modules-pipewire.5.scd b/doc/yambar-modules-pipewire.5.scd index ba79aaf..8010449 100644 --- a/doc/yambar-modules-pipewire.5.scd +++ b/doc/yambar-modules-pipewire.5.scd @@ -43,6 +43,18 @@ pipewire - Monitors pipewire for volume, mute/unmute, device change :[ *Type* :[ *Req* :< *Description* +| left-spacing +: int +: no +: Space, in pixels, in the left side of each rendered volume +| right-spacing +: int +: no +: Space, in pixels, on the right side of each rendered volume +| spacing +: int +: no +: Short-hand for setting both _left-spacing_ and _right-spacing_ | content : particle : yes diff --git a/modules/pipewire.c b/modules/pipewire.c index a2fdcae..1ff3642 100644 --- a/modules/pipewire.c +++ b/modules/pipewire.c @@ -60,6 +60,8 @@ struct private { struct particle *label; struct data *data; + int left_spacing; + int right_spacing; /* pipewire related */ struct output_informations sink_informations; @@ -918,7 +920,7 @@ content(struct module *module) mtx_unlock(&module->lock); - return dynlist_exposable_new(exposables, exposables_length, 0, 0); + return dynlist_exposable_new(exposables, exposables_length, private->left_spacing, private->right_spacing); } static int @@ -965,11 +967,13 @@ run(struct module *module) } static struct module * -pipewire_new(struct particle *label) +pipewire_new(struct particle *label, int left_spacing, int right_spacing) { struct private *private = calloc(1, sizeof(struct private)); assert(private != NULL); private->label = label; + private->left_spacing = left_spacing; + private->right_spacing = right_spacing; struct module *module = module_common_new(); module->private = private; @@ -987,13 +991,25 @@ static struct module * from_conf(struct yml_node const *node, struct conf_inherit inherited) { struct yml_node const *content = yml_get_value(node, "content"); - return pipewire_new(conf_to_particle(content, inherited)); + struct yml_node const *spacing = yml_get_value(node, "spacing"); + struct yml_node const *left_spacing = yml_get_value(node, "left-spacing"); + struct yml_node const *right_spacing = yml_get_value(node, "right-spacing"); + + int left = spacing != NULL ? yml_value_as_int(spacing) : left_spacing != NULL ? yml_value_as_int(left_spacing) : 0; + int right = spacing != NULL ? yml_value_as_int(spacing) + : right_spacing != NULL ? yml_value_as_int(right_spacing) + : 0; + + return pipewire_new(conf_to_particle(content, inherited), left, right); } static bool verify_conf(keychain_t *keychain, struct yml_node const *node) { static struct attr_info const attrs[] = { + {"spacing", false, &conf_verify_unsigned}, + {"left-spacing", false, &conf_verify_unsigned}, + {"right-spacing", false, &conf_verify_unsigned}, MODULE_COMMON_ATTRS, }; return conf_verify_dict(keychain, node, attrs); From 2eb1cda73337544bb0ff5782e3d43edf39371b9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 5 Mar 2025 08:19:29 +0100 Subject: [PATCH 594/611] changelog: pipewire: spacing attributes --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 802c82b..20f637a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,8 @@ * Added "single" mode flag to the `mpd` module ([#428][428]). * niri: add a new module for niri-workspaces and niri-language ([#404][404]). +* pipewire: added `spacing`, `left-spacing` and `right-spacing` + attributes. [96]: https://codeberg.org/dnkl/yambar/issues/96 [380]: https://codeberg.org/dnkl/yambar/issues/380 From a242d3d56975189136a98a6feec8fde6728430e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 5 Mar 2025 08:21:03 +0100 Subject: [PATCH 595/611] ci: sr.ht: skip codespell (this is done in woodpecker) --- .builds/alpine-x64.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.builds/alpine-x64.yml b/.builds/alpine-x64.yml index e4ad80c..1703a3d 100644 --- a/.builds/alpine-x64.yml +++ b/.builds/alpine-x64.yml @@ -37,10 +37,6 @@ sources: # to: tasks: - - codespell: | - pip install codespell - cd yambar - ~/.local/bin/codespell README.md CHANGELOG.md *.c *.h doc/*.scd - fcft: | cd yambar/subprojects git clone https://codeberg.org/dnkl/fcft.git From b486088f77b6f4813a89e38afde77573d1a804e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 5 Mar 2025 08:21:54 +0100 Subject: [PATCH 596/611] examples: codespell: re-using -> reusing --- examples/configurations/laptop.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/configurations/laptop.conf b/examples/configurations/laptop.conf index e57ebb3..1bdd16c 100644 --- a/examples/configurations/laptop.conf +++ b/examples/configurations/laptop.conf @@ -4,7 +4,7 @@ # For X11/i3, you'll want to replace calls to swaymsg with i3-msg, and # the sway-xkb module with the xkb module. -# fonts we'll be re-using here and there +# fonts we'll be reusing here and there awesome: &awesome Font Awesome 6 Free:style=solid:pixelsize=14 awesome_brands: &awesome_brands Font Awesome 6 Brands:pixelsize=16 From 5a515eae99116be895634f9f0a5e9498ae7bce35 Mon Sep 17 00:00:00 2001 From: Nicholas Sudsgaard Date: Tue, 4 Mar 2025 13:42:11 +0900 Subject: [PATCH 597/611] bar/wayland: Add unused attribute to count This variable is only used in LOG_DBG which expands to nothing by default. Adding the unused attribute prevents the compiler from throwing an error (-Wunused-but-set-variable) when building. --- bar/wayland.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bar/wayland.c b/bar/wayland.c index 3d8e4e0..86ab252 100644 --- a/bar/wayland.c +++ b/bar/wayland.c @@ -1223,7 +1223,7 @@ loop(struct bar *_bar, void (*expose)(const struct bar *bar), bool do_expose = false; /* Coalesce “refresh” commands */ - size_t count = 0; + __attribute__((unused)) size_t count = 0; while (true) { uint8_t command; ssize_t r = read(backend->pipe_fds[0], &command, sizeof(command)); From b5450c3918d8a0c2c9ab6384de32933e7b5a4534 Mon Sep 17 00:00:00 2001 From: Nicholas Sudsgaard Date: Tue, 4 Mar 2025 14:46:08 +0900 Subject: [PATCH 598/611] modules/i3: Add unused attribute to focused This variable is only used in assert() which expands to nothing in release builds. Adding the unused attribute prevents the compiler from throwing an error (-Wunused-but-set-variable) when building. --- modules/i3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/i3.c b/modules/i3.c index 47f6d99..cbdafaf 100644 --- a/modules/i3.c +++ b/modules/i3.c @@ -594,7 +594,7 @@ handle_window_event(int sock, int type, const struct json_object *json, void *_m mtx_lock(&mod->lock); struct workspace *ws = NULL; - size_t focused = 0; + __attribute__((unused)) size_t focused = 0; tll_foreach(m->workspaces, it) { if (it->item.focused) { From c27de56beab0a4e6239894599138c931e4a8d4a6 Mon Sep 17 00:00:00 2001 From: haruInDisguise Date: Fri, 5 Jul 2024 23:22:20 +0200 Subject: [PATCH 599/611] Added 'MPRIS' module This commit adds the ability to display status information for MPRIS compatible music players. Closes #53 --- CHANGELOG.md | 2 + doc/meson.build | 3 + doc/yambar-modules-mpris.5.scd | 95 +++ meson.build | 10 +- meson_options.txt | 2 + modules/dbus.h | 13 + modules/meson.build | 7 + modules/mpris.c | 1129 ++++++++++++++++++++++++++++++++ plugin.c | 6 + 9 files changed, 1266 insertions(+), 1 deletion(-) create mode 100644 doc/yambar-modules-mpris.5.scd create mode 100644 modules/dbus.h create mode 100644 modules/mpris.c diff --git a/CHANGELOG.md b/CHANGELOG.md index 20f637a..0e0e772 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,7 @@ ([#404][404]). * pipewire: added `spacing`, `left-spacing` and `right-spacing` attributes. +* mpris: new module ([#53][53]). [96]: https://codeberg.org/dnkl/yambar/issues/96 [380]: https://codeberg.org/dnkl/yambar/issues/380 @@ -38,6 +39,7 @@ [400]: https://codeberg.org/dnkl/yambar/pulls/400 [428]: https://codeberg.org/dnkl/yambar/pulls/428 [404]: https://codeberg.org/dnkl/yambar/issues/404 +[53]: https://codeberg.org/dnkl/yambar/issues/53 ### Changed diff --git a/doc/meson.build b/doc/meson.build index 90a83ec..e801bf1 100644 --- a/doc/meson.build +++ b/doc/meson.build @@ -34,6 +34,9 @@ endif if plugin_mpd_enabled plugin_pages += ['yambar-modules-mpd.5.scd'] endif +if plugin_mpris_enabled + plugin_pages += ['yambar-modules-mpris.5.scd'] +endif if plugin_i3_enabled plugin_pages += ['yambar-modules-i3.5.scd'] plugin_pages += ['yambar-modules-sway.5.scd'] diff --git a/doc/yambar-modules-mpris.5.scd b/doc/yambar-modules-mpris.5.scd new file mode 100644 index 0000000..a52caba --- /dev/null +++ b/doc/yambar-modules-mpris.5.scd @@ -0,0 +1,95 @@ +yambar-modules-mpris(5) + +# NAME +mpris - This module provides MPRIS status such as currently playing artist/album/song + +# TAGS + +[[ *Name* +:[ *Type* +:< *Description* +| state +: string +: One of *offline*, *stopped*, *paused* or *playing* +| shuffle +: bool +: True if the *shuffle* flag is set +| repeat +: string +: One of *none*, *track* or *paylist* +| volume +: range +: Volume in percentage +| album +: string +: Currently playing album +| artist +: string +: Artist of currently playing song +| title +: string +: Title of currently playing song +| file +: string +: Filename or URL of currently playing song +| pos +: string +: *%M:%S*-formatted string describing the song's current position + (also see _elapsed_) +| end +: string +: *%M:%S*-formatted string describing the song's total length (also + see _duration_) +| elapsed +: realtime +: Position in currently playing song, in milliseconds. Can be used + with a _progress-bar_ particle. + +# CONFIGURATION + +[[ *Name* +:[ *Type* +:[ *Req* +:< *Description* +| identities +: list of string +: yes +: A list of MPRIS client identities + +# EXAMPLES + +``` +bar: + center: + - mpris: + identities: + - "spotify" + - "firefox" + content: + map: + conditions: + state != offline && state != stopped: + - string: {text: "{artist}", max: 30 } + - string: {text: "-" } + - string: {text: "{title}", max: 30 } +``` + +# NOTE + +The 'identity' refers a part of your clients DBus bus name. +You can obtain a list of available bus names using: + +``` +Systemd: > busctl --user --list +Playerctl: > playerctl --list-all +Libdbus: > dbus-send --session --print-reply --type=method_call --dest='org.freedesktop.DBus' /org org.freedesktop.DBus.ListNames ... | grep 'org.mpris.MediaPlayer2' +``` + +The identity refers to the part after 'org.mpris.MediaPlayer2'. +For example, firefox may use the bus name +'org.mpris.MediaPlayer2.firefox.instance_1_7' and its identity would be +'firefox' + +# SEE ALSO + +*yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5) diff --git a/meson.build b/meson.build index 81af577..1a6d211 100644 --- a/meson.build +++ b/meson.build @@ -4,13 +4,15 @@ project('yambar', 'c', meson_version: '>=0.59.0', default_options: ['c_std=c18', 'warning_level=1', - 'werror=true', 'b_ndebug=if-release']) is_debug_build = get_option('buildtype').startswith('debug') plugs_as_libs = get_option('core-plugins-as-shared-libraries') cc = meson.get_compiler('c') +cc_flags = [ + '-Werror=all' + ] if cc.has_function('memfd_create', args: ['-D_GNU_SOURCE=200809L'], @@ -75,7 +77,12 @@ backend_wayland = wayland_client.found() and wayland_cursor.found() tllist = dependency('tllist', version: '>=1.0.1', fallback: 'tllist') fcft = dependency('fcft', version: ['>=3.0.0', '<4.0.0'], fallback: 'fcft') +# DBus dependency. Used by 'modules/mpris' +sdbus_library = dependency('libsystemd', 'libelogind', 'basu', required: get_option('plugin-mpris')) +sdbus = declare_dependency(compile_args: ['-DHAVE_' + sdbus_library.name().to_upper()], dependencies:[sdbus_library]) + add_project_arguments( + cc_flags + ['-D_GNU_SOURCE'] + (is_debug_build ? ['-D_DEBUG'] : []) + (backend_x11 ? ['-DENABLE_X11'] : []) + @@ -180,6 +187,7 @@ summary( 'Foreign toplevel (window tracking for Wayland)': plugin_foreign_toplevel_enabled, 'Memory monitoring': plugin_mem_enabled, 'Music Player Daemon (MPD)': plugin_mpd_enabled, + 'Media Player Remote Interface Specificaion (MPRIS)': plugin_mpris_enabled, 'i3+Sway': plugin_i3_enabled, 'Label': plugin_label_enabled, 'Network monitoring': plugin_network_enabled, diff --git a/meson_options.txt b/meson_options.txt index 9fd0dd5..23a8e11 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -26,6 +26,8 @@ option('plugin-mem', type: 'feature', value: 'auto', description: 'Memory monitoring support') option('plugin-mpd', type: 'feature', value: 'auto', description: 'Music Player Daemon (MPD) support') +option('plugin-mpris', type: 'feature', value: 'enabled', + description: 'Media Player Remote Interface Specificaion (MPRIS) support') option('plugin-i3', type: 'feature', value: 'auto', description: 'i3+Sway support') option('plugin-label', type: 'feature', value: 'auto', diff --git a/modules/dbus.h b/modules/dbus.h new file mode 100644 index 0000000..6517cef --- /dev/null +++ b/modules/dbus.h @@ -0,0 +1,13 @@ +#pragma once + +// This header provides an generic interface for different versions of +// systemd-sdbus. + +#if defined(HAVE_LIBSYSTEMD) +#include +#elif defined(HAVE_LIBELOGIND) +#include +#elif defined(HAVE_BASU) +#include +#endif + diff --git a/modules/meson.build b/modules/meson.build index b54e9d7..0e65812 100644 --- a/modules/meson.build +++ b/modules/meson.build @@ -22,6 +22,9 @@ plugin_mem_enabled = get_option('plugin-mem').allowed() mpd = dependency('libmpdclient', required: get_option('plugin-mpd')) plugin_mpd_enabled = mpd.found() +mpris = sdbus +plugin_mpris_enabled = sdbus.found() + json_i3 = dependency('json-c', required: get_option('plugin-i3')) plugin_i3_enabled = json_i3.found() @@ -95,6 +98,10 @@ if plugin_mpd_enabled mod_data += {'mpd': [[], [mpd]]} endif +if plugin_mpris_enabled + mod_data += {'mpris': [[], [mpris]]} +endif + if plugin_i3_enabled mod_data += {'i3': [['i3-common.c', 'i3-common.h'], [dynlist, json_i3]]} endif diff --git a/modules/mpris.c b/modules/mpris.c new file mode 100644 index 0000000..4cf99ef --- /dev/null +++ b/modules/mpris.c @@ -0,0 +1,1129 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "dbus.h" + +#define LOG_MODULE "mpris" +#define LOG_ENABLE_DBG 1 +#include "../bar/bar.h" +#include "../config-verify.h" +#include "../config.h" +#include "../log.h" +#include "../plugin.h" + +#define QUERY_TIMEOUT 100 + +#define PATH "/org/mpris/MediaPlayer2" +#define BUS_NAME "org.mpris.MediaPlayer2" +#define SERVICE "org.mpris.MediaPlayer2" +#define INTERFACE_ROOT "org.mpris.MediaPlayer2" +#define INTERFACE_PLAYER INTERFACE_ROOT ".Player" + +#define DBUS_PATH "/org/freedesktop/DBus" +#define DBUS_BUS_NAME "org.freedesktop.DBus" +#define DBUS_SERVICE "org.freedesktop.DBus" +#define DBUS_INTERFACE_MONITORING "org.freedesktop.DBus.Monitoring" +#define DBUS_INTERFACE_PROPERTIES "org.freedesktop.DBus.Properties" + +enum status { + STATUS_OFFLINE, + STATUS_PLAYING, + STATUS_PAUSED, + STATUS_STOPPED, + STATUS_ERROR, +}; + +typedef tll(char *) string_array; + +struct metadata { + uint64_t length_us; + char *trackid; + string_array artists; + char *album; + char *title; +}; + +struct property { + struct metadata metadata; + char *playback_status; + char *loop_status; + uint64_t position_us; + double rate; + double volume; + bool shuffle; +}; + +struct client { + bool has_seeked_support; + enum status status; + const char *bus_name; + const char *bus_unique_name; + + struct property property; + + /* The unix timestamp of the last position change (ie. + * seeking, pausing) */ + struct timespec seeked_when; +}; + +struct context { + sd_bus *monitor_connection; + sd_bus_message *update_message; + + /* FIXME: There is no nice way to pass the desired identities to + * the event handler for validation. */ + char **identities_ref; + size_t identities_count; + + tll(struct client *) clients; + struct client *current_client; + + bool has_update; +}; + +struct private +{ + thrd_t refresh_thread_id; + int refresh_abort_fd; + + size_t identities_count; + const char **identities; + struct particle *label; + + struct context context; +}; + +#if 0 +static void +debug_print_argument_type(sd_bus_message *message) +{ + char type; + const char *content; + sd_bus_message_peek_type(message, &type, &content); + LOG_DBG("peek_message_type: %c -> %s", type, content); +} +#endif + +#if defined(LOG_ENABLE_DBG) +#define dump_type(message) \ + { \ + char type; \ + const char *content; \ + sd_bus_message_peek_type(message, &type, &content); \ + LOG_DBG("argument layout: %c -> %s", type, content); \ + } +#endif + +static void +metadata_clear(struct metadata *metadata) +{ + tll_free_and_free(metadata->artists, free); + + if (metadata->album != NULL) { + free(metadata->album); + } + + if (metadata->title != NULL) { + free(metadata->title); + } + + if (metadata->trackid != NULL) { + free(metadata->trackid); + } +} + +static void +property_clear(struct property *property) +{ + metadata_clear(&property->metadata); + memset(property, 0, sizeof(*property)); +} + +static void +client_free(struct client *client) +{ + property_clear(&client->property); + + free((void *)client->bus_name); + free((void *)client->bus_unique_name); + free(client); +} + +static void +clients_free_by_unique_name(struct context *context, const char *unique_name) +{ + tll_foreach(context->clients, it) + { + struct client *client = it->item; + if (strcmp(client->bus_unique_name, unique_name) == 0) { + LOG_DBG("client_remove: Removing client %s", client->bus_name); + client_free(client); + tll_remove(context->clients, it); + } + } +} + +static void +client_free_all(struct context *context) +{ + tll_free_and_free(context->clients, client_free); +} + +static void +client_add(struct context *context, const char *name, const char *unique_name) +{ + struct client *client = malloc(sizeof(*client)); + (*client) = (struct client){ + .bus_name = strdup(name), + .bus_unique_name = strdup(unique_name), + }; + + tll_push_back(context->clients, client); + LOG_DBG("client_add: name='%s' unique_name='%s'", name, unique_name); +} + +static struct client * +client_lookup_by_unique_name(struct context *context, const char *unique_name) +{ + tll_foreach(context->clients, it) + { + struct client *client = it->item; + if (strcmp(client->bus_unique_name, unique_name) == 0) { + LOG_DBG("client_lookup: name: %s", client->bus_name); + return client; + } + } + + return NULL; +} + +static void +client_change_unique_name(struct client *client, const char *new_name) +{ + if (client->bus_unique_name != NULL) { + free((void *)client->bus_unique_name); + } + + client->bus_unique_name = strdup(new_name); +} + +static bool +verify_bus_name(char **idents, const size_t ident_count, const char *name) +{ + for (size_t i = 0; i < ident_count; i++) { + const char *ident = idents[i]; + + if (strlen(name) < strlen(BUS_NAME ".") + strlen(ident)) { + continue; + } + + const char *cmp = name + strlen(BUS_NAME "."); + if (strncmp(cmp, ident, strlen(ident)) != 0) { + continue; + } + + return true; + } + + return false; +} + +static bool +read_string_array(sd_bus_message *message, string_array *list) +{ + int status = 0; + + /* message argument layout: 'vas' */ + /* enter variant */ + status = sd_bus_message_enter_container(message, SD_BUS_TYPE_VARIANT, "as"); + if (status <= 0) { + LOG_DBG("unexpected layout: errno=%d (%s)", status, strerror(-status)); + return false; + } + + /* enter array */ + status = sd_bus_message_enter_container(message, SD_BUS_TYPE_ARRAY, "s"); + assert(status >= 0); + + const char *string; + while ((status = sd_bus_message_read_basic(message, SD_BUS_TYPE_STRING, &string)) > 0) { + if (strlen(string) > 0) { + tll_push_back(*list, strdup(string)); + } + } + + if (status < 0) { + LOG_ERR("metadata: unexpected layout: errno=%d (%s)", status, strerror(-status)); + return false; + } + + /* close array */ + sd_bus_message_exit_container(message); + /* close variant */ + sd_bus_message_exit_container(message); + + return true; +} + +static bool +metadata_parse_property(const char *property_name, sd_bus_message *message, struct metadata *buffer) +{ + int status = 0; + const char *string = NULL; + + char argument_type = 0; + const char *argument_layout = NULL; + sd_bus_message_peek_type(message, &argument_type, &argument_layout); + assert(argument_type == SD_BUS_TYPE_VARIANT); + assert(argument_layout != NULL && strlen(argument_layout) > 0); + + if (strcmp(property_name, "mpris:trackid") == 0) { + if (argument_layout[0] != SD_BUS_TYPE_STRING && argument_layout[0] != SD_BUS_TYPE_OBJECT_PATH) + goto unexpected_type; + + status = sd_bus_message_read(message, "v", argument_layout, &string); + if (status > 0) + buffer->trackid = strdup(string); + + /* FIXME: "strcmp matches both 'album' as well as 'albumArtist'" */ + } else if (strcmp(property_name, "xesam:album") == 0) { + status = sd_bus_message_read(message, "v", argument_layout, &string); + if (status > 0 && strlen(string) > 0) + buffer->album = strdup(string); + + } else if (strcmp(property_name, "xesam:artist") == 0) { + status = read_string_array(message, &buffer->artists); + + } else if (strcmp(property_name, "xesam:title") == 0) { + status = sd_bus_message_read(message, "v", "s", &string); + if(status > 0) + buffer->title = strdup(string); + + } else if (strcmp(property_name, "mpris:length") == 0) { + /* MPRIS requires 'mpris:length' to be an i64 (the wording is a bit ambiguous), however some client + * use a u64 instead. */ + if (argument_layout[0] != SD_BUS_TYPE_INT64 && argument_layout[0] != SD_BUS_TYPE_UINT64) + goto unexpected_type; + + status = sd_bus_message_read(message, "v", argument_layout, &buffer->length_us); + + } else { + LOG_DBG("metadata: ignoring property: %s", property_name); + sd_bus_message_skip(message, NULL); + return true; + } + + if (status < 0) { + LOG_ERR("metadata: failed to read property: arg_type='%c' arg_layout='%s' errno=%d (%s)", argument_type, + argument_layout, status, strerror(-status)); + return false; + } + + return true; +unexpected_type: + LOG_ERR("metadata: unexpected type for '%s'", property_name); + return false; +} + +static bool +metadata_parse_array(struct metadata *metadata, sd_bus_message *message) +{ + int status = sd_bus_message_enter_container(message, SD_BUS_TYPE_VARIANT, "a{sv}"); + if (status <= 0) { + LOG_DBG("unexpected layout: errno=%d (%s)", status, strerror(-status)); + return false; + } + status = sd_bus_message_enter_container(message, SD_BUS_TYPE_ARRAY, "{sv}"); + assert(status >= 0); + + while ((status = sd_bus_message_enter_container(message, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) { + const char *property_name = NULL; + status = sd_bus_message_read_basic(message, SD_BUS_TYPE_STRING, &property_name); + if (status <= 0) { + LOG_DBG("unexpected layout: errno=%d (%s)", status, strerror(-status)); + return false; + } + + status = metadata_parse_property(property_name, message, metadata); + if (status == 0) { + return false; + } + + status = sd_bus_message_exit_container(message); + assert(status >= 0); + } + + /* close array */ + sd_bus_message_exit_container(message); + /* close variant */ + sd_bus_message_exit_container(message); + + return status >= 0; +} + +static bool +property_parse(struct property *prop, const char *property_name, sd_bus_message *message) +{ + /* This function is called in two different ways: + * 1. update_status(): The property is passed directly + * 2. update_status_from_message(): The property is passed wrapped + * inside a variant and has to be unpacked */ + const char *argument_layout = NULL; + char argument_type = 0; + int status = sd_bus_message_peek_type(message, &argument_type, &argument_layout); + + assert(status > 0); + assert(argument_type == SD_BUS_TYPE_VARIANT); + assert(argument_layout != NULL && strlen(argument_layout) > 0); + + const char *string; + if (strcmp(property_name, "PlaybackStatus") == 0) { + status = sd_bus_message_read(message, "v", "s", &string); + if (status) + prop->playback_status = strdup(string); + + } else if (strcmp(property_name, "LoopStatus") == 0) { + status = sd_bus_message_read(message, "v", "s", &string); + if (status) + prop->loop_status = strdup(string); + + } else if (strcmp(property_name, "Position") == 0) { + /* MPRIS requires 'Position' to be a i64, however some client + * use a u64 instead. */ + if (argument_layout[0] != SD_BUS_TYPE_INT64 && argument_layout[0] != SD_BUS_TYPE_UINT64) { + LOG_ERR("property: unexpected type for '%s'", property_name); + return false; + } + status = sd_bus_message_read(message, "v", argument_layout[0], &prop->position_us); + + } else if (strcmp(property_name, "Shuffle") == 0) { + status = sd_bus_message_read(message, "v", "b", &prop->shuffle); + + } else if (strcmp(property_name, "Metadata") == 0) { + metadata_clear(&prop->metadata); + status = metadata_parse_array(&prop->metadata, message); + + } else { + LOG_DBG("property: ignoring property: %s", property_name); + sd_bus_message_skip(message, NULL); + return true; + } + + return status > 0; +} + +/* ------------- */ + +static void +format_usec_timestamp(unsigned usec, char *s, size_t sz) +{ + uint32_t secs = usec / 1000 / 1000; + uint32_t hours = secs / (60 * 60); + uint32_t minutes = secs % (60 * 60) / 60; + secs %= 60; + + if (hours > 0) + snprintf(s, sz, "%02u:%02u:%02u", hours, minutes, secs); + else + snprintf(s, sz, "%02u:%02u", minutes, secs); +} + +static void +destroy(struct module *mod) +{ + struct private *m = mod->private; + struct context *context = &m->context; + + client_free_all(context); + + sd_bus_close(context->monitor_connection); + + module_default_destroy(mod); + m->label->destroy(m->label); + free(m); +} + +static void +context_event_handle_name_owner_changed(sd_bus_message *message, struct context *context) +{ + /* NameOwnerChanged (STRING name, STRING old_owner, STRING new_owner) */ + /* This signal indicates that the owner of a name has changed, ie. + * it was acquired, lost or changed */ + + const char *bus_name = NULL, *old_owner = NULL, *new_owner = NULL; + int status = sd_bus_message_read(message, "sss", &bus_name, &old_owner, &new_owner); + assert(status > 0); + +#if 1 + LOG_DBG("event_handler: 'NameOwnerChanged': bus_name: '%s' old_owner: '%s' new_ower: '%s'", bus_name, old_owner, + new_owner); +#endif + + if (strlen(new_owner) == 0 && strlen(old_owner) > 0) { + /* Target bus has been lost */ + struct client *client = client_lookup_by_unique_name(context, old_owner); + + if (client == NULL) + return; + + LOG_DBG("event_handler: 'NameOwnerChanged': Target bus disappeared: %s", client->bus_name); + clients_free_by_unique_name(context, client->bus_unique_name); + + if (context->current_client == client) + context->current_client = NULL; + + return; + } else if (strlen(old_owner) == 0 && strlen(new_owner) > 0) { + /* New unique name registered. Not used */ + return; + } + + /* Name changed */ + assert(new_owner != NULL && strlen(new_owner) > 0); + assert(old_owner != NULL && strlen(old_owner) > 0); + + struct client *client = client_lookup_by_unique_name(context, old_owner); + LOG_DBG("'NameOwnerChanged': Name changed from '%s' to '%s' for client '%s'", old_owner, new_owner, + client->bus_name); + client_change_unique_name(client, new_owner); +} + +static void +context_event_handle_name_acquired(sd_bus_message *message, struct context *context) +{ + /* Spy on applications that requested an "MPRIS style" bus name */ + + /* NameAcquired (STRING name) */ + /* " This signal is sent to a specific application when it gains ownership of a name. " */ + const char *name = NULL; + int status = sd_bus_message_read_basic(message, SD_BUS_TYPE_STRING, &name); + assert(status > 0); + + /*LOG_DBG("event_handler: 'NameAcquired': name: '%s'", name);*/ + + if (strncmp(name, BUS_NAME, strlen(BUS_NAME)) != 0) { + return; + } + + if (verify_bus_name(context->identities_ref, context->identities_count, name)) { + const char *unique_name = sd_bus_message_get_destination(message); + LOG_DBG("'NameAcquired': Acquired new client: %s unique: %s", name, unique_name); + client_add(context, name, unique_name); + } +} + +static int +context_event_handler(sd_bus_message *message, void *userdata, sd_bus_error *ret_error) +{ + struct context *context = userdata; + + const char *member = sd_bus_message_get_member(message); + const char *sender = sd_bus_message_get_sender(message); + const char *path_name = sd_bus_message_get_path(message); + +#if 0 + const char *destination = sd_bus_message_get_destination(message); + const char *self = sd_bus_message_get_sender(message); + LOG_DBG("member: '%s' self: '%s' dest: '%s' sender: '%s'", member, self, + destination, sender); +#endif + + if (tll_length(context->clients) == 0 && strcmp(member, "NameAcquired") != 0) { + return 1; + } + + /* TODO: Allow multiple clients to connect */ + if (strcmp(path_name, DBUS_PATH) == 0 && strcmp(member, "NameAcquired") == 0) { + context_event_handle_name_acquired(message, context); + } + + if (strcmp(path_name, DBUS_PATH) == 0 && strcmp(member, "NameOwnerChanged") == 0) { + context_event_handle_name_owner_changed(message, context); + return 1; + } + + /* Copy the 'PropertiesChanged/Seeked' message, so it can be parsed + * later on */ + if (strcmp(path_name, PATH) == 0 && (strcmp(member, "PropertiesChanged") == 0 || strcmp(member, "Seeked") == 0)) { + struct client *client = client_lookup_by_unique_name(context, sender); + if (client == NULL) + return 1; + + LOG_DBG("event_handler: '%s': name: '%s' unique_name: '%s'", member, client->bus_name, client->bus_unique_name); + + context->has_update = true; + context->current_client = client; + context->update_message = sd_bus_message_ref(message); + + assert(context->update_message != NULL); + } + + return 1; +} + +static bool +context_process_events(struct context *context, uint32_t timeout_ms) +{ + int status = -1; + + status = sd_bus_wait(context->monitor_connection, timeout_ms); + if (status < 0) { + if (status == -ENOTCONN) + LOG_DBG("Disconnect signal has been processed"); + else + LOG_ERR("Failed to query monitor connection: errno=%d", status); + + return false; + } + + /* 'sd_bus_process' processes one 'action' per call. + * This includes: connection, authentication, message processing */ + status = sd_bus_process(context->monitor_connection, NULL); + + if (status < 0) { + if (status == -ENOTCONN) + LOG_DBG("Disconnect signal has been processed"); + else + LOG_ERR("Failed to query monitor connection: errno=%d", status); + + return false; + } + + return true; +} + +static bool +context_new(struct private *m, struct context *context) +{ + int status = true; + sd_bus *connection; + if ((status = sd_bus_default_user(&connection)) < 0) { + LOG_ERR("Failed to connect to the desktop bus. errno: %d", status); + return -1; + } + + /* Turn this connection into a monitor */ + sd_bus_message *message; + status = sd_bus_message_new_method_call(connection, &message, DBUS_SERVICE, DBUS_PATH, DBUS_INTERFACE_MONITORING, + "BecomeMonitor"); + + const char *matching_rules[] = { + /* Listen for... */ + /* ... new MPRIS clients */ + "type='signal',interface='org.freedesktop.DBus',member='NameAcquired',path='/org/freedesktop/" + "DBus',arg0namespace='org.mpris.MediaPlayer2'", + /* ... name changes */ + "type='signal',interface='org.freedesktop.DBus',member='NameOwnerChanged'," + "path='/org/freedesktop/DBus'", + /* ... property changes */ + "type='signal',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged', " + "path='/org/mpris/MediaPlayer2'", + /* ... changes in playback position */ + "type='signal',interface='org.mpris.MediaPlayer2.Player',member='Seeked', " + "path='/org/mpris/MediaPlayer2'", + }; + + /* TODO: Error handling */ + /* "BecomeMonitor" ('asu'): (Rules: String[], Flags: UINT32) */ + /* https://dbus.freedesktop.org/doc/dbus-specification.html#bus-messages-become-monitor */ + status = sd_bus_message_open_container(message, SD_BUS_TYPE_ARRAY, "s"); + for (uint32_t i = 0; i < sizeof(matching_rules) / sizeof(matching_rules[0]); i++) { + status = sd_bus_message_append(message, "s", matching_rules[i]); + } + status = sd_bus_message_close_container(message); + status = sd_bus_message_append_basic(message, SD_BUS_TYPE_UINT32, &(uint32_t){0}); + + sd_bus_message *reply = NULL; + sd_bus_error error = {}; + status = sd_bus_call(NULL, message, QUERY_TIMEOUT, &error, &reply); + + if (status < 0 && sd_bus_error_is_set(&error)) { + LOG_ERR("context_new: got error response with error: %s: %s (%d)", error.name, error.message, + sd_bus_error_get_errno(&error)); + return false; + } + + sd_bus_message_unref(message); + sd_bus_message_unref(reply); + + (*context) = (struct context){ + .monitor_connection = connection, + .identities_ref = (char **)m->identities, + .identities_count = m->identities_count, + .clients = tll_init(), + }; + + sd_bus_add_filter(connection, NULL, context_event_handler, context); + + return status >= 0; +} + +static uint64_t +timespec_diff_us(const struct timespec *a, const struct timespec *b) +{ + uint64_t nsecs_a = a->tv_sec * 1000000000 + a->tv_nsec; + uint64_t nsecs_b = b->tv_sec * 1000000000 + b->tv_nsec; + + assert(nsecs_a >= nsecs_b); + uint64_t nsec_diff = nsecs_a - nsecs_b; + return nsec_diff / 1000; +} + +static bool +update_status_from_message(struct module *mod, sd_bus_message *message) +{ + struct private *m = mod->private; + mtx_lock(&mod->lock); + + struct client *client = m->context.current_client; + int status = 1; + + /* Player.Seeked (UINT64 position)*/ + if (strcmp(sd_bus_message_get_member(message), "Seeked") == 0) { + client->has_seeked_support = true; + + status = sd_bus_message_read_basic(message, SD_BUS_TYPE_INT64, &client->property.position_us); + if (status <= 0) + return status; + + clock_gettime(CLOCK_MONOTONIC, &client->seeked_when); + return true; + } + + /* Properties.PropertiesChanged (STRING interface_name, + * ARRAY of DICT_ENTRY changed_properties, + * ARRAY invalidated_properties); */ + assert(strcmp(sd_bus_message_get_member(message), "PropertiesChanged") == 0); + assert(strcmp(sd_bus_message_get_signature(message, 1), "sa{sv}as") == 0); + + /* argument: 'interface_name' layout: 's' */ + const char *interface_name = NULL; + sd_bus_message_read_basic(message, SD_BUS_TYPE_STRING, &interface_name); + + if (strcmp(interface_name, INTERFACE_PLAYER) != 0) { + LOG_DBG("Ignoring interface: %s", interface_name); + mtx_unlock(&mod->lock); + return true; + } + + /* argument: 'changed_properties' layout: 'a{sv}' */ + + /* Make sure we reset the position on metadata change unless the + * update contains its own position value */ + bool should_reset_position = true; + bool has_entries = sd_bus_message_enter_container(message, SD_BUS_TYPE_ARRAY, "{sv}"); + + while ((has_entries = sd_bus_message_enter_container(message, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) { + const char *property_name = NULL; + int status = sd_bus_message_read_basic(message, SD_BUS_TYPE_STRING, &property_name); + assert(status > 0); + + if (!property_parse(&client->property, property_name, message)) { + return false; + } + + status = sd_bus_message_exit_container(message); + assert(status >= 0); + + if (strcmp(property_name, "PlaybackStatus") == 0) { + if (strcmp(client->property.playback_status, "Stopped") == 0) { + client->status = STATUS_STOPPED; + + } else if (strcmp(client->property.playback_status, "Playing") == 0) { + clock_gettime(CLOCK_MONOTONIC, &client->seeked_when); + client->status = STATUS_PLAYING; + + } else if (strcmp(client->property.playback_status, "Paused") == 0) { + /* Update our position to include the elapsed time */ + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + client->status = STATUS_PAUSED; + client->property.position_us += timespec_diff_us(&now, &client->seeked_when); + } + } + + /* Make sure to reset the position upon metadata/song changes */ + if (should_reset_position && strcmp(property_name, "Metadata") == 0) { + client->property.position_us = 0; + + if (client->property.playback_status == NULL) { + client->property.playback_status = "Paused"; + client->status = STATUS_PAUSED; + } + } + + if (strcmp(property_name, "Position") == 0) { + should_reset_position = false; + } + } + + status = sd_bus_message_exit_container(message); + assert(status > 0); + + mtx_unlock(&mod->lock); + return true; +} + +static struct exposable * +content_empty(struct module *mod) +{ + struct private *m = mod->private; + + struct tag_set tags = { + .tags = (struct tag *[]){ + tag_new_bool(mod, "has-seeked-support", "false"), + tag_new_string(mod, "state", "offline"), + tag_new_bool(mod, "shuffle", "false"), + tag_new_string(mod, "loop", "None"), + tag_new_int_range(mod, "volume", 0, 0, 100), + tag_new_string(mod, "album", ""), + tag_new_string(mod, "artist", ""), + tag_new_string(mod, "title", ""), + tag_new_string(mod, "pos", ""), + tag_new_string(mod, "end", ""), + tag_new_int_realtime( + mod, "elapsed", 0, 0, 0, TAG_REALTIME_NONE), + }, + .count = 10, + }; + + mtx_unlock(&mod->lock); + + struct exposable *exposable = m->label->instantiate(m->label, &tags); + + tag_set_destroy(&tags); + return exposable; +} + +static struct exposable * +content(struct module *mod) +{ + const struct private *m = mod->private; + const struct client *client = m->context.current_client; + + if (client == NULL) { + return content_empty(mod); + } + + const struct metadata *metadata = &client->property.metadata; + const struct property *property = &client->property; + + /* Calculate the current playback position */ + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + + uint64_t elapsed_us = client->property.position_us; + uint64_t length_us = metadata->length_us; + + if (client->has_seeked_support && client->status == STATUS_PLAYING) { + elapsed_us += timespec_diff_us(&now, &client->seeked_when); + if (elapsed_us > length_us) { + LOG_DBG("dynamic update of elapsed overflowed: " + "elapsed=%" PRIu64 ", duration=%" PRIu64, + elapsed_us, length_us); + elapsed_us = length_us; + } + } + + /* Some clients can report misleading or incomplete updates to the + * playback position, potentially causing the position to exceed + * the length */ + if (elapsed_us > length_us) + elapsed_us = length_us = 0; + + char tag_pos_value[16] = {0}, tag_end_value[16] = {0}; + if (length_us > 0) { + format_usec_timestamp(elapsed_us, tag_pos_value, sizeof(tag_pos_value)); + format_usec_timestamp(length_us, tag_end_value, sizeof(tag_end_value)); + } + + char *tag_state_value = NULL; + switch (client->status) { + case STATUS_ERROR: + tag_state_value = "error"; + break; + case STATUS_OFFLINE: + tag_state_value = "offline"; + break; + case STATUS_PLAYING: + tag_state_value = "playing"; + break; + case STATUS_PAUSED: + tag_state_value = "paused"; + break; + case STATUS_STOPPED: + tag_state_value = "stopped"; + break; + } + + const char *tag_loop_value = (property->loop_status == NULL) ? "" : property->loop_status; + const char *tag_album_value = (metadata->album == NULL) ? "" : metadata->album; + const char *tag_artists_value = (tll_length(metadata->artists) <= 0) ? "" : tll_front(metadata->artists); + const char *tag_title_value = (metadata->title == NULL) ? "" : metadata->title; + const uint32_t tag_volume_value = (property->volume >= 0.995) ? 100 : 100 * property->volume; + const bool tag_shuffle_value = property->shuffle; + const enum tag_realtime_unit realtime_unit + = (client->has_seeked_support && client->status == STATUS_PLAYING) ? TAG_REALTIME_MSECS : TAG_REALTIME_NONE; + + struct tag_set tags = { + .tags = (struct tag *[]){ + tag_new_bool(mod, "has_seeked_support", client->has_seeked_support), + tag_new_bool(mod, "shuffle", tag_shuffle_value), + tag_new_int_range(mod, "volume", tag_volume_value, 0, 100), + tag_new_string(mod, "album", tag_album_value), + tag_new_string(mod, "artist", tag_artists_value), + tag_new_string(mod, "end", tag_end_value), + tag_new_string(mod, "loop", tag_loop_value), + tag_new_string(mod, "pos", tag_pos_value), + tag_new_string(mod, "state", tag_state_value), + tag_new_string(mod, "title", tag_title_value), + tag_new_int_realtime( + mod, "elapsed", elapsed_us, 0, length_us, realtime_unit), + }, + .count = 11, + }; + + mtx_unlock(&mod->lock); + + struct exposable *exposable = m->label->instantiate(m->label, &tags); + + tag_set_destroy(&tags); + return exposable; +} + +struct refresh_context { + struct module *mod; + int abort_fd; + long milli_seconds; +}; + +static int +refresh_in_thread(void *arg) +{ + struct refresh_context *ctx = arg; + struct module *mod = ctx->mod; + + /* Extract data from context so that we can free it */ + int abort_fd = ctx->abort_fd; + long milli_seconds = ctx->milli_seconds; + free(ctx); + + /*LOG_DBG("going to sleep for %ldms", milli_seconds);*/ + + /* Wait for timeout, or abort signal */ + struct pollfd fds[] = {{.fd = abort_fd, .events = POLLIN}}; + int r = poll(fds, 1, milli_seconds); + + if (r < 0) { + LOG_ERRNO("failed to poll() in refresh thread"); + return 1; + } + + /* Aborted? */ + if (r == 1) { + assert(fds[0].revents & POLLIN); + /*LOG_DBG("refresh thread aborted");*/ + return 0; + } + + LOG_DBG("timed refresh"); + mod->bar->refresh(mod->bar); + + return 0; +} + +static bool +refresh_in(struct module *mod, long milli_seconds) +{ + struct private *m = mod->private; + + /* Abort currently running refresh thread */ + if (m->refresh_thread_id != 0) { + /*LOG_DBG("aborting current refresh thread");*/ + + /* Signal abort to thread */ + assert(m->refresh_abort_fd != -1); + if (write(m->refresh_abort_fd, &(uint64_t){1}, sizeof(uint64_t)) != sizeof(uint64_t)) { + LOG_ERRNO("failed to signal abort to refresher thread"); + return false; + } + + /* Wait for it to finish */ + int res; + thrd_join(m->refresh_thread_id, &res); + + /* Close and cleanup */ + close(m->refresh_abort_fd); + m->refresh_abort_fd = -1; + m->refresh_thread_id = 0; + } + + /* Create a new eventfd, to be able to signal abort to the thread */ + int abort_fd = eventfd(0, EFD_CLOEXEC); + if (abort_fd == -1) { + LOG_ERRNO("failed to create eventfd"); + return false; + } + + /* Thread context */ + struct refresh_context *ctx = malloc(sizeof(*ctx)); + ctx->mod = mod; + ctx->abort_fd = m->refresh_abort_fd = abort_fd; + ctx->milli_seconds = milli_seconds; + + /* Create thread */ + int r = thrd_create(&m->refresh_thread_id, &refresh_in_thread, ctx); + + if (r != thrd_success) { + LOG_ERR("failed to create refresh thread"); + close(m->refresh_abort_fd); + m->refresh_abort_fd = -1; + m->refresh_thread_id = 0; + free(ctx); + } + + /* Detach - we don't want to have to thrd_join() it */ + // thrd_detach(tid); + return r == 0; +} + +static int +run(struct module *mod) +{ + const struct bar *bar = mod->bar; + struct private *m = mod->private; + + if (!context_new(m, &m->context)) { + LOG_ERR("Failed to setup context"); + return -1; + } + + struct context *context = &m->context; + + int ret = 0; + bool aborted = false; + while (ret == 0 && !aborted) { + const uint32_t timeout_ms = 50; + struct pollfd fds[] = {{.fd = mod->abort_fd, .events = POLLIN}}; + + /* Check for abort event */ + if (poll(fds, 1, timeout_ms) < 0) { + if (errno == EINTR) + continue; + + LOG_ERRNO("failed to poll"); + break; + } + + if (fds[0].revents & POLLIN) { + aborted = true; + break; + } + + if (!context_process_events(context, QUERY_TIMEOUT)) { + aborted = true; + break; + } + + /* Process dynamic updates, received through the contexts + * monitor connection. The 'upate_message' attribute is set + * inside the contexts event callback, if there are any + * updates to be processed. */ + if (context->has_update) { + assert(context->current_client != NULL); + assert(context->update_message != NULL); + + context->has_update = false; + aborted = !update_status_from_message(mod, context->update_message); + context->update_message = sd_bus_message_unref(context->update_message); + } + + bar->refresh(bar); + } + + LOG_DBG("exiting"); + + return ret; +} + +static const char * +description(const struct module *mod) +{ + return "mpris"; +} + +static struct module * +mpris_new(const char **ident, size_t ident_count, struct particle *label) +{ + struct private *priv = calloc(1, sizeof(*priv)); + priv->label = label; + priv->identities = malloc(sizeof(*ident) * ident_count); + priv->identities_count = ident_count; + + for (size_t i = 0; i < ident_count; i++) { + priv->identities[i] = strdup(ident[i]); + } + + struct module *mod = module_common_new(); + mod->private = priv; + mod->run = &run; + mod->destroy = &destroy; + mod->content = &content; + mod->description = &description; + mod->refresh_in = &refresh_in; + return mod; +} + +static struct module * +from_conf(const struct yml_node *node, struct conf_inherit inherited) +{ + const struct yml_node *ident_list = yml_get_value(node, "identities"); + const struct yml_node *c = yml_get_value(node, "content"); + + const size_t ident_count = yml_list_length(ident_list); + const char *ident[ident_count]; + size_t i = 0; + for (struct yml_list_iter iter = yml_list_iter(ident_list); iter.node != NULL; yml_list_next(&iter), i++) { + ident[i] = yml_value_as_string(iter.node); + } + + return mpris_new(ident, ident_count, conf_to_particle(c, inherited)); +} + +static bool +conf_verify_indentities(keychain_t *chain, const struct yml_node *node) +{ + return conf_verify_list(chain, node, &conf_verify_string); +} + +static bool +verify_conf(keychain_t *chain, const struct yml_node *node) +{ + static const struct attr_info attrs[] = { + {"identities", true, &conf_verify_indentities}, + MODULE_COMMON_ATTRS, + }; + + return conf_verify_dict(chain, node, attrs); +} + +const struct module_iface module_mpris_iface = { + .verify_conf = &verify_conf, + .from_conf = &from_conf, +}; + +#if defined(CORE_PLUGINS_AS_SHARED_LIBRARIES) +extern const struct module_iface iface __attribute__((weak, alias("module_mpris_iface"))); +#endif diff --git a/plugin.c b/plugin.c index b1e268b..2ed0a4f 100644 --- a/plugin.c +++ b/plugin.c @@ -57,6 +57,9 @@ EXTERN_MODULE(mem); #if defined(HAVE_PLUGIN_mpd) EXTERN_MODULE(mpd); #endif +#if defined(HAVE_PLUGIN_mpris) +EXTERN_MODULE(mpris); +#endif #if defined(HAVE_PLUGIN_i3) EXTERN_MODULE(i3); #endif @@ -193,6 +196,9 @@ static void __attribute__((constructor)) init(void) #if defined(HAVE_PLUGIN_mpd) REGISTER_CORE_MODULE(mpd, mpd); #endif +#if defined(HAVE_PLUGIN_mpris) + REGISTER_CORE_MODULE(mpris, mpris); +#endif #if defined(HAVE_PLUGIN_i3) REGISTER_CORE_MODULE(i3, i3); #endif From e68ed8d8434e016041be944416def5c8d6e2d0ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 5 Mar 2025 08:41:04 +0100 Subject: [PATCH 600/611] module: mpris: mark debug-only variables with attribute unused Fixes release builds --- modules/mpris.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/modules/mpris.c b/modules/mpris.c index 4cf99ef..538256d 100644 --- a/modules/mpris.c +++ b/modules/mpris.c @@ -463,7 +463,8 @@ context_event_handle_name_owner_changed(sd_bus_message *message, struct context * it was acquired, lost or changed */ const char *bus_name = NULL, *old_owner = NULL, *new_owner = NULL; - int status = sd_bus_message_read(message, "sss", &bus_name, &old_owner, &new_owner); + int status __attribute__((unused)) + = sd_bus_message_read(message, "sss", &bus_name, &old_owner, &new_owner); assert(status > 0); #if 1 @@ -508,7 +509,8 @@ context_event_handle_name_acquired(sd_bus_message *message, struct context *cont /* NameAcquired (STRING name) */ /* " This signal is sent to a specific application when it gains ownership of a name. " */ const char *name = NULL; - int status = sd_bus_message_read_basic(message, SD_BUS_TYPE_STRING, &name); + int status __attribute__((unused)) + = sd_bus_message_read_basic(message, SD_BUS_TYPE_STRING, &name); assert(status > 0); /*LOG_DBG("event_handler: 'NameAcquired': name: '%s'", name);*/ @@ -727,7 +729,8 @@ update_status_from_message(struct module *mod, sd_bus_message *message) while ((has_entries = sd_bus_message_enter_container(message, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) { const char *property_name = NULL; - int status = sd_bus_message_read_basic(message, SD_BUS_TYPE_STRING, &property_name); + int status __attribute__((unused)) + = sd_bus_message_read_basic(message, SD_BUS_TYPE_STRING, &property_name); assert(status > 0); if (!property_parse(&client->property, property_name, message)) { From e423776000982ad5646dfae6f753226f743c36e3 Mon Sep 17 00:00:00 2001 From: haruInDisguise Date: Sun, 9 Mar 2025 22:28:59 +0100 Subject: [PATCH 601/611] module_mpris: Added 'query-timeout' option This enables us to configure the communication timeout with the dbus daemon. --- doc/yambar-modules-mpris.5.scd | 18 ++++++++++++------ modules/mpris.c | 23 ++++++++++++++++------- 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/doc/yambar-modules-mpris.5.scd b/doc/yambar-modules-mpris.5.scd index a52caba..510dc8f 100644 --- a/doc/yambar-modules-mpris.5.scd +++ b/doc/yambar-modules-mpris.5.scd @@ -55,6 +55,11 @@ mpris - This module provides MPRIS status such as currently playing artist/album : list of string : yes : A list of MPRIS client identities +| query_timeout +: int +: no +: Dbus/MPRIS client connection timeout in ms. Try setting/incrementing + this value if the module reports a timeout error. Defaults to 500. # EXAMPLES @@ -77,18 +82,19 @@ bar: # NOTE The 'identity' refers a part of your clients DBus bus name. -You can obtain a list of available bus names using: +You can obtain a list of active client names using: ``` Systemd: > busctl --user --list Playerctl: > playerctl --list-all -Libdbus: > dbus-send --session --print-reply --type=method_call --dest='org.freedesktop.DBus' /org org.freedesktop.DBus.ListNames ... | grep 'org.mpris.MediaPlayer2' +Libdbus: > dbus-send --session --print-reply --type=method_call \ + --dest='org.freedesktop.DBus' /org org.freedesktop.DBus.ListNames ``` -The identity refers to the part after 'org.mpris.MediaPlayer2'. -For example, firefox may use the bus name -'org.mpris.MediaPlayer2.firefox.instance_1_7' and its identity would be -'firefox' +MPRIS client bus names start with 'org.mpris.MediaPlayer2.'. +For example, firefox may use the bus name: +'org.mpris.MediaPlayer2.firefox.instance_1_7' which +gives us the identity 'firefox' # SEE ALSO diff --git a/modules/mpris.c b/modules/mpris.c index 538256d..c610176 100644 --- a/modules/mpris.c +++ b/modules/mpris.c @@ -14,16 +14,17 @@ #include #include "dbus.h" +#include "yml.h" #define LOG_MODULE "mpris" -#define LOG_ENABLE_DBG 1 +#define LOG_ENABLE_DBG 0 #include "../bar/bar.h" #include "../config-verify.h" #include "../config.h" #include "../log.h" #include "../plugin.h" -#define QUERY_TIMEOUT 100 +#define DEFAULT_QUERY_TIMEOUT 500 #define PATH "/org/mpris/MediaPlayer2" #define BUS_NAME "org.mpris.MediaPlayer2" @@ -99,6 +100,7 @@ struct private int refresh_abort_fd; size_t identities_count; + size_t timeout_ms; const char **identities; struct particle *label; @@ -649,10 +651,10 @@ context_new(struct private *m, struct context *context) sd_bus_message *reply = NULL; sd_bus_error error = {}; - status = sd_bus_call(NULL, message, QUERY_TIMEOUT, &error, &reply); + status = sd_bus_call(NULL, message, m->timeout_ms, &error, &reply); if (status < 0 && sd_bus_error_is_set(&error)) { - LOG_ERR("context_new: got error response with error: %s: %s (%d)", error.name, error.message, + LOG_ERR("context_new: got error response: %s: %s (%d)", error.name, error.message, sd_bus_error_get_errno(&error)); return false; } @@ -1035,7 +1037,7 @@ run(struct module *mod) break; } - if (!context_process_events(context, QUERY_TIMEOUT)) { + if (!context_process_events(context, m->timeout_ms)) { aborted = true; break; } @@ -1068,10 +1070,11 @@ description(const struct module *mod) } static struct module * -mpris_new(const char **ident, size_t ident_count, struct particle *label) +mpris_new(const char **ident, size_t ident_count, size_t timeout, struct particle *label) { struct private *priv = calloc(1, sizeof(*priv)); priv->label = label; + priv->timeout_ms = timeout; priv->identities = malloc(sizeof(*ident) * ident_count); priv->identities_count = ident_count; @@ -1093,8 +1096,13 @@ static struct module * from_conf(const struct yml_node *node, struct conf_inherit inherited) { const struct yml_node *ident_list = yml_get_value(node, "identities"); + const struct yml_node *query_timeout = yml_get_value(node, "query_timeout"); const struct yml_node *c = yml_get_value(node, "content"); + size_t timeout_ms = DEFAULT_QUERY_TIMEOUT * 1000; + if(query_timeout != NULL) + timeout_ms = yml_value_as_int(query_timeout) * 1000; + const size_t ident_count = yml_list_length(ident_list); const char *ident[ident_count]; size_t i = 0; @@ -1102,7 +1110,7 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) ident[i] = yml_value_as_string(iter.node); } - return mpris_new(ident, ident_count, conf_to_particle(c, inherited)); + return mpris_new(ident, ident_count, timeout_ms, conf_to_particle(c, inherited)); } static bool @@ -1116,6 +1124,7 @@ verify_conf(keychain_t *chain, const struct yml_node *node) { static const struct attr_info attrs[] = { {"identities", true, &conf_verify_indentities}, + {"query_timeout", false, &conf_verify_unsigned}, MODULE_COMMON_ATTRS, }; From dcf936fd9b04cb77c9125dd5be5856872ec3b405 Mon Sep 17 00:00:00 2001 From: haruInDisguise Date: Mon, 10 Mar 2025 00:32:14 +0100 Subject: [PATCH 602/611] module_mpris: Fixed 'use after free' --- modules/mpris.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/modules/mpris.c b/modules/mpris.c index c610176..b29e883 100644 --- a/modules/mpris.c +++ b/modules/mpris.c @@ -135,14 +135,17 @@ metadata_clear(struct metadata *metadata) if (metadata->album != NULL) { free(metadata->album); + metadata->album = NULL; } if (metadata->title != NULL) { free(metadata->title); + metadata->title = NULL; } if (metadata->trackid != NULL) { free(metadata->trackid); + metadata->trackid = NULL; } } @@ -296,7 +299,7 @@ metadata_parse_property(const char *property_name, sd_bus_message *message, stru goto unexpected_type; status = sd_bus_message_read(message, "v", argument_layout, &string); - if (status > 0) + if (status > 0 && strlen(string) > 0) buffer->trackid = strdup(string); /* FIXME: "strcmp matches both 'album' as well as 'albumArtist'" */ @@ -310,7 +313,7 @@ metadata_parse_property(const char *property_name, sd_bus_message *message, stru } else if (strcmp(property_name, "xesam:title") == 0) { status = sd_bus_message_read(message, "v", "s", &string); - if(status > 0) + if(status > 0 && strlen(string) > 0) buffer->title = strdup(string); } else if (strcmp(property_name, "mpris:length") == 0) { @@ -393,12 +396,12 @@ property_parse(struct property *prop, const char *property_name, sd_bus_message const char *string; if (strcmp(property_name, "PlaybackStatus") == 0) { status = sd_bus_message_read(message, "v", "s", &string); - if (status) + if (status && strlen(string) > 0) prop->playback_status = strdup(string); } else if (strcmp(property_name, "LoopStatus") == 0) { status = sd_bus_message_read(message, "v", "s", &string); - if (status) + if (status && strlen(string) > 0) prop->loop_status = strdup(string); } else if (strcmp(property_name, "Position") == 0) { From 7e76d53c0a4ffe51c873a3aaf3f2faea845c0045 Mon Sep 17 00:00:00 2001 From: Ben Boeckel Date: Sun, 9 Mar 2025 21:43:47 +0100 Subject: [PATCH 603/611] modules/mpris: fix dependency search Move dependency discovery to be with other module dependency searches. Also only define the `sdbus` dependency if the module is enabled. The `mpris` module was always being compiled because the `sdbus` dependency always existed. Instead, check for the external dependency's status and create the `sdbus` bridge dependency only when necessary. --- meson.build | 4 ---- modules/meson.build | 8 +++++--- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/meson.build b/meson.build index 1a6d211..e1f695a 100644 --- a/meson.build +++ b/meson.build @@ -77,10 +77,6 @@ backend_wayland = wayland_client.found() and wayland_cursor.found() tllist = dependency('tllist', version: '>=1.0.1', fallback: 'tllist') fcft = dependency('fcft', version: ['>=3.0.0', '<4.0.0'], fallback: 'fcft') -# DBus dependency. Used by 'modules/mpris' -sdbus_library = dependency('libsystemd', 'libelogind', 'basu', required: get_option('plugin-mpris')) -sdbus = declare_dependency(compile_args: ['-DHAVE_' + sdbus_library.name().to_upper()], dependencies:[sdbus_library]) - add_project_arguments( cc_flags + ['-D_GNU_SOURCE'] + diff --git a/modules/meson.build b/modules/meson.build index 0e65812..f6d53d8 100644 --- a/modules/meson.build +++ b/modules/meson.build @@ -22,8 +22,9 @@ plugin_mem_enabled = get_option('plugin-mem').allowed() mpd = dependency('libmpdclient', required: get_option('plugin-mpd')) plugin_mpd_enabled = mpd.found() -mpris = sdbus -plugin_mpris_enabled = sdbus.found() +# DBus dependency. Used by 'mpris' +sdbus_library = dependency('libsystemd', 'libelogind', 'basu', required: get_option('plugin-mpris')) +plugin_mpris_enabled = sdbus_library.found() json_i3 = dependency('json-c', required: get_option('plugin-i3')) plugin_i3_enabled = json_i3.found() @@ -99,7 +100,8 @@ if plugin_mpd_enabled endif if plugin_mpris_enabled - mod_data += {'mpris': [[], [mpris]]} + sdbus = declare_dependency(compile_args: ['-DHAVE_' + sdbus_library.name().to_upper()], dependencies:[sdbus_library]) + mod_data += {'mpris': [[], [sdbus]]} endif if plugin_i3_enabled From 56467d0ba3eeddb3f57f817d720e70c5ac8b4afc Mon Sep 17 00:00:00 2001 From: Ben Boeckel Date: Sun, 9 Mar 2025 21:45:20 +0100 Subject: [PATCH 604/611] meson: require 0.60.0 This introduces dependencies with multiple names which is used for the `sdbus_library` dependency of the `mpris` module. --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index e1f695a..67d3096 100644 --- a/meson.build +++ b/meson.build @@ -1,7 +1,7 @@ project('yambar', 'c', version: '1.11.0', license: 'MIT', - meson_version: '>=0.59.0', + meson_version: '>=0.60.0', default_options: ['c_std=c18', 'warning_level=1', 'b_ndebug=if-release']) From 0bcde5c453e7765deec020321e08484b0781b980 Mon Sep 17 00:00:00 2001 From: haruInDisguise Date: Mon, 10 Mar 2025 01:36:26 +0100 Subject: [PATCH 605/611] module_mpris: Fixed memory leak The identity list now uses tlllist and is deallocated properly --- modules/mpris.c | 43 +++++++++++++++++-------------------------- 1 file changed, 17 insertions(+), 26 deletions(-) diff --git a/modules/mpris.c b/modules/mpris.c index b29e883..74d2e95 100644 --- a/modules/mpris.c +++ b/modules/mpris.c @@ -83,11 +83,6 @@ struct context { sd_bus *monitor_connection; sd_bus_message *update_message; - /* FIXME: There is no nice way to pass the desired identities to - * the event handler for validation. */ - char **identities_ref; - size_t identities_count; - tll(struct client *) clients; struct client *current_client; @@ -99,14 +94,14 @@ struct private thrd_t refresh_thread_id; int refresh_abort_fd; - size_t identities_count; size_t timeout_ms; - const char **identities; struct particle *label; struct context context; }; +static tll(const char*) identity_list; + #if 0 static void debug_print_argument_type(sd_bus_message *message) @@ -225,10 +220,10 @@ client_change_unique_name(struct client *client, const char *new_name) } static bool -verify_bus_name(char **idents, const size_t ident_count, const char *name) +verify_bus_name(const char *name) { - for (size_t i = 0; i < ident_count; i++) { - const char *ident = idents[i]; + tll_foreach(identity_list, it) { + const char *ident = it->item; if (strlen(name) < strlen(BUS_NAME ".") + strlen(ident)) { continue; @@ -455,9 +450,13 @@ destroy(struct module *mod) sd_bus_close(context->monitor_connection); - module_default_destroy(mod); + tll_foreach(identity_list, it) { + free((char*)it->item); + } m->label->destroy(m->label); free(m); + + module_default_destroy(mod); } static void @@ -524,7 +523,7 @@ context_event_handle_name_acquired(sd_bus_message *message, struct context *cont return; } - if (verify_bus_name(context->identities_ref, context->identities_count, name)) { + if (verify_bus_name(name)) { const char *unique_name = sd_bus_message_get_destination(message); LOG_DBG("'NameAcquired': Acquired new client: %s unique: %s", name, unique_name); client_add(context, name, unique_name); @@ -667,8 +666,6 @@ context_new(struct private *m, struct context *context) (*context) = (struct context){ .monitor_connection = connection, - .identities_ref = (char **)m->identities, - .identities_count = m->identities_count, .clients = tll_init(), }; @@ -1061,8 +1058,8 @@ run(struct module *mod) bar->refresh(bar); } - LOG_DBG("exiting"); + LOG_DBG("exiting"); return ret; } @@ -1073,17 +1070,11 @@ description(const struct module *mod) } static struct module * -mpris_new(const char **ident, size_t ident_count, size_t timeout, struct particle *label) +mpris_new(size_t timeout, struct particle *label) { struct private *priv = calloc(1, sizeof(*priv)); priv->label = label; priv->timeout_ms = timeout; - priv->identities = malloc(sizeof(*ident) * ident_count); - priv->identities_count = ident_count; - - for (size_t i = 0; i < ident_count; i++) { - priv->identities[i] = strdup(ident[i]); - } struct module *mod = module_common_new(); mod->private = priv; @@ -1106,14 +1097,14 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) if(query_timeout != NULL) timeout_ms = yml_value_as_int(query_timeout) * 1000; - const size_t ident_count = yml_list_length(ident_list); - const char *ident[ident_count]; + // FIXME: This is a redundant copy size_t i = 0; for (struct yml_list_iter iter = yml_list_iter(ident_list); iter.node != NULL; yml_list_next(&iter), i++) { - ident[i] = yml_value_as_string(iter.node); + const char *string = strdup(yml_value_as_string(iter.node)); + tll_push_back(identity_list, string); } - return mpris_new(ident, ident_count, timeout_ms, conf_to_particle(c, inherited)); + return mpris_new(timeout_ms, conf_to_particle(c, inherited)); } static bool From 87c74d54b792468ffe6b0348f214548a48204fd0 Mon Sep 17 00:00:00 2001 From: Ben Boeckel Date: Sat, 8 Feb 2025 20:17:13 +0100 Subject: [PATCH 606/611] meson: guard the `full-conf-good` test by the plugins it expects --- test/meson.build | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/test/meson.build b/test/meson.build index e8c7138..a3635a2 100644 --- a/test/meson.build +++ b/test/meson.build @@ -7,4 +7,9 @@ test('no-config', yambar, args: ['-C', '-c', 'xyz'], should_fail: true) test('config-isnt-file', yambar, args: ['-C', '-c', '.'], should_fail: true) test('config-no-bar', yambar, args: ['-C', '-c', join_paths(pwd, 'no-bar.yml')], should_fail: true) -test('full-conf-good', yambar, args: ['-C', '-c', join_paths(pwd, 'full-conf-good.yml')]) +if plugin_alsa_enabled and plugin_backlight_enabled and \ + plugin_battery_enabled and plugin_clock_enabled and \ + plugin_i3_enabled and plugin_mpd_enabled and plugin_network_enabled \ + and plugin_removables_enabled + test('full-conf-good', yambar, args: ['-C', '-c', join_paths(pwd, 'full-conf-good.yml')]) +endif From 6a97b364a01b0bd4af54fa0c0e434acfc2ef704f Mon Sep 17 00:00:00 2001 From: haruInDisguise Date: Mon, 10 Mar 2025 11:13:17 +0100 Subject: [PATCH 607/611] module_mpris: Fixed inconsistent string validation checks This addresses changes requested by @mathstuf --- modules/mpris.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/modules/mpris.c b/modules/mpris.c index 74d2e95..31b8fd2 100644 --- a/modules/mpris.c +++ b/modules/mpris.c @@ -24,8 +24,9 @@ #include "../log.h" #include "../plugin.h" -#define DEFAULT_QUERY_TIMEOUT 500 +#define is_empty_string(str) ((str) == NULL || (str)[0] == '\0') +#define DEFAULT_QUERY_TIMEOUT 500 #define PATH "/org/mpris/MediaPlayer2" #define BUS_NAME "org.mpris.MediaPlayer2" #define SERVICE "org.mpris.MediaPlayer2" @@ -259,7 +260,7 @@ read_string_array(sd_bus_message *message, string_array *list) const char *string; while ((status = sd_bus_message_read_basic(message, SD_BUS_TYPE_STRING, &string)) > 0) { - if (strlen(string) > 0) { + if (!is_empty_string(string)) { tll_push_back(*list, strdup(string)); } } @@ -287,20 +288,20 @@ metadata_parse_property(const char *property_name, sd_bus_message *message, stru const char *argument_layout = NULL; sd_bus_message_peek_type(message, &argument_type, &argument_layout); assert(argument_type == SD_BUS_TYPE_VARIANT); - assert(argument_layout != NULL && strlen(argument_layout) > 0); + assert(!is_empty_string(argument_layout)); if (strcmp(property_name, "mpris:trackid") == 0) { if (argument_layout[0] != SD_BUS_TYPE_STRING && argument_layout[0] != SD_BUS_TYPE_OBJECT_PATH) goto unexpected_type; status = sd_bus_message_read(message, "v", argument_layout, &string); - if (status > 0 && strlen(string) > 0) + if (status > 0 && !is_empty_string(string)) buffer->trackid = strdup(string); /* FIXME: "strcmp matches both 'album' as well as 'albumArtist'" */ } else if (strcmp(property_name, "xesam:album") == 0) { status = sd_bus_message_read(message, "v", argument_layout, &string); - if (status > 0 && strlen(string) > 0) + if (status > 0 && !is_empty_string(string)) buffer->album = strdup(string); } else if (strcmp(property_name, "xesam:artist") == 0) { @@ -308,7 +309,7 @@ metadata_parse_property(const char *property_name, sd_bus_message *message, stru } else if (strcmp(property_name, "xesam:title") == 0) { status = sd_bus_message_read(message, "v", "s", &string); - if(status > 0 && strlen(string) > 0) + if(status > 0 && !is_empty_string(string)) buffer->title = strdup(string); } else if (strcmp(property_name, "mpris:length") == 0) { @@ -386,17 +387,17 @@ property_parse(struct property *prop, const char *property_name, sd_bus_message assert(status > 0); assert(argument_type == SD_BUS_TYPE_VARIANT); - assert(argument_layout != NULL && strlen(argument_layout) > 0); + assert(!is_empty_string(argument_layout)); const char *string; if (strcmp(property_name, "PlaybackStatus") == 0) { status = sd_bus_message_read(message, "v", "s", &string); - if (status && strlen(string) > 0) + if (status && !is_empty_string(string)) prop->playback_status = strdup(string); } else if (strcmp(property_name, "LoopStatus") == 0) { status = sd_bus_message_read(message, "v", "s", &string); - if (status && strlen(string) > 0) + if (status && !is_empty_string(string)) prop->loop_status = strdup(string); } else if (strcmp(property_name, "Position") == 0) { @@ -476,7 +477,7 @@ context_event_handle_name_owner_changed(sd_bus_message *message, struct context new_owner); #endif - if (strlen(new_owner) == 0 && strlen(old_owner) > 0) { + if (is_empty_string(new_owner) && !is_empty_string(old_owner)) { /* Target bus has been lost */ struct client *client = client_lookup_by_unique_name(context, old_owner); @@ -490,14 +491,14 @@ context_event_handle_name_owner_changed(sd_bus_message *message, struct context context->current_client = NULL; return; - } else if (strlen(old_owner) == 0 && strlen(new_owner) > 0) { + } else if (is_empty_string(old_owner) && !is_empty_string(new_owner)) { /* New unique name registered. Not used */ return; } /* Name changed */ - assert(new_owner != NULL && strlen(new_owner) > 0); - assert(old_owner != NULL && strlen(old_owner) > 0); + assert(!is_empty_string(new_owner)); + assert(!is_empty_string(old_owner)); struct client *client = client_lookup_by_unique_name(context, old_owner); LOG_DBG("'NameOwnerChanged': Name changed from '%s' to '%s' for client '%s'", old_owner, new_owner, From dcbb0f88ae9c3f4a9b429c0c69e183551c365559 Mon Sep 17 00:00:00 2001 From: haruInDisguise Date: Mon, 10 Mar 2025 11:34:29 +0100 Subject: [PATCH 608/611] module_mpris: Cleanup Fixed inconsistent variable naming/debug logging --- modules/mpris.c | 78 ++++++++++++++++++++----------------------------- 1 file changed, 31 insertions(+), 47 deletions(-) diff --git a/modules/mpris.c b/modules/mpris.c index 31b8fd2..79f1980 100644 --- a/modules/mpris.c +++ b/modules/mpris.c @@ -13,9 +13,6 @@ #include -#include "dbus.h" -#include "yml.h" - #define LOG_MODULE "mpris" #define LOG_ENABLE_DBG 0 #include "../bar/bar.h" @@ -24,14 +21,17 @@ #include "../log.h" #include "../plugin.h" +#include "dbus.h" +#include "yml.h" + #define is_empty_string(str) ((str) == NULL || (str)[0] == '\0') -#define DEFAULT_QUERY_TIMEOUT 500 -#define PATH "/org/mpris/MediaPlayer2" -#define BUS_NAME "org.mpris.MediaPlayer2" -#define SERVICE "org.mpris.MediaPlayer2" -#define INTERFACE_ROOT "org.mpris.MediaPlayer2" -#define INTERFACE_PLAYER INTERFACE_ROOT ".Player" +#define DEFAULT_QUERY_TIMEOUT_MS (500 * 1000) + +#define MPRIS_PATH "/org/mpris/MediaPlayer2" +#define MPRIS_BUS_NAME "org.mpris.MediaPlayer2" +#define MPRIS_SERVICE "org.mpris.MediaPlayer2" +#define MPRIS_INTERFACE_PLAYER "org.mpris.MediaPlayer2.Player" #define DBUS_PATH "/org/freedesktop/DBus" #define DBUS_BUS_NAME "org.freedesktop.DBus" @@ -101,29 +101,19 @@ struct private struct context context; }; -static tll(const char*) identity_list; +static tll(const char *) identity_list; -#if 0 -static void +#if defined(LOG_ENABLE_DBG) && LOG_ENABLE_DBG +static void __attribute__((unused)) debug_print_argument_type(sd_bus_message *message) { char type; const char *content; sd_bus_message_peek_type(message, &type, &content); - LOG_DBG("peek_message_type: %c -> %s", type, content); + LOG_DBG("argument type: %c -> %s", type, content); } #endif -#if defined(LOG_ENABLE_DBG) -#define dump_type(message) \ - { \ - char type; \ - const char *content; \ - sd_bus_message_peek_type(message, &type, &content); \ - LOG_DBG("argument layout: %c -> %s", type, content); \ - } -#endif - static void metadata_clear(struct metadata *metadata) { @@ -223,14 +213,15 @@ client_change_unique_name(struct client *client, const char *new_name) static bool verify_bus_name(const char *name) { - tll_foreach(identity_list, it) { + tll_foreach(identity_list, it) + { const char *ident = it->item; - if (strlen(name) < strlen(BUS_NAME ".") + strlen(ident)) { + if (strlen(name) < strlen(MPRIS_BUS_NAME ".") + strlen(ident)) { continue; } - const char *cmp = name + strlen(BUS_NAME "."); + const char *cmp = name + strlen(MPRIS_BUS_NAME "."); if (strncmp(cmp, ident, strlen(ident)) != 0) { continue; } @@ -309,7 +300,7 @@ metadata_parse_property(const char *property_name, sd_bus_message *message, stru } else if (strcmp(property_name, "xesam:title") == 0) { status = sd_bus_message_read(message, "v", "s", &string); - if(status > 0 && !is_empty_string(string)) + if (status > 0 && !is_empty_string(string)) buffer->title = strdup(string); } else if (strcmp(property_name, "mpris:length") == 0) { @@ -451,9 +442,7 @@ destroy(struct module *mod) sd_bus_close(context->monitor_connection); - tll_foreach(identity_list, it) { - free((char*)it->item); - } + tll_foreach(identity_list, it) { free((char *)it->item); } m->label->destroy(m->label); free(m); @@ -468,14 +457,11 @@ context_event_handle_name_owner_changed(sd_bus_message *message, struct context * it was acquired, lost or changed */ const char *bus_name = NULL, *old_owner = NULL, *new_owner = NULL; - int status __attribute__((unused)) - = sd_bus_message_read(message, "sss", &bus_name, &old_owner, &new_owner); + int status __attribute__((unused)) = sd_bus_message_read(message, "sss", &bus_name, &old_owner, &new_owner); assert(status > 0); -#if 1 LOG_DBG("event_handler: 'NameOwnerChanged': bus_name: '%s' old_owner: '%s' new_ower: '%s'", bus_name, old_owner, new_owner); -#endif if (is_empty_string(new_owner) && !is_empty_string(old_owner)) { /* Target bus has been lost */ @@ -514,13 +500,12 @@ context_event_handle_name_acquired(sd_bus_message *message, struct context *cont /* NameAcquired (STRING name) */ /* " This signal is sent to a specific application when it gains ownership of a name. " */ const char *name = NULL; - int status __attribute__((unused)) - = sd_bus_message_read_basic(message, SD_BUS_TYPE_STRING, &name); + int status __attribute__((unused)) = sd_bus_message_read_basic(message, SD_BUS_TYPE_STRING, &name); assert(status > 0); - /*LOG_DBG("event_handler: 'NameAcquired': name: '%s'", name);*/ + LOG_DBG("event_handler: 'NameAcquired': name: '%s'", name); - if (strncmp(name, BUS_NAME, strlen(BUS_NAME)) != 0) { + if (strncmp(name, MPRIS_BUS_NAME, strlen(MPRIS_BUS_NAME)) != 0) { return; } @@ -563,7 +548,8 @@ context_event_handler(sd_bus_message *message, void *userdata, sd_bus_error *ret /* Copy the 'PropertiesChanged/Seeked' message, so it can be parsed * later on */ - if (strcmp(path_name, PATH) == 0 && (strcmp(member, "PropertiesChanged") == 0 || strcmp(member, "Seeked") == 0)) { + if (strcmp(path_name, MPRIS_PATH) == 0 + && (strcmp(member, "PropertiesChanged") == 0 || strcmp(member, "Seeked") == 0)) { struct client *client = client_lookup_by_unique_name(context, sender); if (client == NULL) return 1; @@ -717,7 +703,7 @@ update_status_from_message(struct module *mod, sd_bus_message *message) const char *interface_name = NULL; sd_bus_message_read_basic(message, SD_BUS_TYPE_STRING, &interface_name); - if (strcmp(interface_name, INTERFACE_PLAYER) != 0) { + if (strcmp(interface_name, MPRIS_INTERFACE_PLAYER) != 0) { LOG_DBG("Ignoring interface: %s", interface_name); mtx_unlock(&mod->lock); return true; @@ -732,8 +718,7 @@ update_status_from_message(struct module *mod, sd_bus_message *message) while ((has_entries = sd_bus_message_enter_container(message, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) { const char *property_name = NULL; - int status __attribute__((unused)) - = sd_bus_message_read_basic(message, SD_BUS_TYPE_STRING, &property_name); + int status __attribute__((unused)) = sd_bus_message_read_basic(message, SD_BUS_TYPE_STRING, &property_name); assert(status > 0); if (!property_parse(&client->property, property_name, message)) { @@ -1059,7 +1044,6 @@ run(struct module *mod) bar->refresh(bar); } - LOG_DBG("exiting"); return ret; } @@ -1071,11 +1055,11 @@ description(const struct module *mod) } static struct module * -mpris_new(size_t timeout, struct particle *label) +mpris_new(size_t timeout_ms, struct particle *label) { struct private *priv = calloc(1, sizeof(*priv)); priv->label = label; - priv->timeout_ms = timeout; + priv->timeout_ms = timeout_ms; struct module *mod = module_common_new(); mod->private = priv; @@ -1094,8 +1078,8 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) const struct yml_node *query_timeout = yml_get_value(node, "query_timeout"); const struct yml_node *c = yml_get_value(node, "content"); - size_t timeout_ms = DEFAULT_QUERY_TIMEOUT * 1000; - if(query_timeout != NULL) + size_t timeout_ms = DEFAULT_QUERY_TIMEOUT_MS; + if (query_timeout != NULL) timeout_ms = yml_value_as_int(query_timeout) * 1000; // FIXME: This is a redundant copy From dfa0970b75297cf2047d6cf45af98cf093c07e6a Mon Sep 17 00:00:00 2001 From: haruInDisguise Date: Mon, 10 Mar 2025 12:03:17 +0100 Subject: [PATCH 609/611] module_mpris: Fixed multi threading issues regarding 'identity_list' This addresses changes requested by @dnkl --- modules/mpris.c | 38 ++++++++++++++++---------------------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/modules/mpris.c b/modules/mpris.c index 79f1980..6a03e8c 100644 --- a/modules/mpris.c +++ b/modules/mpris.c @@ -86,6 +86,7 @@ struct context { tll(struct client *) clients; struct client *current_client; + const string_array *identity_list_ref; bool has_update; }; @@ -96,13 +97,12 @@ struct private int refresh_abort_fd; size_t timeout_ms; + string_array identity_list; struct particle *label; struct context context; }; -static tll(const char *) identity_list; - #if defined(LOG_ENABLE_DBG) && LOG_ENABLE_DBG static void __attribute__((unused)) debug_print_argument_type(sd_bus_message *message) @@ -166,12 +166,6 @@ clients_free_by_unique_name(struct context *context, const char *unique_name) } } -static void -client_free_all(struct context *context) -{ - tll_free_and_free(context->clients, client_free); -} - static void client_add(struct context *context, const char *name, const char *unique_name) { @@ -211,9 +205,9 @@ client_change_unique_name(struct client *client, const char *new_name) } static bool -verify_bus_name(const char *name) +verify_bus_name(const string_array *identity_list, const char *name) { - tll_foreach(identity_list, it) + tll_foreach(*identity_list, it) { const char *ident = it->item; @@ -438,11 +432,10 @@ destroy(struct module *mod) struct private *m = mod->private; struct context *context = &m->context; - client_free_all(context); - + tll_free_and_free(context->clients, client_free); sd_bus_close(context->monitor_connection); - tll_foreach(identity_list, it) { free((char *)it->item); } + tll_free_and_free(m->identity_list, free); m->label->destroy(m->label); free(m); @@ -509,7 +502,7 @@ context_event_handle_name_acquired(sd_bus_message *message, struct context *cont return; } - if (verify_bus_name(name)) { + if (verify_bus_name(context->identity_list_ref, name)) { const char *unique_name = sd_bus_message_get_destination(message); LOG_DBG("'NameAcquired': Acquired new client: %s unique: %s", name, unique_name); client_add(context, name, unique_name); @@ -652,6 +645,7 @@ context_new(struct private *m, struct context *context) sd_bus_message_unref(reply); (*context) = (struct context){ + .identity_list_ref = &m->identity_list, .monitor_connection = connection, .clients = tll_init(), }; @@ -1055,12 +1049,18 @@ description(const struct module *mod) } static struct module * -mpris_new(size_t timeout_ms, struct particle *label) +mpris_new(const struct yml_node *ident_list, size_t timeout_ms, struct particle *label) { struct private *priv = calloc(1, sizeof(*priv)); priv->label = label; priv->timeout_ms = timeout_ms; + size_t i = 0; + for (struct yml_list_iter iter = yml_list_iter(ident_list); iter.node != NULL; yml_list_next(&iter), i++) { + char *string = strdup(yml_value_as_string(iter.node)); + tll_push_back(priv->identity_list, string); + } + struct module *mod = module_common_new(); mod->private = priv; mod->run = &run; @@ -1082,14 +1082,8 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) if (query_timeout != NULL) timeout_ms = yml_value_as_int(query_timeout) * 1000; - // FIXME: This is a redundant copy - size_t i = 0; - for (struct yml_list_iter iter = yml_list_iter(ident_list); iter.node != NULL; yml_list_next(&iter), i++) { - const char *string = strdup(yml_value_as_string(iter.node)); - tll_push_back(identity_list, string); - } - return mpris_new(timeout_ms, conf_to_particle(c, inherited)); + return mpris_new(ident_list, timeout_ms, conf_to_particle(c, inherited)); } static bool From ca0f565237656dd5bf3af8b5e2d429357545b614 Mon Sep 17 00:00:00 2001 From: haruInDisguise Date: Tue, 18 Mar 2025 12:05:02 +0100 Subject: [PATCH 610/611] module_mpris: Refactoring + Fixed mutex usage - Addressed inconsistens variable naming and removed redundant code. - Mutex locks are now used correctly. - The context struct now references the modules config, making config access less awkward --- modules/mpris.c | 60 +++++++++++++++++++------------------------------ 1 file changed, 23 insertions(+), 37 deletions(-) diff --git a/modules/mpris.c b/modules/mpris.c index 6a03e8c..5ddf6e0 100644 --- a/modules/mpris.c +++ b/modules/mpris.c @@ -81,12 +81,13 @@ struct client { }; struct context { + const struct private *mpd_config; + sd_bus *monitor_connection; sd_bus_message *update_message; tll(struct client *) clients; struct client *current_client; - const string_array *identity_list_ref; bool has_update; }; @@ -98,9 +99,8 @@ struct private size_t timeout_ms; string_array identity_list; - struct particle *label; - struct context context; + struct particle *label; }; #if defined(LOG_ENABLE_DBG) && LOG_ENABLE_DBG @@ -135,31 +135,23 @@ metadata_clear(struct metadata *metadata) } } -static void -property_clear(struct property *property) -{ - metadata_clear(&property->metadata); - memset(property, 0, sizeof(*property)); -} - static void client_free(struct client *client) { - property_clear(&client->property); - free((void *)client->bus_name); free((void *)client->bus_unique_name); free(client); } static void -clients_free_by_unique_name(struct context *context, const char *unique_name) +client_free_by_unique_name(struct context *context, const char *unique_name) { tll_foreach(context->clients, it) { struct client *client = it->item; if (strcmp(client->bus_unique_name, unique_name) == 0) { LOG_DBG("client_remove: Removing client %s", client->bus_name); + client_free(client); tll_remove(context->clients, it); } @@ -430,10 +422,9 @@ static void destroy(struct module *mod) { struct private *m = mod->private; - struct context *context = &m->context; - tll_free_and_free(context->clients, client_free); - sd_bus_close(context->monitor_connection); + tll_free_and_free(m->context.clients, client_free); + sd_bus_close(m->context.monitor_connection); tll_free_and_free(m->identity_list, free); m->label->destroy(m->label); @@ -464,7 +455,7 @@ context_event_handle_name_owner_changed(sd_bus_message *message, struct context return; LOG_DBG("event_handler: 'NameOwnerChanged': Target bus disappeared: %s", client->bus_name); - clients_free_by_unique_name(context, client->bus_unique_name); + client_free_by_unique_name(context, client->bus_unique_name); if (context->current_client == client) context->current_client = NULL; @@ -502,7 +493,7 @@ context_event_handle_name_acquired(sd_bus_message *message, struct context *cont return; } - if (verify_bus_name(context->identity_list_ref, name)) { + if (verify_bus_name(&context->mpd_config->identity_list, name)) { const char *unique_name = sd_bus_message_get_destination(message); LOG_DBG("'NameAcquired': Acquired new client: %s unique: %s", name, unique_name); client_add(context, name, unique_name); @@ -591,15 +582,17 @@ context_process_events(struct context *context, uint32_t timeout_ms) } static bool -context_new(struct private *m, struct context *context) +context_setup(struct context *context) { int status = true; sd_bus *connection; if ((status = sd_bus_default_user(&connection)) < 0) { LOG_ERR("Failed to connect to the desktop bus. errno: %d", status); - return -1; + return false; } + context->monitor_connection = connection; + /* Turn this connection into a monitor */ sd_bus_message *message; status = sd_bus_message_new_method_call(connection, &message, DBUS_SERVICE, DBUS_PATH, DBUS_INTERFACE_MONITORING, @@ -633,23 +626,16 @@ context_new(struct private *m, struct context *context) sd_bus_message *reply = NULL; sd_bus_error error = {}; - status = sd_bus_call(NULL, message, m->timeout_ms, &error, &reply); + status = sd_bus_call(NULL, message, context->mpd_config->timeout_ms, &error, &reply); if (status < 0 && sd_bus_error_is_set(&error)) { - LOG_ERR("context_new: got error response: %s: %s (%d)", error.name, error.message, - sd_bus_error_get_errno(&error)); + LOG_ERR("context_setup: got error: %s: %s (%d)", error.name, error.message, sd_bus_error_get_errno(&error)); return false; } sd_bus_message_unref(message); sd_bus_message_unref(reply); - (*context) = (struct context){ - .identity_list_ref = &m->identity_list, - .monitor_connection = connection, - .clients = tll_init(), - }; - sd_bus_add_filter(connection, NULL, context_event_handler, context); return status >= 0; @@ -765,6 +751,7 @@ static struct exposable * content_empty(struct module *mod) { struct private *m = mod->private; + mtx_lock(&mod->lock); struct tag_set tags = { .tags = (struct tag *[]){ @@ -784,11 +771,10 @@ content_empty(struct module *mod) .count = 10, }; + struct exposable *exposable = m->label->instantiate(m->label, &tags); + tag_set_destroy(&tags); mtx_unlock(&mod->lock); - struct exposable *exposable = m->label->instantiate(m->label, &tags); - - tag_set_destroy(&tags); return exposable; } @@ -862,6 +848,7 @@ content(struct module *mod) const enum tag_realtime_unit realtime_unit = (client->has_seeked_support && client->status == STATUS_PLAYING) ? TAG_REALTIME_MSECS : TAG_REALTIME_NONE; + mtx_lock(&mod->lock); struct tag_set tags = { .tags = (struct tag *[]){ tag_new_bool(mod, "has_seeked_support", client->has_seeked_support), @@ -880,11 +867,10 @@ content(struct module *mod) .count = 11, }; + struct exposable *exposable = m->label->instantiate(m->label, &tags); + tag_set_destroy(&tags); mtx_unlock(&mod->lock); - struct exposable *exposable = m->label->instantiate(m->label, &tags); - - tag_set_destroy(&tags); return exposable; } @@ -990,7 +976,7 @@ run(struct module *mod) const struct bar *bar = mod->bar; struct private *m = mod->private; - if (!context_new(m, &m->context)) { + if (!context_setup(&m->context)) { LOG_ERR("Failed to setup context"); return -1; } @@ -1054,6 +1040,7 @@ mpris_new(const struct yml_node *ident_list, size_t timeout_ms, struct particle struct private *priv = calloc(1, sizeof(*priv)); priv->label = label; priv->timeout_ms = timeout_ms; + priv->context.mpd_config = priv; size_t i = 0; for (struct yml_list_iter iter = yml_list_iter(ident_list); iter.node != NULL; yml_list_next(&iter), i++) { @@ -1082,7 +1069,6 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) if (query_timeout != NULL) timeout_ms = yml_value_as_int(query_timeout) * 1000; - return mpris_new(ident_list, timeout_ms, conf_to_particle(c, inherited)); } From abeffbd9a9fd0b2133343e1149e65d4a795a43d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 5 May 2025 08:14:47 +0200 Subject: [PATCH 611/611] readme: goodbye! --- README.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/README.md b/README.md index 48566dc..4e825b3 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,27 @@ # Yambar +**This project is not developed anymore, and this repository will be +archived in a near future**. + +I do not have neither the time nor the will to work on this +anymore. Mainly because I do not use a bar myself anymore. + +There are also technical difficulties, caused by the fact that yambar +was initially X11, and only later was Wayland support added. + +This means I have to maintain a backend (X11) I have not used myself +in many years, as well as trying to work around technical limitations +imposed by the way both X11 and Wayland is supported, As my own use of +a bar has dwindled, the will to refactor and improve the backends has +disappeared. + +Yambar has seen a lot of contributions, for which I am very +grateful. I hope that means someone is willing to pick up where I left +of, and continue working on yambar. If not, we at least had a good +run; the first commit was in late 2018, roughly 6½ years ago! + + [![Packaging status](https://repology.org/badge/vertical-allrepos/yambar.svg?columns=4)](https://repology.org/project/yambar/versions)