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 01/84] =?UTF-8?q?changelog:=20add=20new=20=E2=80=98unrelea?= =?UTF-8?q?sed=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 02/84] 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 03/84] 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 04/84] 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 05/84] 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 06/84] 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 07/84] 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 08/84] 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 09/84] 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 10/84] 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 11/84] 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 12/84] 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 13/84] 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 14/84] 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 15/84] 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 16/84] 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 17/84] 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 18/84] 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 19/84] 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 20/84] 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 21/84] 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 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 22/84] 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 23/84] 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 24/84] 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 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 25/84] =?UTF-8?q?changelog:=20add=20a=20new=20=E2=80=98unr?= =?UTF-8?q?eleased=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 26/84] 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 27/84] =?UTF-8?q?yml:=20don=E2=80=99t=20crash=20when=20(tr?= =?UTF-8?q?ying=20to)=20merge=20anchors=20that=20aren=E2=80=99t=20dictiona?= =?UTF-8?q?ries?= 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 28/84] 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 29/84] 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 30/84] 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 31/84] 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 32/84] 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 33/84] 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 34/84] 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 35/84] 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 36/84] 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 37/84] 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 38/84] 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 39/84] 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 40/84] 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 41/84] 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 42/84] 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 43/84] 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 44/84] 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 45/84] 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 46/84] 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 47/84] 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 48/84] 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 49/84] 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 50/84] 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 51/84] =?UTF-8?q?tag:=20fix=20crash=20on=20empty=20tag=20s?= =?UTF-8?q?pecifier,=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 52/84] 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 53/84] =?UTF-8?q?particle/string:=20don=E2=80=99t=20thrash?= =?UTF-8?q?=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 54/84] 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 55/84] 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 56/84] =?UTF-8?q?module/battery:=20don=E2=80=99t=20termina?= =?UTF-8?q?te=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 57/84] 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 58/84] 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 59/84] =?UTF-8?q?module/script:=20=E2=80=989=E2=80=99=20is?= =?UTF-8?q?=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 60/84] =?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 61/84] 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 62/84] 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 63/84] =?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 64/84] =?UTF-8?q?bar:=20set=20thread=20name=20of=20the=20b?= =?UTF-8?q?ar=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 65/84] 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 66/84] 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 67/84] =?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 68/84] =?UTF-8?q?exposable:=20add=20=E2=80=98btn=E2=80=99?= =?UTF-8?q?=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 69/84] 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 70/84] 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 71/84] 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 72/84] =?UTF-8?q?particle:=20on-mouse:=20don=E2=80=99t=20c?= =?UTF-8?q?lose=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 73/84] 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 74/84] 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 75/84] 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 76/84] 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 77/84] 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 78/84] 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 79/84] 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 80/84] 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 81/84] 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 82/84] 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 83/84] 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 84/84] 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;